forked from swiftlang/swift
-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathMutex.h
793 lines (694 loc) · 27.9 KB
/
Mutex.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
//===--- Mutex.h - Mutex, ConditionVariable, & ReadWriteLock ----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Mutex, ConditionVariable, Read/Write lock, and Scoped lock abstractions
// for use in Swift runtime.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_RUNTIME_MUTEX_H
#define SWIFT_RUNTIME_MUTEX_H
#include <type_traits>
#if (defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__HAIKU__))
#include "swift/Runtime/MutexPThread.h"
#elif defined(_WIN32)
#include "swift/Runtime/MutexWin32.h"
#elif defined(__wasi__)
#include "swift/Runtime/MutexWASI.h"
#else
#error "Implement equivalent of MutexPThread.h/cpp for your platform."
#endif
namespace swift {
/// A stack based object that notifies one thread waiting on a condition
/// variable on destruction.
template <typename ConditionVariable>
class ScopedNotifyOneT {
ScopedNotifyOneT() = delete;
ScopedNotifyOneT(const ScopedNotifyOneT &) = delete;
ScopedNotifyOneT &operator=(const ScopedNotifyOneT &) = delete;
ConditionVariable &Condition;
public:
explicit ScopedNotifyOneT(ConditionVariable &c) : Condition(c) {}
~ScopedNotifyOneT() {
Condition.notifyOne();
}
};
/// A stack based object that notifies all threads waiting on a condition
/// variable on destruction.
template <typename ConditionVariable>
class ScopedNotifyAllT {
ScopedNotifyAllT() = delete;
ScopedNotifyAllT(const ScopedNotifyAllT &) = delete;
ScopedNotifyAllT &operator=(const ScopedNotifyAllT &) = delete;
ConditionVariable &Condition;
public:
explicit ScopedNotifyAllT(ConditionVariable &c) : Condition(c) {}
~ScopedNotifyAllT() {
Condition.notifyAll();
}
};
/// A ConditionVariable that works with Mutex to allow -- as an example --
/// multi-threaded producers and consumers to signal each other in a safe way.
class ConditionVariable {
friend class Mutex;
ConditionVariable(const ConditionVariable &) = delete;
ConditionVariable &operator=(const ConditionVariable &) = delete;
ConditionVariable(ConditionVariable &&) = delete;
ConditionVariable &operator=(ConditionVariable &&) = delete;
public:
ConditionVariable() { ConditionPlatformHelper::init(Handle); }
~ConditionVariable() { ConditionPlatformHelper::destroy(Handle); }
/// Notifies one waiter (if any exists) that the condition has been met.
///
/// Note: To avoid missed notification it is best hold the related mutex
// lock when calling notifyOne.
void notifyOne() { ConditionPlatformHelper::notifyOne(Handle); }
/// Notifies all waiters (if any exists) that the condition has been met.
///
/// Note: To avoid missed notification it is best hold the related mutex
// lock when calling notifyAll.
void notifyAll() { ConditionPlatformHelper::notifyAll(Handle); }
private:
ConditionHandle Handle;
};
using ScopedNotifyOne = ScopedNotifyOneT<ConditionVariable>;
using ScopedNotifyAll = ScopedNotifyAllT<ConditionVariable>;
/// Compile time adjusted stack based object that locks/unlocks the supplied
/// Mutex type. Use the provided typedefs instead of this directly.
template <typename T, bool Inverted> class ScopedLockT {
ScopedLockT() = delete;
ScopedLockT(const ScopedLockT &) = delete;
ScopedLockT &operator=(const ScopedLockT &) = delete;
ScopedLockT(ScopedLockT &&) = delete;
ScopedLockT &operator=(ScopedLockT &&) = delete;
public:
explicit ScopedLockT(T &l) : Lock(l) {
if (Inverted) {
Lock.unlock();
} else {
Lock.lock();
}
}
~ScopedLockT() {
if (Inverted) {
Lock.lock();
} else {
Lock.unlock();
}
}
private:
T &Lock;
};
class Mutex;
class StaticMutex;
/// A stack based object that locks the supplied mutex on construction
/// and unlocks it on destruction.
///
/// Precondition: Mutex unlocked by this thread, undefined otherwise.
typedef ScopedLockT<Mutex, false> ScopedLock;
typedef ScopedLockT<StaticMutex, false> StaticScopedLock;
/// A stack based object that unlocks the supplied mutex on construction
/// and relocks it on destruction.
///
/// Precondition: Mutex locked by this thread, undefined otherwise.
typedef ScopedLockT<Mutex, true> ScopedUnlock;
typedef ScopedLockT<StaticMutex, true> StaticScopedUnlock;
/// A Mutex object that supports `BasicLockable` and `Lockable` C++ concepts.
/// See http://en.cppreference.com/w/cpp/concept/BasicLockable
/// See http://en.cppreference.com/w/cpp/concept/Lockable
///
/// This is NOT a recursive mutex.
class Mutex {
Mutex(const Mutex &) = delete;
Mutex &operator=(const Mutex &) = delete;
Mutex(Mutex &&) = delete;
Mutex &operator=(Mutex &&) = delete;
public:
/// Constructs a non-recursive mutex.
///
/// If `checked` is true the mutex will attempt to check for misuse and
/// fatalError when detected. If `checked` is false (the default) the
/// mutex will make little to no effort to check for misuse (more efficient).
explicit Mutex(bool checked = false) {
MutexPlatformHelper::init(Handle, checked);
}
~Mutex() { MutexPlatformHelper::destroy(Handle); }
/// The lock() method has the following properties:
/// - Behaves as an atomic operation.
/// - Blocks the calling thread until exclusive ownership of the mutex
/// can be obtained.
/// - Prior m.unlock() operations on the same mutex synchronize-with
/// this lock operation.
/// - The behavior is undefined if the calling thread already owns
/// the mutex (likely a deadlock).
/// - Does not throw exceptions but will halt on error (fatalError).
void lock() { MutexPlatformHelper::lock(Handle); }
/// The unlock() method has the following properties:
/// - Behaves as an atomic operation.
/// - Releases the calling thread's ownership of the mutex and
/// synchronizes-with the subsequent successful lock operations on
/// the same object.
/// - The behavior is undefined if the calling thread does not own
/// the mutex.
/// - Does not throw exceptions but will halt on error (fatalError).
void unlock() { MutexPlatformHelper::unlock(Handle); }
/// The try_lock() method has the following properties:
/// - Behaves as an atomic operation.
/// - Attempts to obtain exclusive ownership of the mutex for the calling
/// thread without blocking. If ownership is not obtained, returns
/// immediately. The function is allowed to spuriously fail and return
/// even if the mutex is not currently owned by another thread.
/// - If try_lock() succeeds, prior unlock() operations on the same object
/// synchronize-with this operation. lock() does not synchronize with a
/// failed try_lock()
/// - The behavior is undefined if the calling thread already owns
/// the mutex (likely a deadlock)?
/// - Does not throw exceptions but will halt on error (fatalError).
bool try_lock() { return MutexPlatformHelper::try_lock(Handle); }
/// Releases lock, waits on supplied condition, and relocks before returning.
///
/// Precondition: Mutex held by this thread, undefined otherwise.
void wait(ConditionVariable &condition) {
ConditionPlatformHelper::wait(condition.Handle, Handle);
}
/// Acquires lock before calling the supplied critical section and releases
/// lock on return from critical section.
///
/// This call can block while waiting for the lock to become available.
///
/// For example the following mutates value while holding the mutex lock.
///
/// ```
/// mutex.lock([&value] { value++; });
/// ```
///
/// Precondition: Mutex not held by this thread, undefined otherwise.
template <typename CriticalSection>
auto withLock(CriticalSection criticalSection) -> decltype(criticalSection()){
ScopedLock guard(*this);
return criticalSection();
}
/// Acquires lock before calling the supplied critical section. If critical
/// section returns `false` then it will wait on the supplied condition and
/// call the critical section again when wait returns (after acquiring lock).
/// If critical section returns `true` (done) it will no longer wait, it
/// will release the lock and return (lockOrWait returns to caller).
///
/// This call can block while waiting for the lock to become available.
///
/// For example the following will loop waiting on the condition until
/// `value > 0`. It will then "consume" that value and stop looping.
/// ...all while being correctly protected by mutex.
///
/// ```
/// mutex.withLockOrWait(condition, [&value] {
/// if (value > 0) {
/// value--;
/// return true;
/// }
/// return false;
/// });
/// ```
///
/// Precondition: Mutex not held by this thread, undefined otherwise.
template <typename CriticalSection>
void withLockOrWait(ConditionVariable &condition,
CriticalSection criticalSection) {
withLock([&] {
while (!criticalSection()) {
wait(condition);
}
});
}
/// Acquires lock before calling the supplied critical section and on return
/// from critical section it notifies one waiter of supplied condition and
/// then releases the lock.
///
/// This call can block while waiting for the lock to become available.
///
/// For example the following mutates value while holding the mutex lock and
/// then notifies one condition waiter about this change.
///
/// ```
/// mutex.withLockThenNotifyOne(condition, [&value] { value++; });
/// ```
///
/// Precondition: Mutex not held by this thread, undefined otherwise.
template <typename CriticalSection>
auto withLockThenNotifyOne(ConditionVariable &condition,
CriticalSection criticalSection)
-> decltype(criticalSection()) {
return withLock([&] {
ScopedNotifyOne guard(condition);
return criticalSection();
});
}
/// Acquires lock before calling the supplied critical section and on return
/// from critical section it notifies all waiters of supplied condition and
/// then releases the lock.
///
/// This call can block while waiting for the lock to become available.
///
/// For example the following mutates value while holding the mutex lock and
/// then notifies all condition waiters about this change.
///
/// ```
/// mutex.withLockThenNotifyAll(condition, [&value] { value++; });
/// ```
///
/// Precondition: Mutex not held by this thread, undefined otherwise.
template <typename CriticalSection>
auto withLockThenNotifyAll(ConditionVariable &condition,
CriticalSection criticalSection)
-> decltype(criticalSection()) {
return withLock([&] {
ScopedNotifyAll guard(condition);
return criticalSection();
});
}
private:
MutexHandle Handle;
};
/// Compile time adjusted stack based object that locks/unlocks the supplied
/// ReadWriteLock type. Use the provided typedefs instead of this directly.
template <typename T, bool Read, bool Inverted> class ScopedRWLockT {
ScopedRWLockT() = delete;
ScopedRWLockT(const ScopedRWLockT &) = delete;
ScopedRWLockT &operator=(const ScopedRWLockT &) = delete;
ScopedRWLockT(ScopedRWLockT &&) = delete;
ScopedRWLockT &operator=(ScopedRWLockT &&) = delete;
public:
explicit ScopedRWLockT(T &l) : Lock(l) {
if (Inverted) {
if (Read) {
Lock.readUnlock();
} else {
Lock.writeUnlock();
}
} else {
if (Read) {
Lock.readLock();
} else {
Lock.writeLock();
}
}
}
~ScopedRWLockT() {
if (Inverted) {
if (Read) {
Lock.readLock();
} else {
Lock.writeLock();
}
} else {
if (Read) {
Lock.readUnlock();
} else {
Lock.writeUnlock();
}
}
}
private:
T &Lock;
};
class ReadWriteLock;
class StaticReadWriteLock;
/// A stack based object that unlocks the supplied ReadWriteLock on
/// construction and locks it for reading on destruction.
///
/// Precondition: ReadWriteLock unlocked by this thread, undefined otherwise.
typedef ScopedRWLockT<ReadWriteLock, true, false> ScopedReadLock;
typedef ScopedRWLockT<StaticReadWriteLock, true, false> StaticScopedReadLock;
/// A stack based object that unlocks the supplied ReadWriteLock on
/// construction and locks it for reading on destruction.
///
/// Precondition: ReadWriteLock unlocked by this thread, undefined
/// otherwise.
typedef ScopedRWLockT<ReadWriteLock, true, true> ScopedReadUnlock;
typedef ScopedRWLockT<StaticReadWriteLock, true, true> StaticScopedReadUnlock;
/// A stack based object that unlocks the supplied ReadWriteLock on
/// construction and locks it for reading on destruction.
///
/// Precondition: ReadWriteLock unlocked by this thread, undefined otherwise.
typedef ScopedRWLockT<ReadWriteLock, false, false> ScopedWriteLock;
typedef ScopedRWLockT<StaticReadWriteLock, false, false> StaticScopedWriteLock;
/// A stack based object that unlocks the supplied ReadWriteLock on
/// construction and locks it for writing on destruction.
///
/// Precondition: ReadWriteLock unlocked by this thread, undefined otherwise.
typedef ScopedRWLockT<ReadWriteLock, false, true> ScopedWriteUnlock;
typedef ScopedRWLockT<StaticReadWriteLock, false, true> StaticScopedWriteUnlock;
/// A Read / Write lock object that has semantics similar to `BasicLockable`
/// and `Lockable` C++ concepts however it supports multiple concurrent
/// threads holding the reader lock as long as the write lock isn't held and
/// only one thread can hold the write local at the same time.
///
/// If you need static allocated ReadWriteLock use StaticReadWriteLock.
///
/// See http://en.cppreference.com/w/cpp/concept/BasicLockable
/// See http://en.cppreference.com/w/cpp/concept/Lockable
///
/// This is NOT a recursive mutex.
class ReadWriteLock {
ReadWriteLock(const ReadWriteLock &) = delete;
ReadWriteLock &operator=(const ReadWriteLock &) = delete;
ReadWriteLock(ReadWriteLock &&) = delete;
ReadWriteLock &operator=(ReadWriteLock &&) = delete;
public:
ReadWriteLock() { ReadWriteLockPlatformHelper::init(Handle); }
~ReadWriteLock() { ReadWriteLockPlatformHelper::destroy(Handle); }
/// The readLock() method has the following properties:
/// - Behaves as an atomic operation.
/// - Blocks the calling thread while the write lock is held by another
/// thread and once the read lock is acquired by the calling thread
/// other threads are prevented from acquiring the write lock.
/// - Multiple threads can hold the read lock at the same time.
/// - Prior unlock() operations on the same lock synchronize-with
/// this lock operation.
/// - The behavior is undefined if the calling thread already owns
/// the read or write lock (likely a deadlock).
/// - Does not throw exceptions but will halt on error (fatalError).
///
/// Callers must not mutate the data protected by the ReadWriteLock while
/// holding the read lock, the write lock must be used.
void readLock() { ReadWriteLockPlatformHelper::readLock(Handle); }
/// The try_readLock() method has the following properties:
/// - Behaves as an atomic operation.
/// - Attempts to obtain the read lock without blocking the calling thread.
/// If ownership is not obtained, returns immediately. The function is
/// allowed to spuriously fail and return even if the lock is not
/// currently owned by another thread.
/// - If try_readLock() succeeds, prior unlock() operations on the same
/// object synchronize-with this operation. unlock() does not synchronize
/// with a failed try_readLock().
/// - The behavior is undefined if the calling thread already owns
/// the read or write lock (likely a deadlock)?
/// - Does not throw exceptions but will halt on error (fatalError).
///
/// Callers must not mutate the data protected by the ReadWriteLock while
/// holding the read lock, the write lock must be used.
bool try_readLock() {
return ReadWriteLockPlatformHelper::try_readLock(Handle);
}
/// The readUnlock() method has the following properties:
/// - Behaves as an atomic operation.
/// - Releases the calling thread's ownership of the read lock
/// and synchronizes-with the subsequent successful lock operations on
/// the same object.
/// - The behavior is undefined if the calling thread does not own
/// the read lock.
/// - Does not throw exceptions but will halt on error (fatalError).
void readUnlock() { ReadWriteLockPlatformHelper::readUnlock(Handle); }
/// The writeLock() method has the following properties:
/// - Behaves as an atomic operation.
/// - Blocks the calling thread while the write lock or a read lock is held
/// by another thread and once the write lock is acquired by the calling
/// thread other threads are prevented from acquiring the write lock or a
/// read lock.
/// - Only one thread can hold the write lock at the same time.
/// - Prior unlock() operations on the same lock synchronize-with
/// this lock operation.
/// - The behavior is undefined if the calling thread already owns
/// the read or write lock (likely a deadlock).
/// - Does not throw exceptions but will halt on error (fatalError).
void writeLock() { ReadWriteLockPlatformHelper::writeLock(Handle); }
/// The try_writeLock() method has the following properties:
/// - Behaves as an atomic operation.
/// - Attempts to obtain the write lock without blocking the calling thread.
/// If ownership is not obtained, returns immediately. The function is
/// allowed to spuriously fail and return even if the lock is not
/// currently owned by another thread.
/// - If try_writeLock() succeeds, prior unlock() operations on the same
/// object synchronize-with this operation. unlock() does not synchronize
/// with a failed try_writeLock().
/// - The behavior is undefined if the calling thread already owns
/// the read or write lock (likely a deadlock)?
/// - Does not throw exceptions but will halt on error (fatalError).
bool try_writeLock() {
return ReadWriteLockPlatformHelper::try_writeLock(Handle);
}
/// The writeUnlock() method has the following properties:
/// - Behaves as an atomic operation.
/// - Releases the calling thread's ownership of the write lock
/// and synchronizes-with the subsequent successful lock operations on
/// the same object.
/// - The behavior is undefined if the calling thread does not own
/// the write lock.
/// - Does not throw exceptions but will halt on error (fatalError).
void writeUnlock() { ReadWriteLockPlatformHelper::writeUnlock(Handle); }
/// Acquires read lock before calling the supplied critical section and
/// releases lock on return from critical section. Callers must not mutate
/// the data protected by the ReadWriteLock while holding the read lock, the
/// write lock must be used.
///
/// This call can block while waiting for the lock to become available.
///
/// For example the following reads the cached value while holding
/// the read lock.
///
/// ```
/// rw.withReadLock([&value] { value = cachedValue; });
/// ```
///
/// Precondition: ReadWriteLock not held by this thread, undefined otherwise.
template <typename CriticalSection>
auto withReadLock(CriticalSection criticalSection)
-> decltype(criticalSection()) {
ScopedReadLock guard(*this);
return criticalSection();
}
/// Acquires write lock before calling the supplied critical section and
/// releases lock on return from critical section.
///
/// This call can block while waiting for the lock to become available.
///
/// For example the following updates the cached value while holding
/// the write lock.
///
/// ```
/// rw.withWriteLock([&newValue] { cachedValue = newValue });
/// ```
///
/// Precondition: ReadWriteLock not held by this thread, undefined otherwise.
template <typename CriticalSection>
auto withWriteLock(CriticalSection criticalSection)
-> decltype(criticalSection()) {
ScopedWriteLock guard(*this);
return criticalSection();
}
private:
ReadWriteLockHandle Handle;
};
/// A static allocation variant of ConditionVariable.
///
/// Use ConditionVariable instead unless you need static allocation.
class StaticConditionVariable {
friend class StaticMutex;
StaticConditionVariable(const StaticConditionVariable &) = delete;
StaticConditionVariable &operator=(const StaticConditionVariable &) = delete;
StaticConditionVariable(StaticConditionVariable &&) = delete;
StaticConditionVariable &operator=(StaticConditionVariable &&) = delete;
public:
#if SWIFT_CONDITION_SUPPORTS_CONSTEXPR
constexpr
#endif
StaticConditionVariable()
: Handle(ConditionPlatformHelper::staticInit()) {
}
/// See ConditionVariable::notifyOne
void notifyOne() { ConditionPlatformHelper::notifyOne(Handle); }
/// See ConditionVariable::notifyAll
void notifyAll() { ConditionPlatformHelper::notifyAll(Handle); }
private:
ConditionHandle Handle;
};
using StaticScopedNotifyOne = ScopedNotifyOneT<StaticConditionVariable>;
using StaticScopedNotifyAll = ScopedNotifyAllT<StaticConditionVariable>;
/// A static allocation variant of Mutex.
///
/// Use Mutex instead unless you need static allocation.
class StaticMutex {
StaticMutex(const StaticMutex &) = delete;
StaticMutex &operator=(const StaticMutex &) = delete;
StaticMutex(StaticMutex &&) = delete;
StaticMutex &operator=(StaticMutex &&) = delete;
public:
#if SWIFT_MUTEX_SUPPORTS_CONSTEXPR
constexpr
#endif
StaticMutex()
: Handle(MutexPlatformHelper::staticInit()) {
}
/// See Mutex::lock
void lock() { MutexPlatformHelper::lock(Handle); }
/// See Mutex::unlock
void unlock() { MutexPlatformHelper::unlock(Handle); }
/// See Mutex::try_lock
bool try_lock() { return MutexPlatformHelper::try_lock(Handle); }
/// See Mutex::wait
void wait(StaticConditionVariable &condition) {
ConditionPlatformHelper::wait(condition.Handle, Handle);
}
/// See Mutex::lock
template <typename CriticalSection>
auto withLock(CriticalSection criticalSection) -> decltype(criticalSection()){
StaticScopedLock guard(*this);
return criticalSection();
}
/// See Mutex::withLockOrWait
template <typename CriticalSection>
void withLockOrWait(StaticConditionVariable &condition,
CriticalSection criticalSection) {
withLock([&] {
while (!criticalSection()) {
wait(condition);
}
});
}
/// See Mutex::withLockThenNotifyOne
template <typename CriticalSection>
auto withLockThenNotifyOne(StaticConditionVariable &condition,
CriticalSection criticalSection)
-> decltype(criticalSection()) {
return withLock([&] {
StaticScopedNotifyOne guard(condition);
return criticalSection();
});
}
/// See Mutex::withLockThenNotifyAll
template <typename CriticalSection>
auto withLockThenNotifyAll(StaticConditionVariable &condition,
CriticalSection criticalSection)
-> decltype(criticalSection()) {
return withLock([&] {
StaticScopedNotifyAll guard(condition);
return criticalSection();
});
}
private:
MutexHandle Handle;
};
/// A static allocation variant of ReadWriteLock.
///
/// Use ReadWriteLock instead unless you need static allocation.
class StaticReadWriteLock {
StaticReadWriteLock(const StaticReadWriteLock &) = delete;
StaticReadWriteLock &operator=(const StaticReadWriteLock &) = delete;
StaticReadWriteLock(StaticReadWriteLock &&) = delete;
StaticReadWriteLock &operator=(StaticReadWriteLock &&) = delete;
public:
#if SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR
constexpr
#endif
StaticReadWriteLock()
: Handle(ReadWriteLockPlatformHelper::staticInit()) {
}
/// See ReadWriteLock::readLock
void readLock() { ReadWriteLockPlatformHelper::readLock(Handle); }
/// See ReadWriteLock::try_readLock
bool try_readLock() {
return ReadWriteLockPlatformHelper::try_readLock(Handle);
}
/// See ReadWriteLock::readUnlock
void readUnlock() { ReadWriteLockPlatformHelper::readUnlock(Handle); }
/// See ReadWriteLock::writeLock
void writeLock() { ReadWriteLockPlatformHelper::writeLock(Handle); }
/// See ReadWriteLock::try_writeLock
bool try_writeLock() {
return ReadWriteLockPlatformHelper::try_writeLock(Handle);
}
/// See ReadWriteLock::writeUnlock
void writeUnlock() { ReadWriteLockPlatformHelper::writeUnlock(Handle); }
/// See ReadWriteLock::withReadLock
template <typename CriticalSection>
auto withReadLock(CriticalSection criticalSection)
-> decltype(criticalSection()) {
StaticScopedReadLock guard(*this);
return criticalSection();
}
/// See ReadWriteLock::withWriteLock
template <typename CriticalSection>
auto withWriteLock(CriticalSection criticalSection)
-> decltype(criticalSection()) {
StaticScopedWriteLock guard(*this);
return criticalSection();
}
private:
ReadWriteLockHandle Handle;
};
/// A Mutex object that supports `BasicLockable` C++ concepts. It is
/// considered
/// unsafe to use because it doesn't do any error checking. It is only for
/// use in pathways that deal with reporting fatalErrors to avoid the
/// potential
/// for recursive fatalErrors that could happen if you used Mutex.
///
/// Always use Mutex, unless in the above mentioned error pathway situation.
class StaticUnsafeMutex {
StaticUnsafeMutex(const StaticUnsafeMutex &) = delete;
StaticUnsafeMutex &operator=(const StaticUnsafeMutex &) = delete;
StaticUnsafeMutex(StaticUnsafeMutex &&) = delete;
StaticUnsafeMutex &operator=(StaticUnsafeMutex &&) = delete;
public:
#if SWIFT_MUTEX_SUPPORTS_CONSTEXPR
constexpr
#endif
StaticUnsafeMutex()
: Handle(MutexPlatformHelper::staticInit()) {
}
/// The lock() method has the following properties:
/// - Behaves as an atomic operation.
/// - Blocks the calling thread until exclusive ownership of the mutex
/// can be obtained.
/// - Prior m.unlock() operations on the same mutex synchronize-with
/// this lock operation.
/// - The behavior is undefined if the calling thread already owns
/// the mutex (likely a deadlock).
/// - Ignores errors that may happen, undefined when an error happens.
void lock() { MutexPlatformHelper::unsafeLock(Handle); }
/// The unlock() method has the following properties:
/// - Behaves as an atomic operation.
/// - Releases the calling thread's ownership of the mutex and
/// synchronizes-with the subsequent successful lock operations on
/// the same object.
/// - The behavior is undefined if the calling thread does not own
/// the mutex.
/// - Ignores errors that may happen, undefined when an error happens.
void unlock() { MutexPlatformHelper::unsafeUnlock(Handle); }
private:
MutexHandle Handle;
};
// Enforce literal requirements for static variants.
#if SWIFT_MUTEX_SUPPORTS_CONSTEXPR
static_assert(std::is_literal_type<StaticMutex>::value,
"StaticMutex must be literal type");
static_assert(std::is_literal_type<StaticUnsafeMutex>::value,
"StaticUnsafeMutex must be literal type");
#else
// Your platform doesn't currently support statically allocated Mutex
// you will possibly see global-constructors warnings
#endif
#if SWIFT_CONDITION_SUPPORTS_CONSTEXPR
static_assert(std::is_literal_type<StaticConditionVariable>::value,
"StaticConditionVariable must be literal type");
#else
// Your platform doesn't currently support statically allocated ConditionVar
// you will possibly see global-constructors warnings
#endif
#if SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR
static_assert(std::is_literal_type<StaticReadWriteLock>::value,
"StaticReadWriteLock must be literal type");
#else
// Your platform doesn't currently support statically allocated ReadWriteLocks
// you will possibly see global-constructors warnings
#endif
}
#endif