spot 2.16
Loading...
Searching...
No Matches
bloemen.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 <stdlib.h>
24#include <thread>
25#include <vector>
26#include <utility>
27#include <spot/bricks/brick-hashset>
28#include <spot/kripke/kripke.hh>
29#include <spot/misc/common.hh>
30#include <spot/misc/fixpool.hh>
31#include <spot/misc/timer.hh>
32#include <spot/twacube/twacube.hh>
33#include <spot/twacube/fwd.hh>
34#include <spot/mc/mc.hh>
35
36namespace spot
37{
40 template<typename State,
41 typename StateHash,
42 typename StateEqual>
44 {
45
46 public:
48 enum class uf_status { LIVE, LOCK, DEAD };
50 enum class list_status { BUSY, LOCK, DONE };
52 enum class claim_status { CLAIM_FOUND, CLAIM_NEW, CLAIM_DEAD };
53
56 {
58 State st_;
60 std::atomic<uf_element*> parent;
62 std::atomic<unsigned> worker_;
64 std::atomic<uf_element*> next_;
66 std::atomic<uf_status> uf_status_;
68 std::atomic<list_status> list_status_;
69 };
70
73 {
77
79 uf_element_hasher() = default;
80
82 brick::hash::hash128_t
83 hash(const uf_element* lhs) const
84 {
85 StateHash hash;
86 // Not modulo 31 according to brick::hashset specifications.
87 unsigned u = hash(lhs->st_) % (1<<30);
88 return {u, u};
89 }
90
92 bool equal(const uf_element* lhs,
93 const uf_element* rhs) const
94 {
95 StateEqual equal;
96 return equal(lhs->st_, rhs->st_);
97 }
98 };
99
101 using shared_map = brick::hashset::FastConcurrent <uf_element*,
103
106 map_(uf.map_), tid_(uf.tid_), size_(std::thread::hardware_concurrency()),
107 nb_th_(std::thread::hardware_concurrency()), inserted_(0),
108 p_(sizeof(uf_element))
109 { }
110
111
113 iterable_uf(shared_map& map, unsigned tid):
114 map_(map), tid_(tid), size_(std::thread::hardware_concurrency()),
115 nb_th_(std::thread::hardware_concurrency()), inserted_(0),
116 p_(sizeof(uf_element))
117 {
118 }
119
122
124 std::pair<claim_status, uf_element*>
125 make_claim(State a)
126 {
127 unsigned w_id = (1U << tid_);
128
129 // Setup and try to insert the new state in the shared map.
130 uf_element* v = (uf_element*) p_.allocate();
131 v->st_ = a;
132 v->parent = v;
133 v->next_ = v;
134 v->worker_ = 0;
135 v->uf_status_ = uf_status::LIVE;
136 v->list_status_ = list_status::BUSY;
137
138 auto it = map_.insert({v});
139 bool b = it.isnew();
140
141 // Insertion failed, delete element
142 // FIXME: Should we add a local cache to avoid useless allocations?
143 if (!b)
144 p_.deallocate(v);
145 else
146 ++inserted_;
147
148 uf_element* a_root = find(*it);
149 if (a_root->uf_status_.load() == uf_status::DEAD)
150 return {claim_status::CLAIM_DEAD, *it};
151
152 if ((a_root->worker_.load() & w_id) != 0)
153 return {claim_status::CLAIM_FOUND, *it};
154
155 atomic_fetch_or(&(a_root->worker_), w_id);
156 while (a_root->parent.load() != a_root)
157 {
158 a_root = find(a_root);
159 atomic_fetch_or(&(a_root->worker_), w_id);
160 }
161
162 return {claim_status::CLAIM_NEW, *it};
163 }
164
167 {
168 uf_element* parent = a->parent.load();
169 uf_element* x = a;
170 uf_element* y;
171
172 while (x != parent)
173 {
174 y = parent;
175 parent = y->parent.load();
176 if (parent == y)
177 return y;
178 x->parent.store(parent);
179 x = parent;
180 parent = x->parent.load();
181 }
182 return x;
183 }
184
187 {
188 while (true)
189 {
190 uf_element* a_root = find(a);
191 uf_element* b_root = find(b);
192 if (a_root == b_root)
193 return true;
194
195 if (a_root->parent.load() == a_root)
196 return false;
197 }
198 }
199
202 {
203 uf_status expected = uf_status::LIVE;
204 if (a->uf_status_.load() == expected)
205 {
206 if (std::atomic_compare_exchange_strong
207 (&(a->uf_status_), &expected, uf_status::LOCK))
208 {
209 if (a->parent.load() == a)
210 return true;
211 unlock_root(a);
212 }
213 }
214 return false;
215 }
216
218 inline void unlock_root(uf_element* a)
219 {
220 a->uf_status_.store(uf_status::LIVE);
221 }
222
225 {
226 uf_element* a_list = a;
227 while (true)
228 {
229 bool dontcare = false;
230 a_list = pick_from_list(a_list, &dontcare);
231 if (a_list == nullptr)
232 {
233 return nullptr;
234 }
235
236 auto expected = list_status::BUSY;
237 bool b = std::atomic_compare_exchange_strong
238 (&(a_list->list_status_), &expected, list_status::LOCK);
239
240 if (b)
241 return a_list;
242
243 a_list = a_list->next_.load();
244 }
245 }
246
249 {
250 a->list_status_.store(list_status::BUSY);
251 }
252
255 {
256 uf_element* a_root;
257 uf_element* b_root;
258 uf_element* q;
259 uf_element* r;
260
261 while (true)
262 {
263 a_root = find(a);
264 b_root = find(b);
265
266 if (a_root == b_root)
267 return;
268
269 r = std::max(a_root, b_root);
270 q = std::min(a_root, b_root);
271
272 if (!lock_root(q))
273 continue;
274
275 break;
276 }
277
278 uf_element* a_list = lock_list(a);
279 if (a_list == nullptr)
280 {
281 unlock_root(q);
282 return;
283 }
284
285 uf_element* b_list = lock_list(b);
286 if (b_list == nullptr)
287 {
288 unlock_list(a_list);
289 unlock_root(q);
290 return;
291 }
292
293 SPOT_ASSERT(a_list->list_status_.load() == list_status::LOCK);
294 SPOT_ASSERT(b_list->list_status_.load() == list_status::LOCK);
295
296 // Swapping
297 uf_element* a_next = a_list->next_.load();
298 uf_element* b_next = b_list->next_.load();
299 SPOT_ASSERT(a_next != nullptr);
300 SPOT_ASSERT(b_next != nullptr);
301
302 a_list->next_.store(b_next);
303 b_list->next_.store(a_next);
304 q->parent.store(r);
305
306 // Update workers
307 unsigned q_worker = q->worker_.load();
308 unsigned r_worker = r->worker_.load();
309 if ((q_worker|r_worker) != r_worker)
310 {
311 atomic_fetch_or(&(r->worker_), q_worker);
312 while (r->parent.load() != r)
313 {
314 r = find(r);
315 atomic_fetch_or(&(r->worker_), q_worker);
316 }
317 }
318
319 unlock_list(a_list);
320 unlock_list(b_list);
321 unlock_root(q);
322 }
323
326 {
327 uf_element* a = u;
328 while (true)
329 {
330 list_status a_status;
331 while (true)
332 {
333 a_status = a->list_status_.load();
334
335 if (a_status == list_status::BUSY)
336 {
337 return a;
338 }
339
340 if (a_status == list_status::DONE)
341 break;
342 }
343
344 uf_element* b = a->next_.load();
345
346 // ------------------------------ NO LAZY : start
347 // if (b == u)
348 // {
349 // uf_element* a_root = find(a);
350 // uf_status status = a_root->uf_status_.load();
351 // while (status != uf_status::DEAD)
352 // {
353 // if (status == uf_status::LIVE)
354 // *sccfound = std::atomic_compare_exchange_strong
355 // (&(a_root->uf_status_), &status, uf_status::DEAD);
356 // status = a_root->uf_status_.load();
357 // }
358 // return nullptr;
359 // }
360 // a = b;
361 // ------------------------------ NO LAZY : end
362
363 if (a == b)
364 {
365 uf_element* a_root = find(u);
366 uf_status status = a_root->uf_status_.load();
367 while (status != uf_status::DEAD)
368 {
369 if (status == uf_status::LIVE)
370 *sccfound = std::atomic_compare_exchange_strong
371 (&(a_root->uf_status_), &status, uf_status::DEAD);
372 status = a_root->uf_status_.load();
373 }
374 return nullptr;
375 }
376
377 list_status b_status;
378 while (true)
379 {
380 b_status = b->list_status_.load();
381
382 if (b_status == list_status::BUSY)
383 {
384 return b;
385 }
386
387 if (b_status == list_status::DONE)
388 break;
389 }
390
391 SPOT_ASSERT(b_status == list_status::DONE);
392 SPOT_ASSERT(a_status == list_status::DONE);
393
394 uf_element* c = b->next_.load();
395 a->next_.store(c);
396 a = c;
397 }
398 }
399
402 {
403 while (true)
404 {
405 list_status a_status = a->list_status_.load();
406
407 if (a_status == list_status::DONE)
408 break;
409
410 if (a_status == list_status::BUSY)
411 std::atomic_compare_exchange_strong
412 (&(a->list_status_), &a_status, list_status::DONE);
413 }
414 }
415
417 unsigned inserted()
418 {
419 return inserted_;
420 }
421
422 private:
423 iterable_uf() = default;
424
425 shared_map map_;
426 unsigned tid_;
427 unsigned size_;
428 unsigned nb_th_;
429 unsigned inserted_;
431 };
432
439 template<typename State, typename SuccIterator,
440 typename StateHash, typename StateEqual>
442 {
443 private:
444 swarmed_bloemen() = delete;
445
446 public:
447
451 using uf_element = typename uf::uf_element;
452
456 using shared_map = typename uf::shared_map;
457
460 {
461 return new uf(m, i);
462 }
463
466 twacube_ptr, /* useless here */
467 shared_map& map, /* useless here */
469 unsigned tid,
470 std::atomic<bool>& stop):
471 sys_(sys), uf_(*uf), tid_(tid),
472 nb_th_(std::thread::hardware_concurrency()),
473 stop_(stop)
474 {
475 static_assert(spot::is_a_kripkecube_ptr<decltype(&sys),
476 State, SuccIterator>::value,
477 "error: does not match the kripkecube requirements");
478 }
479
481 void run()
482 {
483 setup();
484 State init = sys_.initial(tid_);
485 auto pair = uf_.make_claim(init);
486 todo_.push_back(pair.second);
487 Rp_.push_back(pair.second);
488 ++states_;
489
490 while (!todo_.empty())
491 {
492 bloemen_recursive_start:
493 while (!stop_.load(std::memory_order_relaxed))
494 {
495 bool sccfound = false;
496 uf_element* v_prime = uf_.pick_from_list(todo_.back(), &sccfound);
497 if (v_prime == nullptr)
498 {
499 // The SCC has been explored!
500 sccs_ += sccfound;
501 break;
502 }
503
504 auto it = sys_.succ(v_prime->st_, tid_);
505 while (!it->done())
506 {
507 auto w = uf_.make_claim(it->state());
508 it->next();
509 ++transitions_;
510 if (w.first == uf::claim_status::CLAIM_NEW)
511 {
512 todo_.push_back(w.second);
513 Rp_.push_back(w.second);
514 ++states_;
515 sys_.recycle(it, tid_);
516 goto bloemen_recursive_start;
517 }
518 else if (w.first == uf::claim_status::CLAIM_FOUND)
519 {
520 while (!uf_.sameset(todo_.back(), w.second))
521 {
522 uf_element* r = Rp_.back();
523 Rp_.pop_back();
524 uf_.unite(r, Rp_.back());
525 }
526 }
527 }
528 uf_.remove_from_list(v_prime);
529 sys_.recycle(it, tid_);
530 }
531
532 if (todo_.back() == Rp_.back())
533 Rp_.pop_back();
534 todo_.pop_back();
535 }
536 finalize();
537 }
538
540 void setup()
541 {
542 tm_.start("DFS thread " + std::to_string(tid_));
543 }
544
546 void finalize()
547 {
548 bool tst_val = false;
549 bool new_val = true;
550 bool exchanged = stop_.compare_exchange_strong(tst_val, new_val);
551 if (exchanged)
552 finisher_ = true;
553 tm_.stop("DFS thread " + std::to_string(tid_));
554 }
555
557 bool finisher()
558 {
559 return finisher_;
560 }
561
563 unsigned states()
564 {
565 return states_;
566 }
567
569 unsigned transitions()
570 {
571 return transitions_;
572 }
573
575 unsigned walltime()
576 {
577 return tm_.timer("DFS thread " + std::to_string(tid_)).walltime();
578 }
579
581 std::string name()
582 {
583 return "bloemen_scc";
584 }
585
587 int sccs()
588 {
589 return sccs_;
590 }
591
594 {
595 return mc_rvalue::SUCCESS;
596 }
597
599 std::string trace()
600 {
601 // Returning a trace makes no sense in this algorithm
602 return "";
603 }
604
605 private:
607 std::vector<uf_element*> todo_;
608 std::vector<uf_element*> Rp_;
610 unsigned tid_;
611 unsigned nb_th_;
612 unsigned inserted_ = 0;
613 unsigned states_ = 0;
614 unsigned transitions_ = 0;
615 unsigned sccs_ = 0;
616 spot::timer_map tm_;
617 std::atomic<bool>& stop_;
618 bool finisher_ = false;
619 };
620}
A fixed-size memory pool implementation.
Definition fixpool.hh:46
void * allocate()
Allocate size bytes of memory.
Definition fixpool.hh:88
This class allows to ensure (at compile time) if a given parameter is of type kripkecube....
Definition kripke.hh:71
Iterable Union-Find for parallel reachability algorithms.
Definition bloemen.hh:44
uf_status
Status values for union-find elements.
Definition bloemen.hh:48
bool sameset(uf_element *a, uf_element *b)
Check if elements are in same set.
Definition bloemen.hh:186
uf_element * pick_from_list(uf_element *u, bool *sccfound)
Pick element from list; mark SCC as dead if complete.
Definition bloemen.hh:325
iterable_uf(const iterable_uf< State, StateHash, StateEqual > &uf)
Copy constructor.
Definition bloemen.hh:105
void remove_from_list(uf_element *a)
Mark element as removed from list.
Definition bloemen.hh:401
iterable_uf(shared_map &map, unsigned tid)
Constructor from shared map and thread ID.
Definition bloemen.hh:113
claim_status
Status values for claim operations.
Definition bloemen.hh:52
void unlock_root(uf_element *a)
Unlock root element.
Definition bloemen.hh:218
brick::hashset::FastConcurrent< uf_element *, uf_element_hasher > shared_map
Concurrent hashset for shared state storage.
Definition bloemen.hh:102
std::pair< claim_status, uf_element * > make_claim(State a)
Try to claim a state; returns status and element pointer.
Definition bloemen.hh:125
void unlock_list(uf_element *a)
Unlock list element.
Definition bloemen.hh:248
unsigned inserted()
Return number of successfully inserted states.
Definition bloemen.hh:417
bool lock_root(uf_element *a)
Lock root element if live; return true if successful.
Definition bloemen.hh:201
list_status
Status values for list operations.
Definition bloemen.hh:50
~iterable_uf()
Destructor.
Definition bloemen.hh:121
void unite(uf_element *a, uf_element *b)
Unite two sets.
Definition bloemen.hh:254
uf_element * find(uf_element *a)
Find root of element using path compression.
Definition bloemen.hh:166
uf_element * lock_list(uf_element *a)
Lock next element in list.
Definition bloemen.hh:224
This class is a template representation of a Kripke structure. It is composed of two template paramet...
Definition kripke.hh:40
Bloemen parallel SCC decomposition algorithm for reachability.
Definition bloemen.hh:442
typename uf::shared_map shared_map
Type alias for shared map.
Definition bloemen.hh:456
unsigned walltime()
Return wall time in milliseconds.
Definition bloemen.hh:575
int sccs()
Return number of SCCs found.
Definition bloemen.hh:587
mc_rvalue result()
Return emptiness check result.
Definition bloemen.hh:593
unsigned transitions()
Return number of transitions traversed.
Definition bloemen.hh:569
std::string name()
Return algorithm name.
Definition bloemen.hh:581
bool finisher()
Check if this thread finished the search.
Definition bloemen.hh:557
unsigned states()
Return number of states visited.
Definition bloemen.hh:563
void finalize()
Finalize thread resources.
Definition bloemen.hh:546
void run()
Run the algorithm.
Definition bloemen.hh:481
std::string trace()
Return trace (returns empty string)
Definition bloemen.hh:599
typename uf::uf_element uf_element
Type alias for union-find element.
Definition bloemen.hh:451
iterable_uf< State, StateHash, StateEqual > uf
Type alias for iterable union-find.
Definition bloemen.hh:449
swarmed_bloemen(kripkecube< State, SuccIterator > &sys, twacube_ptr, shared_map &map, iterable_uf< State, StateHash, StateEqual > *uf, unsigned tid, std::atomic< bool > &stop)
Constructor for parallel Bloemen algorithm.
Definition bloemen.hh:465
static shared_struct * make_shared_structure(shared_map m, unsigned i)
Create shared structure for thread tid.
Definition bloemen.hh:459
void setup()
Setup thread resources.
Definition bloemen.hh:540
A map of timer, where each timer has a name.
Definition timer.hh:231
void stop(const std::string &name)
Stop timer name.
Definition timer.hh:251
void start(const std::string &name)
Start a timer with name name.
Definition timer.hh:240
const spot::timer & timer(const std::string &name) const
Return the timer name.
Definition timer.hh:276
std::chrono::milliseconds::rep walltime() const
Return cumulative wall time.
Definition timer.hh:208
void deallocate(void *ptr)
Recycle size bytes of memory.
Definition fixpool.hh:133
std::shared_ptr< twacube > twacube_ptr
Definition fwd.hh:28
Definition automata.hh:26
mc_rvalue
Return value of a parallel model-checking algorithm.
Definition mc.hh:50
@ SUCCESS
The algorithm finished normally.
Hasher for union-find elements.
Definition bloemen.hh:73
uf_element_hasher(const uf_element *)
Constructor from element pointer.
Definition bloemen.hh:75
brick::hash::hash128_t hash(const uf_element *lhs) const
Compute hash of element.
Definition bloemen.hh:83
uf_element_hasher()=default
Default constructor.
bool equal(const uf_element *lhs, const uf_element *rhs) const
Check equality of elements.
Definition bloemen.hh:92
Represents a Union-Find element.
Definition bloemen.hh:56
std::atomic< list_status > list_status_
current status for the list
Definition bloemen.hh:68
std::atomic< unsigned > worker_
The set of worker for a given state.
Definition bloemen.hh:62
State st_
the state handled by the element
Definition bloemen.hh:58
std::atomic< uf_element * > parent
reference to the pointer
Definition bloemen.hh:60
std::atomic< uf_element * > next_
next element for work stealing
Definition bloemen.hh:64
std::atomic< uf_status > uf_status_
current status for the element
Definition bloemen.hh:66

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.8