Files
swift-mirror/include/swift/Runtime/Mutex.h
3405691582 5847726f51 Preliminary support for OpenBSD in the stdlib.
These should hopefully all be uncontroversial, minimal changes to deal
with progressing the build to completion on OpenBSD or addressing minor
portability issues. This is not the full set of changes to get a
successful build; other portability issues will be addressed in future
commits.

Most of this is just adding the relevant clauses to the ifdefs, but of
note in this commit:

* StdlibUnittest.swift: the default conditional in _getOSVersion assumes
  an Apple platform, therefore the explicit conditional and the relevant
  enums need filling out. The default conditional should be #error, but
  we'll fix this in a different commit.

* tgmath.swift.gyb: inexplicably, OpenBSD is missing just lgammal_r.
  Tests are updated correspondingly.

* ThreadLocalStorage.h: we use the pthread implementation, so it
  seems we should typedef __swift_thread_key_t as pthread_key_t.
  However, that's also a tweak for another commit.
2020-03-01 12:50:06 -05:00

794 lines
28 KiB
C++

//===--- 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(__OpenBSD__) || 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