1 | #ifndef BOOST_THREAD_PTHREAD_SHARED_MUTEX_HPP |
2 | #define BOOST_THREAD_PTHREAD_SHARED_MUTEX_HPP |
3 | |
4 | // (C) Copyright 2006-8 Anthony Williams |
5 | // (C) Copyright 2012 Vicente J. Botet Escriba |
6 | // |
7 | // Distributed under the Boost Software License, Version 1.0. (See |
8 | // accompanying file LICENSE_1_0.txt or copy at |
9 | // http://www.boost.org/LICENSE_1_0.txt) |
10 | |
11 | #include <boost/assert.hpp> |
12 | #include <boost/static_assert.hpp> |
13 | #include <boost/thread/mutex.hpp> |
14 | #include <boost/thread/condition_variable.hpp> |
15 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
16 | #include <boost/thread/detail/thread_interruption.hpp> |
17 | #endif |
18 | #ifdef BOOST_THREAD_USES_CHRONO |
19 | #include <boost/chrono/system_clocks.hpp> |
20 | #include <boost/chrono/ceil.hpp> |
21 | #endif |
22 | #include <boost/thread/detail/delete.hpp> |
23 | #include <boost/assert.hpp> |
24 | |
25 | #include <boost/config/abi_prefix.hpp> |
26 | |
27 | namespace boost |
28 | { |
29 | class shared_mutex |
30 | { |
31 | private: |
32 | class state_data |
33 | { |
34 | public: |
35 | state_data () : |
36 | shared_count(0), |
37 | exclusive(false), |
38 | upgrade(false), |
39 | exclusive_waiting_blocked(false) |
40 | {} |
41 | |
42 | void assert_free() const |
43 | { |
44 | BOOST_ASSERT( ! exclusive ); |
45 | BOOST_ASSERT( ! upgrade ); |
46 | BOOST_ASSERT( shared_count==0 ); |
47 | } |
48 | |
49 | void assert_locked() const |
50 | { |
51 | BOOST_ASSERT( exclusive ); |
52 | BOOST_ASSERT( shared_count==0 ); |
53 | BOOST_ASSERT( ! upgrade ); |
54 | } |
55 | |
56 | void assert_lock_shared () const |
57 | { |
58 | BOOST_ASSERT( ! exclusive ); |
59 | BOOST_ASSERT( shared_count>0 ); |
60 | //BOOST_ASSERT( (! upgrade) || (shared_count>1)); |
61 | // if upgraded there are at least 2 threads sharing the mutex, |
62 | // except when unlock_upgrade_and_lock has decreased the number of readers but has not taken yet exclusive ownership. |
63 | } |
64 | |
65 | void assert_lock_upgraded () const |
66 | { |
67 | BOOST_ASSERT( ! exclusive ); |
68 | BOOST_ASSERT( upgrade ); |
69 | BOOST_ASSERT( shared_count>0 ); |
70 | } |
71 | |
72 | void assert_lock_not_upgraded () const |
73 | { |
74 | BOOST_ASSERT( ! upgrade ); |
75 | } |
76 | |
77 | bool can_lock () const |
78 | { |
79 | return ! (shared_count || exclusive); |
80 | } |
81 | |
82 | void exclusive_blocked (bool blocked) |
83 | { |
84 | exclusive_waiting_blocked = blocked; |
85 | } |
86 | |
87 | void lock () |
88 | { |
89 | exclusive = true; |
90 | } |
91 | |
92 | void unlock () |
93 | { |
94 | exclusive = false; |
95 | exclusive_waiting_blocked = false; |
96 | } |
97 | |
98 | bool can_lock_shared () const |
99 | { |
100 | return ! (exclusive || exclusive_waiting_blocked); |
101 | } |
102 | |
103 | bool more_shared () const |
104 | { |
105 | return shared_count > 0 ; |
106 | } |
107 | unsigned get_shared_count () const |
108 | { |
109 | return shared_count ; |
110 | } |
111 | unsigned lock_shared () |
112 | { |
113 | return ++shared_count; |
114 | } |
115 | |
116 | |
117 | void unlock_shared () |
118 | { |
119 | --shared_count; |
120 | } |
121 | |
122 | bool unlock_shared_downgrades() |
123 | { |
124 | if (upgrade) { |
125 | upgrade=false; |
126 | exclusive=true; |
127 | return true; |
128 | } else { |
129 | exclusive_waiting_blocked=false; |
130 | return false; |
131 | } |
132 | } |
133 | |
134 | void lock_upgrade () |
135 | { |
136 | ++shared_count; |
137 | upgrade=true; |
138 | } |
139 | bool can_lock_upgrade () const |
140 | { |
141 | return ! (exclusive || exclusive_waiting_blocked || upgrade); |
142 | } |
143 | |
144 | void unlock_upgrade () |
145 | { |
146 | upgrade=false; |
147 | --shared_count; |
148 | } |
149 | |
150 | //private: |
151 | unsigned shared_count; |
152 | bool exclusive; |
153 | bool upgrade; |
154 | bool exclusive_waiting_blocked; |
155 | }; |
156 | |
157 | |
158 | |
159 | state_data state; |
160 | boost::mutex state_change; |
161 | boost::condition_variable shared_cond; |
162 | boost::condition_variable exclusive_cond; |
163 | boost::condition_variable upgrade_cond; |
164 | |
165 | void release_waiters() |
166 | { |
167 | exclusive_cond.notify_one(); |
168 | shared_cond.notify_all(); |
169 | } |
170 | |
171 | public: |
172 | |
173 | BOOST_THREAD_NO_COPYABLE(shared_mutex) |
174 | |
175 | shared_mutex() |
176 | { |
177 | } |
178 | |
179 | ~shared_mutex() |
180 | { |
181 | } |
182 | |
183 | void lock_shared() |
184 | { |
185 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
186 | boost::this_thread::disable_interruption do_not_disturb; |
187 | #endif |
188 | boost::unique_lock<boost::mutex> lk(state_change); |
189 | while(!state.can_lock_shared()) |
190 | { |
191 | shared_cond.wait(lk); |
192 | } |
193 | state.lock_shared(); |
194 | } |
195 | |
196 | bool try_lock_shared() |
197 | { |
198 | boost::unique_lock<boost::mutex> lk(state_change); |
199 | |
200 | if(!state.can_lock_shared()) |
201 | { |
202 | return false; |
203 | } |
204 | state.lock_shared(); |
205 | return true; |
206 | } |
207 | |
208 | #if defined BOOST_THREAD_USES_DATETIME |
209 | bool timed_lock_shared(system_time const& timeout) |
210 | { |
211 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
212 | boost::this_thread::disable_interruption do_not_disturb; |
213 | #endif |
214 | boost::unique_lock<boost::mutex> lk(state_change); |
215 | |
216 | while(!state.can_lock_shared()) |
217 | { |
218 | if(!shared_cond.timed_wait(lk,timeout)) |
219 | { |
220 | return false; |
221 | } |
222 | } |
223 | state.lock_shared(); |
224 | return true; |
225 | } |
226 | |
227 | template<typename TimeDuration> |
228 | bool timed_lock_shared(TimeDuration const & relative_time) |
229 | { |
230 | return timed_lock_shared(get_system_time()+relative_time); |
231 | } |
232 | #endif |
233 | #ifdef BOOST_THREAD_USES_CHRONO |
234 | template <class Rep, class Period> |
235 | bool try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time) |
236 | { |
237 | return try_lock_shared_until(chrono::steady_clock::now() + rel_time); |
238 | } |
239 | template <class Clock, class Duration> |
240 | bool try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time) |
241 | { |
242 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
243 | boost::this_thread::disable_interruption do_not_disturb; |
244 | #endif |
245 | boost::unique_lock<boost::mutex> lk(state_change); |
246 | |
247 | while(!state.can_lock_shared()) |
248 | //while(state.exclusive || state.exclusive_waiting_blocked) |
249 | { |
250 | if(cv_status::timeout==shared_cond.wait_until(lk,abs_time)) |
251 | { |
252 | return false; |
253 | } |
254 | } |
255 | state.lock_shared(); |
256 | return true; |
257 | } |
258 | #endif |
259 | void unlock_shared() |
260 | { |
261 | boost::unique_lock<boost::mutex> lk(state_change); |
262 | state.assert_lock_shared(); |
263 | state.unlock_shared(); |
264 | if (! state.more_shared()) |
265 | { |
266 | if (state.upgrade) |
267 | { |
268 | // As there is a thread doing a unlock_upgrade_and_lock that is waiting for ! state.more_shared() |
269 | // avoid other threads to lock, lock_upgrade or lock_shared, so only this thread is notified. |
270 | state.upgrade=false; |
271 | state.exclusive=true; |
272 | //lk.unlock(); |
273 | upgrade_cond.notify_one(); |
274 | } |
275 | else |
276 | { |
277 | state.exclusive_waiting_blocked=false; |
278 | //lk.unlock(); |
279 | } |
280 | release_waiters(); |
281 | } |
282 | } |
283 | |
284 | void lock() |
285 | { |
286 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
287 | boost::this_thread::disable_interruption do_not_disturb; |
288 | #endif |
289 | boost::unique_lock<boost::mutex> lk(state_change); |
290 | |
291 | while (state.shared_count || state.exclusive) |
292 | { |
293 | state.exclusive_waiting_blocked=true; |
294 | exclusive_cond.wait(lk); |
295 | } |
296 | state.exclusive=true; |
297 | } |
298 | |
299 | #if defined BOOST_THREAD_USES_DATETIME |
300 | bool timed_lock(system_time const& timeout) |
301 | { |
302 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
303 | boost::this_thread::disable_interruption do_not_disturb; |
304 | #endif |
305 | boost::unique_lock<boost::mutex> lk(state_change); |
306 | |
307 | while(state.shared_count || state.exclusive) |
308 | { |
309 | state.exclusive_waiting_blocked=true; |
310 | if(!exclusive_cond.timed_wait(lk,timeout)) |
311 | { |
312 | if(state.shared_count || state.exclusive) |
313 | { |
314 | state.exclusive_waiting_blocked=false; |
315 | release_waiters(); |
316 | return false; |
317 | } |
318 | break; |
319 | } |
320 | } |
321 | state.exclusive=true; |
322 | return true; |
323 | } |
324 | |
325 | template<typename TimeDuration> |
326 | bool timed_lock(TimeDuration const & relative_time) |
327 | { |
328 | return timed_lock(get_system_time()+relative_time); |
329 | } |
330 | #endif |
331 | #ifdef BOOST_THREAD_USES_CHRONO |
332 | template <class Rep, class Period> |
333 | bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) |
334 | { |
335 | return try_lock_until(chrono::steady_clock::now() + rel_time); |
336 | } |
337 | template <class Clock, class Duration> |
338 | bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time) |
339 | { |
340 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
341 | boost::this_thread::disable_interruption do_not_disturb; |
342 | #endif |
343 | boost::unique_lock<boost::mutex> lk(state_change); |
344 | |
345 | while(state.shared_count || state.exclusive) |
346 | { |
347 | state.exclusive_waiting_blocked=true; |
348 | if(cv_status::timeout == exclusive_cond.wait_until(lk,abs_time)) |
349 | { |
350 | if(state.shared_count || state.exclusive) |
351 | { |
352 | state.exclusive_waiting_blocked=false; |
353 | release_waiters(); |
354 | return false; |
355 | } |
356 | break; |
357 | } |
358 | } |
359 | state.exclusive=true; |
360 | return true; |
361 | } |
362 | #endif |
363 | |
364 | bool try_lock() |
365 | { |
366 | boost::unique_lock<boost::mutex> lk(state_change); |
367 | |
368 | if(state.shared_count || state.exclusive) |
369 | { |
370 | return false; |
371 | } |
372 | else |
373 | { |
374 | state.exclusive=true; |
375 | return true; |
376 | } |
377 | |
378 | } |
379 | |
380 | void unlock() |
381 | { |
382 | boost::unique_lock<boost::mutex> lk(state_change); |
383 | state.assert_locked(); |
384 | state.exclusive=false; |
385 | state.exclusive_waiting_blocked=false; |
386 | state.assert_free(); |
387 | release_waiters(); |
388 | } |
389 | |
390 | void lock_upgrade() |
391 | { |
392 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
393 | boost::this_thread::disable_interruption do_not_disturb; |
394 | #endif |
395 | boost::unique_lock<boost::mutex> lk(state_change); |
396 | while(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) |
397 | { |
398 | shared_cond.wait(lk); |
399 | } |
400 | state.lock_shared(); |
401 | state.upgrade=true; |
402 | } |
403 | |
404 | #if defined BOOST_THREAD_USES_DATETIME |
405 | bool timed_lock_upgrade(system_time const& timeout) |
406 | { |
407 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
408 | boost::this_thread::disable_interruption do_not_disturb; |
409 | #endif |
410 | boost::unique_lock<boost::mutex> lk(state_change); |
411 | while(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) |
412 | { |
413 | if(!shared_cond.timed_wait(lk,timeout)) |
414 | { |
415 | if(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) |
416 | { |
417 | return false; |
418 | } |
419 | break; |
420 | } |
421 | } |
422 | state.lock_shared(); |
423 | state.upgrade=true; |
424 | return true; |
425 | } |
426 | |
427 | template<typename TimeDuration> |
428 | bool timed_lock_upgrade(TimeDuration const & relative_time) |
429 | { |
430 | return timed_lock_upgrade(get_system_time()+relative_time); |
431 | } |
432 | #endif |
433 | #ifdef BOOST_THREAD_USES_CHRONO |
434 | template <class Rep, class Period> |
435 | bool try_lock_upgrade_for(const chrono::duration<Rep, Period>& rel_time) |
436 | { |
437 | return try_lock_upgrade_until(chrono::steady_clock::now() + rel_time); |
438 | } |
439 | template <class Clock, class Duration> |
440 | bool try_lock_upgrade_until(const chrono::time_point<Clock, Duration>& abs_time) |
441 | { |
442 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
443 | boost::this_thread::disable_interruption do_not_disturb; |
444 | #endif |
445 | boost::unique_lock<boost::mutex> lk(state_change); |
446 | while(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) |
447 | { |
448 | if(cv_status::timeout == shared_cond.wait_until(lk,abs_time)) |
449 | { |
450 | if(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) |
451 | { |
452 | return false; |
453 | } |
454 | break; |
455 | } |
456 | } |
457 | state.lock_shared(); |
458 | state.upgrade=true; |
459 | return true; |
460 | } |
461 | #endif |
462 | bool try_lock_upgrade() |
463 | { |
464 | boost::unique_lock<boost::mutex> lk(state_change); |
465 | if(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) |
466 | { |
467 | return false; |
468 | } |
469 | else |
470 | { |
471 | state.lock_shared(); |
472 | state.upgrade=true; |
473 | state.assert_lock_upgraded(); |
474 | return true; |
475 | } |
476 | } |
477 | |
478 | void unlock_upgrade() |
479 | { |
480 | boost::unique_lock<boost::mutex> lk(state_change); |
481 | //state.upgrade=false; |
482 | state.unlock_upgrade(); |
483 | if(! state.more_shared() ) |
484 | { |
485 | state.exclusive_waiting_blocked=false; |
486 | release_waiters(); |
487 | } else { |
488 | shared_cond.notify_all(); |
489 | } |
490 | } |
491 | |
492 | // Upgrade <-> Exclusive |
493 | void unlock_upgrade_and_lock() |
494 | { |
495 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
496 | boost::this_thread::disable_interruption do_not_disturb; |
497 | #endif |
498 | boost::unique_lock<boost::mutex> lk(state_change); |
499 | state.assert_lock_upgraded(); |
500 | state.unlock_shared(); |
501 | while (state.more_shared()) |
502 | { |
503 | upgrade_cond.wait(lk); |
504 | } |
505 | state.upgrade=false; |
506 | state.exclusive=true; |
507 | state.assert_locked(); |
508 | } |
509 | |
510 | void unlock_and_lock_upgrade() |
511 | { |
512 | boost::unique_lock<boost::mutex> lk(state_change); |
513 | state.assert_locked(); |
514 | state.exclusive=false; |
515 | state.upgrade=true; |
516 | state.lock_shared(); |
517 | state.exclusive_waiting_blocked=false; |
518 | state.assert_lock_upgraded(); |
519 | release_waiters(); |
520 | } |
521 | |
522 | bool try_unlock_upgrade_and_lock() |
523 | { |
524 | boost::unique_lock<boost::mutex> lk(state_change); |
525 | state.assert_lock_upgraded(); |
526 | if( !state.exclusive |
527 | && !state.exclusive_waiting_blocked |
528 | && state.upgrade |
529 | && state.shared_count==1) |
530 | { |
531 | state.shared_count=0; |
532 | state.exclusive=true; |
533 | state.upgrade=false; |
534 | state.assert_locked(); |
535 | return true; |
536 | } |
537 | return false; |
538 | } |
539 | #ifdef BOOST_THREAD_USES_CHRONO |
540 | template <class Rep, class Period> |
541 | bool |
542 | try_unlock_upgrade_and_lock_for( |
543 | const chrono::duration<Rep, Period>& rel_time) |
544 | { |
545 | return try_unlock_upgrade_and_lock_until( |
546 | chrono::steady_clock::now() + rel_time); |
547 | } |
548 | template <class Clock, class Duration> |
549 | bool |
550 | try_unlock_upgrade_and_lock_until( |
551 | const chrono::time_point<Clock, Duration>& abs_time) |
552 | { |
553 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
554 | boost::this_thread::disable_interruption do_not_disturb; |
555 | #endif |
556 | boost::unique_lock<boost::mutex> lk(state_change); |
557 | state.assert_lock_upgraded(); |
558 | if (state.shared_count != 1) |
559 | { |
560 | for (;;) |
561 | { |
562 | cv_status status = shared_cond.wait_until(lk,abs_time); |
563 | if (state.shared_count == 1) |
564 | break; |
565 | if(status == cv_status::timeout) |
566 | return false; |
567 | } |
568 | } |
569 | state.upgrade=false; |
570 | state.exclusive=true; |
571 | state.exclusive_waiting_blocked=false; |
572 | state.shared_count=0; |
573 | return true; |
574 | } |
575 | #endif |
576 | |
577 | // Shared <-> Exclusive |
578 | void unlock_and_lock_shared() |
579 | { |
580 | boost::unique_lock<boost::mutex> lk(state_change); |
581 | state.assert_locked(); |
582 | state.exclusive=false; |
583 | state.lock_shared(); |
584 | state.exclusive_waiting_blocked=false; |
585 | release_waiters(); |
586 | } |
587 | |
588 | #ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS |
589 | bool try_unlock_shared_and_lock() |
590 | { |
591 | boost::unique_lock<boost::mutex> lk(state_change); |
592 | state.assert_lock_shared(); |
593 | if( !state.exclusive |
594 | && !state.exclusive_waiting_blocked |
595 | && !state.upgrade |
596 | && state.shared_count==1) |
597 | { |
598 | state.shared_count=0; |
599 | state.exclusive=true; |
600 | return true; |
601 | } |
602 | return false; |
603 | } |
604 | #ifdef BOOST_THREAD_USES_CHRONO |
605 | template <class Rep, class Period> |
606 | bool |
607 | try_unlock_shared_and_lock_for( |
608 | const chrono::duration<Rep, Period>& rel_time) |
609 | { |
610 | return try_unlock_shared_and_lock_until( |
611 | chrono::steady_clock::now() + rel_time); |
612 | } |
613 | template <class Clock, class Duration> |
614 | bool |
615 | try_unlock_shared_and_lock_until( |
616 | const chrono::time_point<Clock, Duration>& abs_time) |
617 | { |
618 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
619 | boost::this_thread::disable_interruption do_not_disturb; |
620 | #endif |
621 | boost::unique_lock<boost::mutex> lk(state_change); |
622 | state.assert_lock_shared(); |
623 | if (state.shared_count != 1) |
624 | { |
625 | for (;;) |
626 | { |
627 | cv_status status = shared_cond.wait_until(lk,abs_time); |
628 | if (state.shared_count == 1) |
629 | break; |
630 | if(status == cv_status::timeout) |
631 | return false; |
632 | } |
633 | } |
634 | state.upgrade=false; |
635 | state.exclusive=true; |
636 | state.exclusive_waiting_blocked=false; |
637 | state.shared_count=0; |
638 | return true; |
639 | } |
640 | #endif |
641 | #endif |
642 | |
643 | // Shared <-> Upgrade |
644 | void unlock_upgrade_and_lock_shared() |
645 | { |
646 | boost::unique_lock<boost::mutex> lk(state_change); |
647 | state.assert_lock_upgraded(); |
648 | state.upgrade=false; |
649 | state.exclusive_waiting_blocked=false; |
650 | release_waiters(); |
651 | } |
652 | |
653 | #ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS |
654 | bool try_unlock_shared_and_lock_upgrade() |
655 | { |
656 | boost::unique_lock<boost::mutex> lk(state_change); |
657 | state.assert_lock_shared(); |
658 | if( !state.exclusive |
659 | && !state.exclusive_waiting_blocked |
660 | && !state.upgrade |
661 | ) |
662 | { |
663 | state.upgrade=true; |
664 | return true; |
665 | } |
666 | return false; |
667 | } |
668 | #ifdef BOOST_THREAD_USES_CHRONO |
669 | template <class Rep, class Period> |
670 | bool |
671 | try_unlock_shared_and_lock_upgrade_for( |
672 | const chrono::duration<Rep, Period>& rel_time) |
673 | { |
674 | return try_unlock_shared_and_lock_upgrade_until( |
675 | chrono::steady_clock::now() + rel_time); |
676 | } |
677 | template <class Clock, class Duration> |
678 | bool |
679 | try_unlock_shared_and_lock_upgrade_until( |
680 | const chrono::time_point<Clock, Duration>& abs_time) |
681 | { |
682 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
683 | boost::this_thread::disable_interruption do_not_disturb; |
684 | #endif |
685 | boost::unique_lock<boost::mutex> lk(state_change); |
686 | state.assert_lock_shared(); |
687 | if( state.exclusive |
688 | || state.exclusive_waiting_blocked |
689 | || state.upgrade |
690 | ) |
691 | { |
692 | for (;;) |
693 | { |
694 | cv_status status = exclusive_cond.wait_until(lk,abs_time); |
695 | if( ! state.exclusive |
696 | && ! state.exclusive_waiting_blocked |
697 | && ! state.upgrade |
698 | ) |
699 | break; |
700 | if(status == cv_status::timeout) |
701 | return false; |
702 | } |
703 | } |
704 | state.upgrade=true; |
705 | return true; |
706 | } |
707 | #endif |
708 | #endif |
709 | }; |
710 | |
711 | typedef shared_mutex upgrade_mutex; |
712 | } |
713 | |
714 | #include <boost/config/abi_suffix.hpp> |
715 | |
716 | #endif |
717 | |