source: mainline/uspace/lib/cpp/include/impl/mutex.hpp@ d275344

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d275344 was d350175, checked in by Dzejrou <dzejrou@…>, 7 years ago

cpp: added shared_timed_mutex

  • Property mode set to 100644
File size: 21.3 KB
Line 
1/*
2 * Copyright (c) 2018 Jaroslav Jindrak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef LIBCPP_MUTEX
30#define LIBCPP_MUTEX
31
32#include <functional>
33#include <internal/common.hpp>
34#include <internal/thread.hpp>
35#include <thread>
36
37namespace std
38{
39 /**
40 * 20.4.1.2.1, class mutex:
41 */
42
43 class mutex
44 {
45 public:
46 constexpr mutex() noexcept
47 : mtx_{}
48 {
49 aux::threading::mutex::init(mtx_);
50 }
51
52 ~mutex() = default;
53
54 mutex(const mutex&) = delete;
55 mutex& operator=(const mutex&) = delete;
56
57 void lock();
58 bool try_lock();
59 void unlock();
60
61 using native_handle_type = aux::mutex_t*;
62 native_handle_type native_handle();
63
64 private:
65 aux::mutex_t mtx_;
66 };
67
68 /**
69 * 30.4.1.2.2, class recursive_mutex:
70 */
71
72 class recursive_mutex
73 {
74 public:
75 constexpr recursive_mutex() noexcept
76 : mtx_{}, lock_level_{}, owner_{}
77 {
78 aux::threading::mutex::init(mtx_);
79 }
80
81 ~recursive_mutex();
82
83 recursive_mutex(const recursive_mutex&) = delete;
84 recursive_mutex& operator=(const recursive_mutex&) = delete;
85
86 void lock();
87 bool try_lock() noexcept;
88 void unlock();
89
90 using native_handle_type = aux::mutex_t*;
91 native_handle_type native_handle();
92
93 private:
94 aux::mutex_t mtx_;
95 size_t lock_level_;
96 thread::id owner_;
97 };
98
99 /**
100 * 30.4.1.3.1, class timed_mutex:
101 */
102
103 class timed_mutex
104 {
105 public:
106 timed_mutex() noexcept;
107 ~timed_mutex();
108
109 timed_mutex(const timed_mutex&) = delete;
110 timed_mutex& operator=(const timed_mutex&) = delete;
111
112 void lock();
113 bool try_lock();
114 void unlock();
115
116 template<class Rep, class Period>
117 bool try_lock_for(const chrono::duration<Rep, Period>& rel_time)
118 {
119 auto time = aux::threading::time::convert(rel_time);
120
121 return aux::threading::mutex::try_lock_for(time);
122 }
123
124 template<class Clock, class Duration>
125 bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time)
126 {
127 auto dur = (abs_time - Clock::now());
128 auto time = aux::threading::time::convert(dur);
129
130 return aux::threading::mutex::try_lock_for(time);
131 }
132
133 using native_handle_type = aux::mutex_t*;
134 native_handle_type native_handle();
135
136 private:
137 aux::mutex_t mtx_;
138 };
139
140 /**
141 * 30.4.1.3.2, class recursive_timed_mutex:
142 */
143
144 class recursive_timed_mutex
145 {
146 public:
147 recursive_timed_mutex() noexcept
148 : mtx_{}, lock_level_{}, owner_{}
149 {
150 aux::threading::mutex::init(mtx_);
151 }
152
153 ~recursive_timed_mutex();
154
155 recursive_timed_mutex(const recursive_timed_mutex&) = delete;
156 recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
157
158 void lock();
159 bool try_lock() noexcept;
160 void unlock();
161
162 template<class Rep, class Period>
163 bool try_lock_for(const chrono::duration<Rep, Period>& rel_time)
164 {
165 if (owner_ == this_thread::get_id())
166 return true;
167
168 auto time = aux::threading::time::convert(rel_time);
169 auto ret = aux::threading::mutex::try_lock_for(time);
170
171 if (ret)
172 ++lock_level_;
173 return ret;
174 }
175
176 template<class Clock, class Duration>
177 bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time)
178 {
179 if (owner_ == this_thread::get_id())
180 return true;
181
182 auto dur = (abs_time - Clock::now());
183 auto time = aux::threading::time::convert(dur);
184 auto ret = aux::threading::mutex::try_lock_for(time);
185
186 if (ret)
187 ++lock_level_;
188 return ret;
189 }
190
191 using native_handle_type = aux::mutex_t*;
192 native_handle_type native_handle();
193
194 private:
195 aux::mutex_t mtx_;
196 size_t lock_level_;
197 thread::id owner_;
198 };
199
200 /**
201 * 30.4.1.4.1, class shared_timed_mutex:
202 */
203
204 class shared_timed_mutex
205 {
206 public:
207 shared_timed_mutex() noexcept;
208 ~shared_timed_mutex();
209
210 shared_timed_mutex(const shared_timed_mutex&) = delete;
211 shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
212
213 void lock();
214 bool try_lock();
215 void unlock();
216
217 template<class Rep, class Period>
218 bool try_lock_for(const chrono::duration<Rep, Period>& rel_time)
219 {
220 auto time = aux::threading::time::convert(rel_time);
221
222 return aux::threading::shared_mutex::try_lock_for(time);
223 }
224
225 template<class Clock, class Duration>
226 bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time)
227 {
228 auto dur = (abs_time - Clock::now());
229 auto time = aux::threading::time::convert(dur);
230
231 return aux::threading::shared_mutex::try_lock_for(time);
232 }
233
234 void lock_shared();
235 bool try_lock_shared();
236 void unlock_shared();
237
238 template<class Rep, class Period>
239 bool try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time)
240 {
241 auto time = aux::threading::time::convert(rel_time);
242
243 return aux::threading::shared_mutex::try_lock_shared_for(time);
244 }
245
246 template<class Clock, class Duration>
247 bool try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time)
248 {
249 auto dur = (abs_time - Clock::now());
250 auto time = aux::threading::time::convert(dur);
251
252 return aux::threading::shared_mutex::try_lock_shared_for(time);
253 }
254
255 using native_handle_type = aux::shared_mutex_t*;
256 native_handle_type native_handle();
257
258 private:
259 aux::shared_mutex_t mtx_;
260 };
261
262 struct defer_lock_t
263 { /* DUMMY BODY */ };
264
265 struct try_to_lock_t
266 { /* DUMMY BODY */ };
267
268 struct adopt_lock_t
269 { /* DUMMY BODY */ };
270
271 constexpr defer_lock_t defer_lock
272 { /* DUMMY BODY */ };
273
274 constexpr try_to_lock_t try_to_lock
275 { /* DUMMY BODY */ };
276
277 constexpr adopt_lock_t adopt_lock
278 { /* DUMMY BODY */ };
279
280 /**
281 * 30.4.2.1, class template lock_guard:
282 */
283
284 template<class Mutex>
285 class lock_guard
286 {
287 public:
288 using mutex_type = Mutex;
289
290 explicit lock_guard(mutex_type& mtx)
291 : mtx_{mtx}
292 {
293 mtx.lock();
294 }
295
296 lock_guard(mutex_type& mtx, adopt_lock_t)
297 : mtx_{mtx}
298 { /* DUMMY BODY */ }
299
300 ~lock_guard()
301 {
302 mtx_.unlock();
303 }
304
305 lock_guard(const lock_guard&) = delete;
306 lock_guard& operator=(const lock_guard&) = delete;
307
308 private:
309 mutex_type& mtx_;
310 };
311
312 template<class Mutex>
313 class unique_lock
314 {
315 public:
316 using mutex_type = Mutex;
317
318 /**
319 * 30.4.2.2.1, construction/copy/destroy:
320 */
321
322 unique_lock() noexcept
323 : mtx_{nullptr}, owns_{false}
324 { /* DUMMY BODY */ }
325
326 explicit unique_lock(mutex_type& mtx)
327 : mtx_{&mtx}, owns_{true}
328 {
329 mtx_->lock();
330 }
331
332 unique_lock(mutex_type& mtx, defer_lock_t) noexcept
333 : mtx_{&mtx}, owns_{false}
334 { /* DUMMY BODY */ }
335
336 unique_lock(mutex_type& mtx, try_to_lock_t)
337 : mtx_{&mtx}, owns_{}
338 {
339 owns_ = mtx_->try_lock();
340 }
341
342 unique_lock(mutex_type& mtx, adopt_lock_t)
343 : mtx_{&mtx}, owns_{true}
344 { /* DUMMY BODY */ }
345
346 template<class Clock, class Duration>
347 unique_lock(mutex_type& mtx, const chrono::time_point<Clock, Duration>& abs_time)
348 : mtx_{&mtx}, owns_{}
349 {
350 owns_ = mtx_->try_lock_until(abs_time);
351 }
352
353 template<class Rep, class Period>
354 unique_lock(mutex_type& mtx, const chrono::duration<Rep, Period>& rel_time)
355 : mtx_{&mtx}, owns_{}
356 {
357 owns_ = mtx_->try_lock_for(rel_time);
358 }
359
360 ~unique_lock()
361 {
362 if (owns_)
363 mtx_->unlock();
364 }
365
366 unique_lock(const unique_lock&) = delete;
367 unique_lock& operator=(const unique_lock&) = delete;
368
369 unique_lock(unique_lock&& other) noexcept
370 : mtx_{move(other.mtx_)}, owns_{move(other.owns_)}
371 {
372 other.mtx_ = nullptr;
373 other.owns_ = false;
374 }
375
376 unique_lock& operator=(unique_lock&& other)
377 {
378 if (owns_)
379 mtx_->unlock();
380
381 mtx_ = move(other.mtx_);
382 owns_ = move(other.owns_);
383
384 other.mtx_ = nullptr;
385 other.owns_ = false;
386 }
387
388 /**
389 * 30.4.2.2.2, locking:
390 */
391
392 void lock()
393 {
394 /**
395 * TODO:
396 * throw system_error operation_not_permitted if mtx_ == nullptr
397 * throw system_error resource_deadlock_would_occur if owns_ == true
398 */
399
400 mtx_->lock();
401 owns_ = true;
402 }
403
404 bool try_lock()
405 {
406 /**
407 * TODO:
408 * throw system_error operation_not_permitted if mtx_ == nullptr
409 * throw system_error resource_deadlock_would_occur if owns_ == true
410 */
411
412 owns_ = mtx_->try_lock();
413
414 return owns_;
415 }
416
417 template<class Rep, class Period>
418 bool try_lock_for(const chrono::duration<Rep, Period>& rel_time)
419 {
420 /**
421 * TODO:
422 * throw system_error operation_not_permitted if mtx_ == nullptr
423 * throw system_error resource_deadlock_would_occur if owns_ == true
424 */
425
426 owns_ = mtx_->try_lock_for(rel_time);
427
428 return owns_;
429 }
430
431 template<class Clock, class Duration>
432 bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time)
433 {
434 /**
435 * TODO:
436 * throw system_error operation_not_permitted if mtx_ == nullptr
437 * throw system_error resource_deadlock_would_occur if owns_ == true
438 */
439
440 owns_ = mtx_->try_lock_until(abs_time);
441
442 return owns_;
443 }
444
445 void unlock()
446 {
447 /**
448 * TODO:
449 * throw system_error operation_not_permitted if owns_ == false
450 */
451
452 mtx_->unlock();
453 }
454
455 /**
456 * 30.4.2.2.3, modifiers:
457 */
458
459 void swap(unique_lock& other) noexcept
460 {
461 std::swap(mtx_, other.mtx_);
462 std::swap(owns_, other.owns_);
463 }
464
465 mutex_type* release() noexcept
466 {
467 auto ret = mtx_;
468 mtx_ = nullptr;
469 owns_ = false;
470
471 return ret;
472 }
473
474 /**
475 * 30.4.2.2.4, observers:
476 */
477
478 bool owns_lock() const noexcept
479 {
480 return owns_;
481 }
482
483 explicit operator bool() const noexcept
484 {
485 return owns_;
486 }
487
488 mutex_type* mutex() const noexcept
489 {
490 return mtx_;
491 }
492
493 private:
494 mutex_type* mtx_;
495 bool owns_;
496 };
497
498 template<class Mutex>
499 void swap(unique_lock<Mutex>& lhs, unique_lock<Mutex>& rhs) noexcept
500 {
501 lhs.swap(rhs);
502 }
503
504 /**
505 * 30.4.2.3, class template shared_lock:
506 */
507
508 template<class Mutex>
509 class shared_lock
510 {
511 public:
512 using mutex_type = Mutex;
513
514 /**
515 * 30.4.2.2.1, construction/copy/destroy:
516 */
517
518 shared_lock() noexcept
519 : mtx_{nullptr}, owns_{false}
520 { /* DUMMY BODY */ }
521
522 explicit shared_lock(mutex_type& mtx)
523 : mtx_{&mtx}, owns_{true}
524 {
525 mtx_->lock_shared();
526 }
527
528 shared_lock(mutex_type& mtx, defer_lock_t) noexcept
529 : mtx_{&mtx}, owns_{false}
530 { /* DUMMY BODY */ }
531
532 shared_lock(mutex_type& mtx, try_to_lock_t)
533 : mtx_{&mtx}, owns_{}
534 {
535 owns_ = mtx_->try_lock_shared();
536 }
537
538 shared_lock(mutex_type& mtx, adopt_lock_t)
539 : mtx_{&mtx}, owns_{true}
540 { /* DUMMY BODY */ }
541
542 template<class Clock, class Duration>
543 shared_lock(mutex_type& mtx, const chrono::time_point<Clock, Duration>& abs_time)
544 : mtx_{&mtx}, owns_{}
545 {
546 owns_ = mtx_->try_lock_shared_until(abs_time);
547 }
548
549 template<class Rep, class Period>
550 shared_lock(mutex_type& mtx, const chrono::duration<Rep, Period>& rel_time)
551 : mtx_{&mtx}, owns_{}
552 {
553 owns_ = mtx_->try_lock_shared_for(rel_time);
554 }
555
556 ~shared_lock()
557 {
558 if (owns_)
559 mtx_->unlock_shared();
560 }
561
562 shared_lock(const shared_lock&) = delete;
563 shared_lock& operator=(const shared_lock&) = delete;
564
565 shared_lock(shared_lock&& other) noexcept
566 : mtx_{move(other.mtx_)}, owns_{move(other.owns_)}
567 {
568 other.mtx_ = nullptr;
569 other.owns_ = false;
570 }
571
572 shared_lock& operator=(shared_lock&& other)
573 {
574 if (owns_)
575 mtx_->unlock_shared();
576
577 mtx_ = move(other.mtx_);
578 owns_ = move(other.owns_);
579
580 other.mtx_ = nullptr;
581 other.owns_ = false;
582 }
583
584 /**
585 * 30.4.2.2.2, locking:
586 */
587
588 void lock()
589 {
590 /**
591 * TODO:
592 * throw system_error operation_not_permitted if mtx_ == nullptr
593 * throw system_error resource_deadlock_would_occur if owns_ == true
594 */
595
596 mtx_->lock_shared();
597 owns_ = true;
598 }
599
600 bool try_lock()
601 {
602 /**
603 * TODO:
604 * throw system_error operation_not_permitted if mtx_ == nullptr
605 * throw system_error resource_deadlock_would_occur if owns_ == true
606 */
607
608 owns_ = mtx_->try_lock_shared();
609
610 return owns_;
611 }
612
613 template<class Rep, class Period>
614 bool try_lock_for(const chrono::duration<Rep, Period>& rel_time)
615 {
616 /**
617 * TODO:
618 * throw system_error operation_not_permitted if mtx_ == nullptr
619 * throw system_error resource_deadlock_would_occur if owns_ == true
620 */
621
622 owns_ = mtx_->try_lock_shared_for(rel_time);
623
624 return owns_;
625 }
626
627 template<class Clock, class Duration>
628 bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time)
629 {
630 /**
631 * TODO:
632 * throw system_error operation_not_permitted if mtx_ == nullptr
633 * throw system_error resource_deadlock_would_occur if owns_ == true
634 */
635
636 owns_ = mtx_->try_lock_shared_until(abs_time);
637
638 return owns_;
639 }
640
641 void unlock()
642 {
643 /**
644 * TODO:
645 * throw system_error operation_not_permitted if owns_ == false
646 */
647
648 mtx_->unlock_shared();
649 }
650
651 /**
652 * 30.4.2.2.3, modifiers:
653 */
654
655 void swap(shared_lock& other) noexcept
656 {
657 std::swap(mtx_, other.mtx_);
658 std::swap(owns_, other.owns_);
659 }
660
661 mutex_type* release() noexcept
662 {
663 auto ret = mtx_;
664 mtx_ = nullptr;
665 owns_ = false;
666
667 return ret;
668 }
669
670 /**
671 * 30.4.2.2.4, observers:
672 */
673
674 bool owns_lock() const noexcept
675 {
676 return owns_;
677 }
678
679 explicit operator bool() const noexcept
680 {
681 return owns_;
682 }
683
684 mutex_type* mutex() const noexcept
685 {
686 return mtx_;
687 }
688
689 private:
690 mutex_type* mtx_;
691 bool owns_;
692 };
693
694 template<class Mutex>
695 void swap(shared_lock<Mutex>& lhs, shared_lock<Mutex>& rhs) noexcept
696 {
697 lhs.swap(rhs);
698 }
699
700 namespace aux
701 {
702 template<class L>
703 int try_lock_tail(int idx, L& l)
704 {
705 if (!l.try_lock())
706 return idx;
707 else
708 return -1;
709 }
710
711 template<class L1, class... L2>
712 int try_lock_tail(int idx, L1& l1, L2&... ls)
713 {
714 if (!l1.try_lock())
715 return idx;
716
717 auto ret = try_lock_tail(idx + 1, ls...);
718 if (ret != -1)
719 l1.unlock();
720
721 return ret;
722 }
723 }
724
725 template<class L1, class L2, class... L3>
726 int try_lock(L1& l1, L2& l2, L3&... ls)
727 {
728 return aux::try_lock_tail(0, l1, l2, ls...);
729 }
730
731 namespace aux
732 {
733 template<class L>
734 bool lock_tail(L& l)
735 {
736 return l.try_lock();
737 }
738
739 template<class L1, class... L2>
740 bool lock_tail(L1& l1, L2&... ls)
741 {
742 if (l1.try_lock())
743 {
744 auto ret = lock_tail(ls...);
745 if (ret)
746 return true;
747
748 l1.unlock();
749 }
750
751 return false;
752 }
753 }
754
755 template<class L1, class L2, class... L3>
756 void lock(L1& l1, L2& l2, L3&... ls)
757 {
758 do
759 {
760 l1.lock();
761
762 if (aux::lock_tail(l2, ls...))
763 return;
764 l1.unlock();
765 } while (true);
766 }
767
768 struct once_flag
769 {
770 constexpr once_flag() noexcept
771 : called_{false}, mtx_{}
772 { /* DUMMY BODY */ }
773
774 once_flag(const once_flag&) = delete;
775 once_flag& operator=(const once_flag&) = delete;
776
777 private:
778 bool called_;
779 mutex mtx_;
780
781 template<class Callable, class... Args>
782 friend void call_once(once_flag&, Callable&&, Args&&...);
783 };
784
785 template<class Callable, class... Args>
786 void call_once(once_flag& flag, Callable&& func, Args&&... args)
787 {
788 flag.mtx_.lock();
789 if (!flag.called_)
790 {
791 // TODO: exception handling
792
793 aux::invoke(forward<Callable>(func), forward<Args>(args)...);
794 flag.called_ = true;
795 }
796 flag.mtx_.unlock();
797 }
798}
799
800#endif
Note: See TracBrowser for help on using the repository browser.