spot  2.16
deadlock.hh
1 // -*- coding: utf-8 -*-
2 // Copyright (C) by the Spot authors, see the AUTHORS file for details.
3 //
4 // This file is part of Spot, a model checking library.
5 //
6 // Spot is free software; you can redistribute it and/or modify it
7 // under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // Spot is distributed in the hope that it will be useful, but WITHOUT
12 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 // License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 
19 #pragma once
20 
21 #include <atomic>
22 #include <chrono>
23 #include <spot/bricks/brick-hashset>
24 #include <stdlib.h>
25 #include <thread>
26 #include <vector>
27 #include <spot/misc/common.hh>
28 #include <spot/kripke/kripke.hh>
29 #include <spot/misc/fixpool.hh>
30 #include <spot/misc/timer.hh>
31 #include <spot/twacube/twacube.hh>
32 #include <spot/twacube/fwd.hh>
33 #include <spot/mc/mc.hh>
34 
35 namespace spot
36 {
43  template<typename State, typename SuccIterator,
44  typename StateHash, typename StateEqual,
45  typename Deadlock>
46  class SPOT_API swarmed_deadlock
47  {
49  enum st_status
50  {
51  UNKNOWN = 1, // First time this state is discovered by this thread
52  OPEN = 2, // The state is currently processed by this thread
53  CLOSED = 4, // All the successors of this state have been visited
54  };
55 
57  struct deadlock_pair
58  {
59  State st;
60  int* colors;
61  };
62 
64  struct pair_hasher
65  {
66  pair_hasher(const deadlock_pair*)
67  { }
68 
69  pair_hasher() = default;
70 
71  brick::hash::hash128_t
72  hash(const deadlock_pair* lhs) const
73  {
74  StateHash hash;
75  // Not modulo 31 according to brick::hashset specifications.
76  unsigned u = hash(lhs->st) % (1<<30);
77  return {u, u};
78  }
79 
80  bool equal(const deadlock_pair* lhs,
81  const deadlock_pair* rhs) const
82  {
83  StateEqual equal;
84  return equal(lhs->st, rhs->st);
85  }
86  };
87 
88  static constexpr bool compute_deadlock =
89  std::is_same<std::true_type, Deadlock>::value;
90 
91  public:
92 
94  using shared_map = brick::hashset::FastConcurrent <deadlock_pair*,
95  pair_hasher>;
98 
101  {
102  return nullptr; // Useless
103  }
104 
107  twacube_ptr, /* useless here */
108  shared_map& map, shared_struct* /* useless here */,
109  unsigned tid,
110  std::atomic<bool>& stop):
111  sys_(sys), tid_(tid), map_(map),
112  nb_th_(std::thread::hardware_concurrency()),
113  p_(sizeof(int)*std::thread::hardware_concurrency()),
114  p_pair_(sizeof(deadlock_pair)),
115  stop_(stop)
116  {
117  static_assert(spot::is_a_kripkecube_ptr<decltype(&sys),
118  State, SuccIterator>::value,
119  "error: does not match the kripkecube requirements");
120  SPOT_ASSERT(nb_th_ > tid);
121  }
122 
123  virtual ~swarmed_deadlock()
124  {
125  while (!todo_.empty())
126  {
127  sys_.recycle(todo_.back().it, tid_);
128  todo_.pop_back();
129  }
130  }
131 
133  void run()
134  {
135  setup();
136  State initial = sys_.initial(tid_);
137  if (SPOT_LIKELY(push(initial)))
138  {
139  todo_.push_back({initial, sys_.succ(initial, tid_), transitions_});
140  }
141  while (!todo_.empty() && !stop_.load(std::memory_order_relaxed))
142  {
143  if (todo_.back().it->done())
144  {
145  if (SPOT_LIKELY(pop()))
146  {
147  deadlock_ = todo_.back().current_tr == transitions_;
148  if (compute_deadlock && deadlock_)
149  break;
150  sys_.recycle(todo_.back().it, tid_);
151  todo_.pop_back();
152  }
153  }
154  else
155  {
156  ++transitions_;
157  State dst = todo_.back().it->state();
158 
159  if (SPOT_LIKELY(push(dst)))
160  {
161  todo_.back().it->next();
162  todo_.push_back({dst, sys_.succ(dst, tid_), transitions_});
163  }
164  else
165  {
166  todo_.back().it->next();
167  }
168  }
169  }
170  finalize();
171  }
172 
174  void setup()
175  {
176  tm_.start("DFS thread " + std::to_string(tid_));
177  }
178 
180  bool push(State s)
181  {
182  // Prepare data for a newer allocation
183  int* ref = (int*) p_.allocate();
184  for (unsigned i = 0; i < nb_th_; ++i)
185  ref[i] = UNKNOWN;
186 
187  // Try to insert the new state in the shared map.
188  deadlock_pair* v = (deadlock_pair*) p_pair_.allocate();
189  v->st = s;
190  v->colors = ref;
191  auto it = map_.insert(v);
192  bool b = it.isnew();
193 
194  // Insertion failed, delete element
195  // FIXME Should we add a local cache to avoid useless allocations?
196  if (!b)
197  p_.deallocate(ref);
198 
199  // The state has been marked dead by another thread
200  for (unsigned i = 0; !b && i < nb_th_; ++i)
201  if ((*it)->colors[i] == static_cast<int>(CLOSED))
202  return false;
203 
204  // The state has already been visited by the current thread
205  if ((*it)->colors[tid_] == static_cast<int>(OPEN))
206  return false;
207 
208  // Keep a ptr over the array of colors
209  refs_.push_back((*it)->colors);
210 
211  // Mark state as visited.
212  (*it)->colors[tid_] = OPEN;
213  ++states_;
214  return true;
215  }
216 
218  bool pop()
219  {
220  // Track maximum dfs size
221  dfs_ = todo_.size() > dfs_ ? todo_.size() : dfs_;
222 
223  // Don't avoid pop but modify the status of the state
224  // during backtrack
225  refs_.back()[tid_] = CLOSED;
226  refs_.pop_back();
227  return true;
228  }
229 
231  void finalize()
232  {
233  bool tst_val = false;
234  bool new_val = true;
235  bool exchanged = stop_.compare_exchange_strong(tst_val, new_val);
236  if (exchanged)
237  finisher_ = true;
238  tm_.stop("DFS thread " + std::to_string(tid_));
239  }
240 
242  bool finisher()
243  {
244  return finisher_;
245  }
246 
248  unsigned states()
249  {
250  return states_;
251  }
252 
254  unsigned transitions()
255  {
256  return transitions_;
257  }
258 
260  unsigned walltime()
261  {
262  return tm_.timer("DFS thread " + std::to_string(tid_)).walltime();
263  }
264 
266  std::string name()
267  {
268  if (compute_deadlock)
269  return "deadlock";
270  return "reachability";
271  }
272 
274  int sccs()
275  {
276  return -1;
277  }
278 
281  {
282  if (compute_deadlock)
283  return deadlock_ ? mc_rvalue::DEADLOCK : mc_rvalue::NO_DEADLOCK;
284  return mc_rvalue::SUCCESS;
285  }
286 
288  std::string trace()
289  {
290  std::string result;
291  for (auto& e: todo_)
292  result += sys_.to_string(e.s, tid_);
293  return result;
294  }
295 
296  private:
297  struct todo_element
298  {
299  State s;
300  SuccIterator* it;
301  unsigned current_tr;
302  };
303  kripkecube<State, SuccIterator>& sys_;
304  std::vector<todo_element> todo_;
305  unsigned transitions_ = 0;
306  unsigned tid_;
307  shared_map map_;
308  spot::timer_map tm_;
309  unsigned states_ = 0;
310  unsigned dfs_ = 0;
312  unsigned nb_th_ = 0;
313  fixed_size_pool<pool_type::Unsafe> p_;
314  fixed_size_pool<pool_type::Unsafe> p_pair_;
315  bool deadlock_ = false;
316  std::atomic<bool>& stop_;
319  std::vector<int*> refs_;
320  bool finisher_ = false;
321  };
322 }
This class allows to ensure (at compile time) if a given parameter is of type kripkecube....
Definition: kripke.hh:71
This class is a template representation of a Kripke structure. It is composed of two template paramet...
Definition: kripke.hh:40
This class aims to explore a model to detect whether it contains a deadlock. This deadlock detection ...
Definition: deadlock.hh:47
unsigned transitions()
Return number of transitions traversed.
Definition: deadlock.hh:254
mc_rvalue result()
Return deadlock detection result.
Definition: deadlock.hh:280
static shared_struct * make_shared_structure(shared_map, unsigned)
Create shared structure for thread tid.
Definition: deadlock.hh:100
bool pop()
Pop state from stack.
Definition: deadlock.hh:218
void finalize()
Finalize thread resources.
Definition: deadlock.hh:231
std::string trace()
Return trace.
Definition: deadlock.hh:288
swarmed_deadlock(kripkecube< State, SuccIterator > &sys, twacube_ptr, shared_map &map, shared_struct *, unsigned tid, std::atomic< bool > &stop)
Constructor for parallel deadlock detection.
Definition: deadlock.hh:106
std::string name()
Return algorithm name.
Definition: deadlock.hh:266
int sccs()
Return number of SCCs found (returns -1)
Definition: deadlock.hh:274
shared_map shared_struct
Type alias for shared structure.
Definition: deadlock.hh:97
bool push(State s)
Push state onto stack.
Definition: deadlock.hh:180
void run()
Run the algorithm.
Definition: deadlock.hh:133
void setup()
Setup thread resources.
Definition: deadlock.hh:174
unsigned states()
Return number of states visited.
Definition: deadlock.hh:248
bool finisher()
Check if this thread finished the search.
Definition: deadlock.hh:242
unsigned walltime()
Return wall time in milliseconds.
Definition: deadlock.hh:260
brick::hashset::FastConcurrent< deadlock_pair *, pair_hasher > shared_map
Concurrent hashset for shared state storage.
Definition: deadlock.hh:95
A map of timer, where each timer has a name.
Definition: timer.hh:231
std::shared_ptr< twacube > twacube_ptr
Definition: fwd.hh:25
Definition: automata.hh:26
mc_rvalue
Return value of a parallel model-checking algorithm.
Definition: mc.hh:50
@ NO_DEADLOCK
No deadlock has been found.
@ DEADLOCK
A deadlock has been found.
@ SUCCESS
The algorithm finished normally.

Please direct any question, comment, or bug report to the Spot mailing list at spot@lrde.epita.fr.
Generated on Fri Feb 27 2015 10:00:07 for spot by doxygen 1.9.1