mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[Runtime] Remove all use of read/write locks.
Read/write locks are not as good as you'd think; a simple mutex is better in almost all cases. rdar://90776105
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
//===--- Mutex.h - Mutex and ReadWriteLock ----------------------*- C++ -*-===//
|
//===--- Mutex.h - Mutex ----------------------------------------*- C++ -*-===//
|
||||||
//
|
//
|
||||||
// This source file is part of the Swift.org open source project
|
// This source file is part of the Swift.org open source project
|
||||||
//
|
//
|
||||||
@@ -10,14 +10,18 @@
|
|||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
//
|
//
|
||||||
// Mutex, ReadWriteLock, and Scoped lock abstractions for use in
|
// Mutex and Scoped lock abstractions for use in Swift runtime.
|
||||||
// Swift runtime.
|
|
||||||
//
|
//
|
||||||
// We intentionally do not provide a condition-variable abstraction.
|
// We intentionally do not provide a condition-variable abstraction.
|
||||||
// Traditional condition-variable interfaces are subject to unavoidable
|
// Traditional condition-variable interfaces are subject to unavoidable
|
||||||
// priority inversions, as well as making poor use of threads.
|
// priority inversions, as well as making poor use of threads.
|
||||||
// Prefer AtomicWaitQueue.
|
// Prefer AtomicWaitQueue.
|
||||||
//
|
//
|
||||||
|
// We also intentionally avoid read/write locks. It's difficult to implement a
|
||||||
|
// performant and fair read/write lock, and indeed many common implementations
|
||||||
|
// rely on condition variables, which, again, are subject to unavoidable
|
||||||
|
// priority inversions.
|
||||||
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#ifndef SWIFT_RUNTIME_MUTEX_H
|
#ifndef SWIFT_RUNTIME_MUTEX_H
|
||||||
@@ -167,238 +171,6 @@ private:
|
|||||||
MutexHandle Handle;
|
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(std::forward<CriticalSection>(criticalSection)()) {
|
|
||||||
ScopedReadLock guard(*this);
|
|
||||||
return std::forward<CriticalSection>(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(std::forward<CriticalSection>(criticalSection)()) {
|
|
||||||
ScopedWriteLock guard(*this);
|
|
||||||
return std::forward<CriticalSection>(criticalSection)();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ReadWriteLockHandle Handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A static allocation variant of Mutex.
|
/// A static allocation variant of Mutex.
|
||||||
///
|
///
|
||||||
/// Use Mutex instead unless you need static allocation.
|
/// Use Mutex instead unless you need static allocation.
|
||||||
@@ -450,66 +222,6 @@ private:
|
|||||||
MutexHandle Handle;
|
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(std::forward<CriticalSection>(criticalSection)()) {
|
|
||||||
StaticScopedReadLock guard(*this);
|
|
||||||
return std::forward<CriticalSection>(criticalSection)();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See ReadWriteLock::withWriteLock
|
|
||||||
template <typename CriticalSection>
|
|
||||||
auto withWriteLock(CriticalSection &&criticalSection)
|
|
||||||
-> decltype(std::forward<CriticalSection>(criticalSection)()) {
|
|
||||||
StaticScopedWriteLock guard(*this);
|
|
||||||
return std::forward<CriticalSection>(criticalSection)();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ReadWriteLockHandle Handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A Mutex object that supports `BasicLockable` C++ concepts. It is
|
/// A Mutex object that supports `BasicLockable` C++ concepts. It is
|
||||||
/// considered
|
/// considered
|
||||||
/// unsafe to use because it doesn't do any error checking. It is only for
|
/// unsafe to use because it doesn't do any error checking. It is only for
|
||||||
@@ -634,13 +346,6 @@ static_assert(std::is_literal_type<StaticConditionVariable>::value,
|
|||||||
// you will possibly see global-constructors warnings
|
// you will possibly see global-constructors warnings
|
||||||
#endif
|
#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
|
#endif
|
||||||
|
|||||||
@@ -34,11 +34,9 @@ namespace c11threads {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef c11threads::rwlock *ReadWriteLockHandle;
|
|
||||||
typedef ::mtx_t MutexHandle;
|
typedef ::mtx_t MutexHandle;
|
||||||
|
|
||||||
#define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 0
|
#define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 0
|
||||||
#define SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR 0
|
|
||||||
|
|
||||||
/// C11 low-level implementation that supports Mutex
|
/// C11 low-level implementation that supports Mutex
|
||||||
/// found in Mutex.h
|
/// found in Mutex.h
|
||||||
@@ -86,77 +84,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// C11 low-level implementation that supports ReadWriteLock
|
|
||||||
/// found in Mutex.h
|
|
||||||
///
|
|
||||||
/// See ReadWriteLock
|
|
||||||
namespace c11threads {
|
|
||||||
|
|
||||||
class rwlock {
|
|
||||||
private:
|
|
||||||
unsigned activeReaders_;
|
|
||||||
unsigned waitingWriters_;
|
|
||||||
bool writerActive_;
|
|
||||||
::cnd_t cond_;
|
|
||||||
::mtx_t mutex_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
rwlock();
|
|
||||||
~rwlock();
|
|
||||||
|
|
||||||
rwlock(const rwlock &other) = delete;
|
|
||||||
rwlock &operator=(const rwlock &other) = delete;
|
|
||||||
|
|
||||||
void readLock();
|
|
||||||
bool try_readLock();
|
|
||||||
void readUnlock();
|
|
||||||
void writeLock();
|
|
||||||
bool try_writeLock();
|
|
||||||
void writeUnlock();
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReadWriteLockPlatformHelper {
|
|
||||||
public:
|
|
||||||
static ReadWriteLockHandle staticInit() {
|
|
||||||
return new c11threads::rwlock();
|
|
||||||
};
|
|
||||||
|
|
||||||
static void init(ReadWriteLockHandle &rwlock) {
|
|
||||||
rwlock = new c11threads::rwlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroy(ReadWriteLockHandle &rwlock) {
|
|
||||||
delete rwlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void readLock(ReadWriteLockHandle &rwlock) {
|
|
||||||
rwlock->readLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool try_readLock(ReadWriteLockHandle &rwlock) {
|
|
||||||
rwlock->try_readLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void readUnlock(ReadWriteLockHandle &rwlock) {
|
|
||||||
rwlock->readUnlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void writeLock(ReadWriteLockHandle &rwlock) {
|
|
||||||
rwlock->writeLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool try_writeLock(ReadWriteLockHandle &rwlock) {
|
|
||||||
rwlock->try_writeLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void writeUnlock(ReadWriteLockHandle &rwlock) {
|
|
||||||
rwlock->writeUnlock();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -27,8 +27,6 @@
|
|||||||
|
|
||||||
namespace swift {
|
namespace swift {
|
||||||
|
|
||||||
typedef pthread_rwlock_t ReadWriteLockHandle;
|
|
||||||
|
|
||||||
#if HAS_OS_UNFAIR_LOCK
|
#if HAS_OS_UNFAIR_LOCK
|
||||||
typedef os_unfair_lock MutexHandle;
|
typedef os_unfair_lock MutexHandle;
|
||||||
#else
|
#else
|
||||||
@@ -41,10 +39,8 @@ typedef pthread_mutex_t MutexHandle;
|
|||||||
// results in a reinterpret_cast which violates constexpr.
|
// results in a reinterpret_cast which violates constexpr.
|
||||||
// WASI currently doesn't support threading/locking at all.
|
// WASI currently doesn't support threading/locking at all.
|
||||||
#define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 0
|
#define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 0
|
||||||
#define SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR 0
|
|
||||||
#else
|
#else
|
||||||
#define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 1
|
#define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 1
|
||||||
#define SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR 1
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// PThread low-level implementation that supports Mutex
|
/// PThread low-level implementation that supports Mutex
|
||||||
@@ -114,30 +110,6 @@ inline bool MutexPlatformHelper::try_lock(os_unfair_lock &lock) {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// PThread low-level implementation that supports ReadWriteLock
|
|
||||||
/// found in Mutex.h
|
|
||||||
///
|
|
||||||
/// See ReadWriteLock
|
|
||||||
struct ReadWriteLockPlatformHelper {
|
|
||||||
#if SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR
|
|
||||||
static constexpr
|
|
||||||
#else
|
|
||||||
static
|
|
||||||
#endif
|
|
||||||
ReadWriteLockHandle
|
|
||||||
staticInit() {
|
|
||||||
return PTHREAD_RWLOCK_INITIALIZER;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void init(ReadWriteLockHandle &rwlock);
|
|
||||||
static void destroy(ReadWriteLockHandle &rwlock);
|
|
||||||
static void readLock(ReadWriteLockHandle &rwlock);
|
|
||||||
static bool try_readLock(ReadWriteLockHandle &rwlock);
|
|
||||||
static void readUnlock(ReadWriteLockHandle &rwlock);
|
|
||||||
static void writeLock(ReadWriteLockHandle &rwlock);
|
|
||||||
static bool try_writeLock(ReadWriteLockHandle &rwlock);
|
|
||||||
static void writeUnlock(ReadWriteLockHandle &rwlock);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -22,10 +22,8 @@
|
|||||||
namespace swift {
|
namespace swift {
|
||||||
|
|
||||||
typedef void* MutexHandle;
|
typedef void* MutexHandle;
|
||||||
typedef void* ReadWriteLockHandle;
|
|
||||||
|
|
||||||
#define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 1
|
#define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 1
|
||||||
#define SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR 1
|
|
||||||
|
|
||||||
struct MutexPlatformHelper {
|
struct MutexPlatformHelper {
|
||||||
static constexpr MutexHandle staticInit() { return nullptr; }
|
static constexpr MutexHandle staticInit() { return nullptr; }
|
||||||
@@ -38,17 +36,6 @@ struct MutexPlatformHelper {
|
|||||||
static void unsafeUnlock(MutexHandle &mutex) {}
|
static void unsafeUnlock(MutexHandle &mutex) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ReadWriteLockPlatformHelper {
|
|
||||||
static constexpr ReadWriteLockHandle staticInit() { return nullptr; }
|
|
||||||
static void init(ReadWriteLockHandle &rwlock) {}
|
|
||||||
static void destroy(ReadWriteLockHandle &rwlock) {}
|
|
||||||
static void readLock(ReadWriteLockHandle &rwlock) {}
|
|
||||||
static bool try_readLock(ReadWriteLockHandle &rwlock) { return true; }
|
|
||||||
static void readUnlock(ReadWriteLockHandle &rwlock) {}
|
|
||||||
static void writeLock(ReadWriteLockHandle &rwlock) {}
|
|
||||||
static bool try_writeLock(ReadWriteLockHandle &rwlock) { return true; }
|
|
||||||
static void writeUnlock(ReadWriteLockHandle &rwlock) {}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -25,10 +25,8 @@
|
|||||||
namespace swift {
|
namespace swift {
|
||||||
|
|
||||||
typedef SRWLOCK MutexHandle;
|
typedef SRWLOCK MutexHandle;
|
||||||
typedef SRWLOCK ReadWriteLockHandle;
|
|
||||||
|
|
||||||
#define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 1
|
#define SWIFT_MUTEX_SUPPORTS_CONSTEXPR 1
|
||||||
#define SWIFT_READWRITELOCK_SUPPORTS_CONSTEXPR 1
|
|
||||||
|
|
||||||
struct MutexPlatformHelper {
|
struct MutexPlatformHelper {
|
||||||
static constexpr MutexHandle staticInit() { return SRWLOCK_INIT; }
|
static constexpr MutexHandle staticInit() { return SRWLOCK_INIT; }
|
||||||
@@ -50,29 +48,4 @@ struct MutexPlatformHelper {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ReadWriteLockPlatformHelper {
|
|
||||||
static constexpr ReadWriteLockHandle staticInit() { return SRWLOCK_INIT; }
|
|
||||||
static void init(ReadWriteLockHandle &rwlock) { InitializeSRWLock(&rwlock); }
|
|
||||||
static void destroy(ReadWriteLockHandle &rwlock) {}
|
|
||||||
static void readLock(ReadWriteLockHandle &rwlock) {
|
|
||||||
AcquireSRWLockShared(&rwlock);
|
|
||||||
}
|
|
||||||
static bool try_readLock(ReadWriteLockHandle &rwlock) {
|
|
||||||
return TryAcquireSRWLockShared(&rwlock) != 0;
|
|
||||||
}
|
|
||||||
static void readUnlock(ReadWriteLockHandle &rwlock) {
|
|
||||||
ReleaseSRWLockShared(&rwlock);
|
|
||||||
}
|
|
||||||
static void writeLock(ReadWriteLockHandle &rwlock) {
|
|
||||||
AcquireSRWLockExclusive(&rwlock);
|
|
||||||
}
|
|
||||||
static bool try_writeLock(ReadWriteLockHandle &rwlock) {
|
|
||||||
return TryAcquireSRWLockExclusive(&rwlock) != 0;
|
|
||||||
}
|
|
||||||
static void writeUnlock(ReadWriteLockHandle &rwlock) {
|
|
||||||
ReleaseSRWLockExclusive(&rwlock);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -145,8 +145,8 @@ enum class TypeNameKind {
|
|||||||
using TypeNameCacheKey = llvm::PointerIntPair<const Metadata *, 2, TypeNameKind>;
|
using TypeNameCacheKey = llvm::PointerIntPair<const Metadata *, 2, TypeNameKind>;
|
||||||
|
|
||||||
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
||||||
static StaticReadWriteLock TypeNameCacheLock;
|
static StaticMutex TypeNameCacheLock;
|
||||||
static StaticReadWriteLock MangledToPrettyFunctionNameCacheLock;
|
static StaticMutex MangledToPrettyFunctionNameCacheLock;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Cache containing rendered names for Metadata.
|
/// Cache containing rendered names for Metadata.
|
||||||
@@ -167,7 +167,7 @@ swift::swift_getTypeName(const Metadata *type, bool qualified) {
|
|||||||
// Attempt read-only lookup of cache entry.
|
// Attempt read-only lookup of cache entry.
|
||||||
{
|
{
|
||||||
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
||||||
StaticScopedReadLock guard(TypeNameCacheLock);
|
StaticMutex::ScopedLock guard(TypeNameCacheLock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto found = cache.find(key);
|
auto found = cache.find(key);
|
||||||
@@ -180,7 +180,7 @@ swift::swift_getTypeName(const Metadata *type, bool qualified) {
|
|||||||
// Read-only lookup failed to find item, we may need to create it.
|
// Read-only lookup failed to find item, we may need to create it.
|
||||||
{
|
{
|
||||||
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
||||||
StaticScopedWriteLock guard(TypeNameCacheLock);
|
StaticMutex::ScopedLock guard(TypeNameCacheLock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Do lookup again just to make sure it wasn't created by another
|
// Do lookup again just to make sure it wasn't created by another
|
||||||
@@ -213,7 +213,7 @@ swift::swift_getMangledTypeName(const Metadata *type) {
|
|||||||
// Attempt read-only lookup of cache entry.
|
// Attempt read-only lookup of cache entry.
|
||||||
{
|
{
|
||||||
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
||||||
StaticScopedReadLock guard(TypeNameCacheLock);
|
StaticMutex::ScopedLock guard(TypeNameCacheLock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto found = cache.find(key);
|
auto found = cache.find(key);
|
||||||
@@ -226,7 +226,7 @@ swift::swift_getMangledTypeName(const Metadata *type) {
|
|||||||
// Read-only cache lookup failed, we may need to create it.
|
// Read-only cache lookup failed, we may need to create it.
|
||||||
{
|
{
|
||||||
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
||||||
StaticScopedWriteLock guard(TypeNameCacheLock);
|
StaticMutex::ScopedLock guard(TypeNameCacheLock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Do lookup again just to make sure it wasn't created by another
|
// Do lookup again just to make sure it wasn't created by another
|
||||||
@@ -271,7 +271,7 @@ swift::swift_getFunctionFullNameFromMangledName(
|
|||||||
// Attempt read-only lookup of cache entry.
|
// Attempt read-only lookup of cache entry.
|
||||||
{
|
{
|
||||||
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
||||||
StaticScopedReadLock guard(MangledToPrettyFunctionNameCacheLock);
|
StaticMutex::ScopedLock guard(MangledToPrettyFunctionNameCacheLock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto found = cache.find(mangledName);
|
auto found = cache.find(mangledName);
|
||||||
@@ -388,7 +388,7 @@ swift::swift_getFunctionFullNameFromMangledName(
|
|||||||
|
|
||||||
{
|
{
|
||||||
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
#if SWIFT_CASTING_SUPPORTS_MUTEX
|
||||||
StaticScopedWriteLock guard(MangledToPrettyFunctionNameCacheLock);
|
StaticMutex::ScopedLock guard(MangledToPrettyFunctionNameCacheLock);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cache.insert({mangledName, {result, size}});
|
cache.insert({mangledName, {result, size}});
|
||||||
|
|||||||
@@ -21,24 +21,6 @@
|
|||||||
|
|
||||||
using namespace swift;
|
using namespace swift;
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// A simple scoped locker class for a C11 mutex
|
|
||||||
class locker {
|
|
||||||
private:
|
|
||||||
mtx_t &mutex_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit locker(mtx_t &mutex) : mutex_(mutex) {
|
|
||||||
c11threads::handleError(mtx_lock(&mutex_));
|
|
||||||
}
|
|
||||||
~locker() {
|
|
||||||
c11threads::handleError(mtx_unlock(&mutex_));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace c11threads {
|
namespace c11threads {
|
||||||
|
|
||||||
#ifndef SWIFT_FATAL_ERROR
|
#ifndef SWIFT_FATAL_ERROR
|
||||||
@@ -50,61 +32,6 @@ void fatalError(int errcode) {
|
|||||||
SWIFT_FATAL_ERROR(0, "C11 threads call failed with %d\n", errcode);
|
SWIFT_FATAL_ERROR(0, "C11 threads call failed with %d\n", errcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A simple reader/writer lock implementation, with writer priority
|
|
||||||
rwlock::rwlock() : activeReaders_(0), waitingWriters_(0), writerActive_(false) {
|
|
||||||
handleError(::cnd_init(&cond_));
|
|
||||||
handleError(::mtx_init(&mutex_, ::mtx_plain));
|
|
||||||
}
|
|
||||||
|
|
||||||
rwlock::~rwlock() {
|
|
||||||
::cnd_destroy(&cond_);
|
|
||||||
::mtx_destroy(&mutex_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rwlock::readLock() {
|
|
||||||
locker l(mutex_);
|
|
||||||
while (waitingWriters_ || writerActive_)
|
|
||||||
handleError(::cnd_wait(&cond_, &mutex_));
|
|
||||||
++activeReaders_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rwlock::try_readLock() {
|
|
||||||
locker l(mutex_);
|
|
||||||
if (waitingWriters_ || writerActive_)
|
|
||||||
return false;
|
|
||||||
++activeReaders_;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void rwlock::readUnlock() {
|
|
||||||
locker l(mutex_);
|
|
||||||
if (!--activeReaders_)
|
|
||||||
handleError(::cnd_broadcast(&cond_));
|
|
||||||
}
|
|
||||||
|
|
||||||
void rwlock::writeLock() {
|
|
||||||
locker l(mutex_);
|
|
||||||
++waitingWriters_;
|
|
||||||
while (activeReaders_ || writerActive_)
|
|
||||||
handleError(::cnd_wait(&cond_, mutex_));
|
|
||||||
--waitingWriters_;
|
|
||||||
writerActive_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rwlock::try_writeLock() {
|
|
||||||
locker l(mutex_);
|
|
||||||
if (activeReaders_ || writerActive_)
|
|
||||||
return false;
|
|
||||||
writerActive_ = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void rwlock::writeUnlock() {
|
|
||||||
locker l(mutex_);
|
|
||||||
writerActive_ = false;
|
|
||||||
handleError(::cnd_broadcast(&cond_));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // SWIFT_STDLIB_THREADING_C11
|
#endif // SWIFT_STDLIB_THREADING_C11
|
||||||
|
|||||||
@@ -106,38 +106,4 @@ bool MutexPlatformHelper::try_lock(pthread_mutex_t &mutex) {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void ReadWriteLockPlatformHelper::init(pthread_rwlock_t &rwlock) {
|
|
||||||
reportError(pthread_rwlock_init(&rwlock, nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadWriteLockPlatformHelper::destroy(pthread_rwlock_t &rwlock) {
|
|
||||||
reportError(pthread_rwlock_destroy(&rwlock));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadWriteLockPlatformHelper::readLock(pthread_rwlock_t &rwlock) {
|
|
||||||
reportError(pthread_rwlock_rdlock(&rwlock));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReadWriteLockPlatformHelper::try_readLock(pthread_rwlock_t &rwlock) {
|
|
||||||
returnTrueOrReportError(pthread_rwlock_tryrdlock(&rwlock),
|
|
||||||
/* returnFalseOnEBUSY = */ true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadWriteLockPlatformHelper::writeLock(pthread_rwlock_t &rwlock) {
|
|
||||||
reportError(pthread_rwlock_wrlock(&rwlock));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReadWriteLockPlatformHelper::try_writeLock(pthread_rwlock_t &rwlock) {
|
|
||||||
returnTrueOrReportError(pthread_rwlock_trywrlock(&rwlock),
|
|
||||||
/* returnFalseOnEBUSY = */ true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadWriteLockPlatformHelper::readUnlock(pthread_rwlock_t &rwlock) {
|
|
||||||
reportError(pthread_rwlock_unlock(&rwlock));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadWriteLockPlatformHelper::writeUnlock(pthread_rwlock_t &rwlock) {
|
|
||||||
reportError(pthread_rwlock_unlock(&rwlock));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // SWIFT_STDLIB_THREADING_PTHREADS
|
#endif // SWIFT_STDLIB_THREADING_PTHREADS
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ static bool UpdateGlobalRuntimeFunctionCounters = false;
|
|||||||
/// Global set of counters tracking the total number of runtime invocations.
|
/// Global set of counters tracking the total number of runtime invocations.
|
||||||
struct RuntimeFunctionCountersStateSentinel {
|
struct RuntimeFunctionCountersStateSentinel {
|
||||||
RuntimeFunctionCountersState State;
|
RuntimeFunctionCountersState State;
|
||||||
StaticReadWriteLock Lock;
|
StaticMutex Lock;
|
||||||
};
|
};
|
||||||
static RuntimeFunctionCountersStateSentinel RuntimeGlobalFunctionCountersState;
|
static RuntimeFunctionCountersStateSentinel RuntimeGlobalFunctionCountersState;
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ static RuntimeFunctionCountersStateSentinel RuntimeGlobalFunctionCountersState;
|
|||||||
/// them.
|
/// them.
|
||||||
struct RuntimeObjectCacheSentinel {
|
struct RuntimeObjectCacheSentinel {
|
||||||
llvm::DenseMap<HeapObject *, RuntimeFunctionCountersState> Cache;
|
llvm::DenseMap<HeapObject *, RuntimeFunctionCountersState> Cache;
|
||||||
StaticReadWriteLock Lock;
|
StaticMutex Lock;
|
||||||
};
|
};
|
||||||
static Lazy<RuntimeObjectCacheSentinel> RuntimeObjectStateCache;
|
static Lazy<RuntimeObjectCacheSentinel> RuntimeObjectStateCache;
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ static uint16_t RuntimeFunctionCountersOffsets[] = {
|
|||||||
void SWIFT_RT_TRACK_INVOCATION_NAME(RT_FUNCTION)(HeapObject * object) { \
|
void SWIFT_RT_TRACK_INVOCATION_NAME(RT_FUNCTION)(HeapObject * object) { \
|
||||||
/* Update global counters. */ \
|
/* Update global counters. */ \
|
||||||
if (UpdateGlobalRuntimeFunctionCounters) { \
|
if (UpdateGlobalRuntimeFunctionCounters) { \
|
||||||
StaticScopedWriteLock lock(RuntimeGlobalFunctionCountersState.Lock); \
|
StaticMutex::ScopedLock lock(RuntimeGlobalFunctionCountersState.Lock); \
|
||||||
RuntimeGlobalFunctionCountersState.State \
|
RuntimeGlobalFunctionCountersState.State \
|
||||||
.SWIFT_RT_FUNCTION_INVOCATION_COUNTER_NAME(RT_FUNCTION)++; \
|
.SWIFT_RT_FUNCTION_INVOCATION_COUNTER_NAME(RT_FUNCTION)++; \
|
||||||
if (GlobalRuntimeFunctionCountersUpdateHandler) { \
|
if (GlobalRuntimeFunctionCountersUpdateHandler) { \
|
||||||
@@ -117,7 +117,7 @@ static uint16_t RuntimeFunctionCountersOffsets[] = {
|
|||||||
/* Update per object counters. */ \
|
/* Update per object counters. */ \
|
||||||
if (UpdatePerObjectRuntimeFunctionCounters && object) { \
|
if (UpdatePerObjectRuntimeFunctionCounters && object) { \
|
||||||
auto &theSentinel = RuntimeObjectStateCache.get(); \
|
auto &theSentinel = RuntimeObjectStateCache.get(); \
|
||||||
StaticScopedWriteLock lock(theSentinel.Lock); \
|
StaticMutex::ScopedLock lock(theSentinel.Lock); \
|
||||||
theSentinel.Cache[object].SWIFT_RT_FUNCTION_INVOCATION_COUNTER_NAME( \
|
theSentinel.Cache[object].SWIFT_RT_FUNCTION_INVOCATION_COUNTER_NAME( \
|
||||||
RT_FUNCTION)++; \
|
RT_FUNCTION)++; \
|
||||||
/* TODO: Remember the order/history of operations? */ \
|
/* TODO: Remember the order/history of operations? */ \
|
||||||
@@ -131,7 +131,7 @@ static uint16_t RuntimeFunctionCountersOffsets[] = {
|
|||||||
void _swift_getObjectRuntimeFunctionCounters(
|
void _swift_getObjectRuntimeFunctionCounters(
|
||||||
HeapObject *object, RuntimeFunctionCountersState *result) {
|
HeapObject *object, RuntimeFunctionCountersState *result) {
|
||||||
auto &theSentinel = RuntimeObjectStateCache.get();
|
auto &theSentinel = RuntimeObjectStateCache.get();
|
||||||
StaticScopedReadLock lock(theSentinel.Lock);
|
StaticMutex::ScopedLock lock(theSentinel.Lock);
|
||||||
*result = theSentinel.Cache[object];
|
*result = theSentinel.Cache[object];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ void _swift_getObjectRuntimeFunctionCounters(
|
|||||||
void _swift_setObjectRuntimeFunctionCounters(
|
void _swift_setObjectRuntimeFunctionCounters(
|
||||||
HeapObject *object, RuntimeFunctionCountersState *state) {
|
HeapObject *object, RuntimeFunctionCountersState *state) {
|
||||||
auto &theSentinel = RuntimeObjectStateCache.get();
|
auto &theSentinel = RuntimeObjectStateCache.get();
|
||||||
StaticScopedWriteLock lock(theSentinel.Lock);
|
StaticMutex::ScopedLock lock(theSentinel.Lock);
|
||||||
theSentinel.Cache[object] = *state;
|
theSentinel.Cache[object] = *state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,14 +148,14 @@ void _swift_setObjectRuntimeFunctionCounters(
|
|||||||
/// each runtime function of interest.
|
/// each runtime function of interest.
|
||||||
void _swift_getGlobalRuntimeFunctionCounters(
|
void _swift_getGlobalRuntimeFunctionCounters(
|
||||||
RuntimeFunctionCountersState *result) {
|
RuntimeFunctionCountersState *result) {
|
||||||
StaticScopedReadLock lock(RuntimeGlobalFunctionCountersState.Lock);
|
StaticMutex::ScopedLock lock(RuntimeGlobalFunctionCountersState.Lock);
|
||||||
*result = RuntimeGlobalFunctionCountersState.State;
|
*result = RuntimeGlobalFunctionCountersState.State;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the global runtime state of function pointers from a provided state.
|
/// Set the global runtime state of function pointers from a provided state.
|
||||||
void _swift_setGlobalRuntimeFunctionCounters(
|
void _swift_setGlobalRuntimeFunctionCounters(
|
||||||
RuntimeFunctionCountersState *state) {
|
RuntimeFunctionCountersState *state) {
|
||||||
StaticScopedWriteLock lock(RuntimeGlobalFunctionCountersState.Lock);
|
StaticMutex::ScopedLock lock(RuntimeGlobalFunctionCountersState.Lock);
|
||||||
RuntimeGlobalFunctionCountersState.State = *state;
|
RuntimeGlobalFunctionCountersState.State = *state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ static void _swift_dumpRuntimeCounters(RuntimeFunctionCountersState *State) {
|
|||||||
/// Dump all per-object runtime function pointers.
|
/// Dump all per-object runtime function pointers.
|
||||||
void _swift_dumpObjectsRuntimeFunctionPointers() {
|
void _swift_dumpObjectsRuntimeFunctionPointers() {
|
||||||
auto &theSentinel = RuntimeObjectStateCache.get();
|
auto &theSentinel = RuntimeObjectStateCache.get();
|
||||||
StaticScopedReadLock lock(theSentinel.Lock);
|
StaticMutex::ScopedLock lock(theSentinel.Lock);
|
||||||
for (auto &Pair : theSentinel.Cache) {
|
for (auto &Pair : theSentinel.Cache) {
|
||||||
printf("\n\nRuntime counters for object at address %p:\n", Pair.getFirst());
|
printf("\n\nRuntime counters for object at address %p:\n", Pair.getFirst());
|
||||||
_swift_dumpRuntimeCounters(&Pair.getSecond());
|
_swift_dumpRuntimeCounters(&Pair.getSecond());
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace {
|
|||||||
|
|
||||||
struct SwiftNullSentinelCache {
|
struct SwiftNullSentinelCache {
|
||||||
std::vector<id> Cache;
|
std::vector<id> Cache;
|
||||||
StaticReadWriteLock Lock;
|
StaticMutex Lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Lazy<SwiftNullSentinelCache> Sentinels;
|
static Lazy<SwiftNullSentinelCache> Sentinels;
|
||||||
@@ -73,7 +73,7 @@ static id getSentinelForDepth(unsigned depth) {
|
|||||||
auto &theSentinels = Sentinels.get();
|
auto &theSentinels = Sentinels.get();
|
||||||
unsigned depthIndex = depth - 2;
|
unsigned depthIndex = depth - 2;
|
||||||
{
|
{
|
||||||
StaticScopedReadLock lock(theSentinels.Lock);
|
StaticMutex::ScopedLock lock(theSentinels.Lock);
|
||||||
const auto &cache = theSentinels.Cache;
|
const auto &cache = theSentinels.Cache;
|
||||||
if (depthIndex < cache.size()) {
|
if (depthIndex < cache.size()) {
|
||||||
id cached = cache[depthIndex];
|
id cached = cache[depthIndex];
|
||||||
@@ -83,7 +83,7 @@ static id getSentinelForDepth(unsigned depth) {
|
|||||||
}
|
}
|
||||||
// Make one if we need to.
|
// Make one if we need to.
|
||||||
{
|
{
|
||||||
StaticScopedWriteLock lock(theSentinels.Lock);
|
StaticMutex::ScopedLock lock(theSentinels.Lock);
|
||||||
if (depthIndex >= theSentinels.Cache.size())
|
if (depthIndex >= theSentinels.Cache.size())
|
||||||
theSentinels.Cache.resize(depthIndex + 1);
|
theSentinels.Cache.resize(depthIndex + 1);
|
||||||
auto &cached = theSentinels.Cache[depthIndex];
|
auto &cached = theSentinels.Cache[depthIndex];
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//===--- Mutex.cpp - Mutex and ReadWriteLock Tests ------------------------===//
|
//===--- Mutex.cpp - Mutex Tests ------------------------------------------===//
|
||||||
//
|
//
|
||||||
// This source file is part of the Swift.org open source project
|
// This source file is part of the Swift.org open source project
|
||||||
//
|
//
|
||||||
@@ -254,504 +254,3 @@ void scopedReadThreaded(RW &lock) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ReadWriteLockTest, ScopedReadLockThreaded) {
|
|
||||||
ReadWriteLock lock;
|
|
||||||
scopedReadThreaded<ScopedReadLock, true>(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StaticReadWriteLockTest, ScopedReadLockThreaded) {
|
|
||||||
static StaticReadWriteLock lock;
|
|
||||||
scopedReadThreaded<StaticScopedReadLock, true>(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ReadWriteLockTest, ScopedReadUnlockThreaded) {
|
|
||||||
ReadWriteLock lock;
|
|
||||||
scopedReadThreaded<ScopedReadUnlock, false>(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StaticReadWriteLockTest, ScopedReadUnlockThreaded) {
|
|
||||||
static StaticReadWriteLock lock;
|
|
||||||
scopedReadThreaded<StaticScopedReadUnlock, false>(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename SWL, bool Locking, typename RW>
|
|
||||||
void scopedWriteLockThreaded(RW &lock) {
|
|
||||||
const int threadCount = 10;
|
|
||||||
|
|
||||||
std::set<int> readerHistory;
|
|
||||||
std::vector<std::set<int>> writerHistory;
|
|
||||||
writerHistory.assign(threadCount, std::set<int>());
|
|
||||||
|
|
||||||
int protectedValue = 0;
|
|
||||||
readerHistory.insert(protectedValue);
|
|
||||||
|
|
||||||
threadedExecute(threadCount,
|
|
||||||
[&](int index) {
|
|
||||||
if (Locking) {
|
|
||||||
for (int i = 0; i < 20; ++i) {
|
|
||||||
{
|
|
||||||
SWL guard(lock);
|
|
||||||
protectedValue += index * i;
|
|
||||||
writerHistory[index].insert(protectedValue);
|
|
||||||
}
|
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lock.writeLock();
|
|
||||||
for (int i = 0; i < 20; ++i) {
|
|
||||||
protectedValue += index * i;
|
|
||||||
writerHistory[index].insert(protectedValue);
|
|
||||||
{
|
|
||||||
SWL unguard(lock);
|
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lock.writeUnlock();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
|
||||||
lock.readLock();
|
|
||||||
readerHistory.insert(protectedValue);
|
|
||||||
lock.readUnlock();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
std::set<int> mergedHistory;
|
|
||||||
for (auto &history : writerHistory) {
|
|
||||||
mergedHistory.insert(history.begin(), history.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto value : readerHistory) {
|
|
||||||
ASSERT_EQ(mergedHistory.count(value), 1U);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ReadWriteLockTest, ScopedWriteLockThreaded) {
|
|
||||||
ReadWriteLock lock;
|
|
||||||
scopedWriteLockThreaded<ScopedWriteLock, true>(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StaticReadWriteLockTest, ScopedWriteLockThreaded) {
|
|
||||||
static StaticReadWriteLock lock;
|
|
||||||
scopedWriteLockThreaded<StaticScopedWriteLock, true>(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ReadWriteLockTest, ScopedWriteUnlockThreaded) {
|
|
||||||
ReadWriteLock lock;
|
|
||||||
scopedWriteLockThreaded<ScopedWriteUnlock, false>(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StaticReadWriteLockTest, ScopedWriteUnlockThreaded) {
|
|
||||||
static StaticReadWriteLock lock;
|
|
||||||
scopedWriteLockThreaded<StaticScopedWriteUnlock, false>(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename RW> void readLockWhileReadLockedThreaded(RW &lock) {
|
|
||||||
lock.readLock();
|
|
||||||
|
|
||||||
const int threadCount = 10;
|
|
||||||
|
|
||||||
std::atomic<bool> results[threadCount] = {};
|
|
||||||
|
|
||||||
std::atomic<bool> done(false);
|
|
||||||
threadedExecute(threadCount,
|
|
||||||
[&](int index) {
|
|
||||||
// Always perform at least one iteration of this loop to
|
|
||||||
// avoid spurious failures if this thread is slow to run.
|
|
||||||
do {
|
|
||||||
lock.withReadLock([&] {
|
|
||||||
results[index] = true;
|
|
||||||
std::this_thread::sleep_for(
|
|
||||||
std::chrono::milliseconds(5));
|
|
||||||
});
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
} while (!done);
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
done = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
lock.readUnlock();
|
|
||||||
|
|
||||||
for (auto &result : results) {
|
|
||||||
ASSERT_TRUE(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ReadWriteLockTest, ReadLockWhileReadLockedThreaded) {
|
|
||||||
ReadWriteLock lock;
|
|
||||||
readLockWhileReadLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StaticReadWriteLockTest, ReadLockWhileReadLockedThreaded) {
|
|
||||||
static StaticReadWriteLock lock;
|
|
||||||
readLockWhileReadLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename RW> void readLockWhileWriteLockedThreaded(RW &lock) {
|
|
||||||
lock.writeLock();
|
|
||||||
|
|
||||||
const int threadCount = 10;
|
|
||||||
|
|
||||||
std::atomic<int> results[threadCount] = {};
|
|
||||||
|
|
||||||
std::atomic<bool> done(false);
|
|
||||||
threadedExecute(threadCount,
|
|
||||||
[&](int index) {
|
|
||||||
// Always perform at least one iteration of this loop to
|
|
||||||
// avoid spurious failures if this thread is slow to run.
|
|
||||||
do {
|
|
||||||
lock.withReadLock([&] {
|
|
||||||
results[index] += 1;
|
|
||||||
std::this_thread::sleep_for(
|
|
||||||
std::chrono::milliseconds(5));
|
|
||||||
});
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
} while (!done);
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
done = true;
|
|
||||||
lock.writeUnlock();
|
|
||||||
});
|
|
||||||
|
|
||||||
for (auto &result : results) {
|
|
||||||
ASSERT_EQ(result, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ReadWriteLockTest, ReadLockWhileWriteLockedThreaded) {
|
|
||||||
ReadWriteLock lock;
|
|
||||||
readLockWhileWriteLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StaticReadWriteLockTest, ReadLockWhileWriteLockedThreaded) {
|
|
||||||
static StaticReadWriteLock lock;
|
|
||||||
readLockWhileWriteLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename RW> void writeLockWhileReadLockedThreaded(RW &lock) {
|
|
||||||
lock.readLock();
|
|
||||||
|
|
||||||
const int threadCount = 10;
|
|
||||||
|
|
||||||
std::atomic<int> results[threadCount] = {};
|
|
||||||
|
|
||||||
std::atomic<bool> done(false);
|
|
||||||
threadedExecute(threadCount,
|
|
||||||
[&](int index) {
|
|
||||||
// Always perform at least one iteration of this loop to
|
|
||||||
// avoid spurious failures if this thread is slow to run.
|
|
||||||
do {
|
|
||||||
lock.withWriteLock([&] {
|
|
||||||
results[index] += 1;
|
|
||||||
std::this_thread::sleep_for(
|
|
||||||
std::chrono::milliseconds(5));
|
|
||||||
});
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
} while (!done);
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
done = true;
|
|
||||||
lock.readUnlock();
|
|
||||||
});
|
|
||||||
|
|
||||||
for (auto &result : results) {
|
|
||||||
ASSERT_EQ(result, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ReadWriteLockTest, WriteLockWhileReadLockedThreaded) {
|
|
||||||
ReadWriteLock lock;
|
|
||||||
writeLockWhileReadLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StaticReadWriteLockTest, WriteLockWhileReadLockedThreaded) {
|
|
||||||
static StaticReadWriteLock lock;
|
|
||||||
writeLockWhileReadLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename RW> void writeLockWhileWriteLockedThreaded(RW &lock) {
|
|
||||||
lock.writeLock();
|
|
||||||
|
|
||||||
const int threadCount = 10;
|
|
||||||
|
|
||||||
std::atomic<int> results[threadCount] = {};
|
|
||||||
|
|
||||||
std::atomic<bool> done(false);
|
|
||||||
threadedExecute(threadCount,
|
|
||||||
[&](int index) {
|
|
||||||
// Always perform at least one iteration of this loop to
|
|
||||||
// avoid spurious failures if this thread is slow to run.
|
|
||||||
do {
|
|
||||||
lock.withWriteLock([&] {
|
|
||||||
results[index] += 1;
|
|
||||||
std::this_thread::sleep_for(
|
|
||||||
std::chrono::milliseconds(5));
|
|
||||||
});
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
} while (!done);
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
done = true;
|
|
||||||
lock.writeUnlock();
|
|
||||||
});
|
|
||||||
|
|
||||||
for (auto &result : results) {
|
|
||||||
ASSERT_EQ(result, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ReadWriteLockTest, WriteLockWhileWriteLockedThreaded) {
|
|
||||||
ReadWriteLock lock;
|
|
||||||
writeLockWhileWriteLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StaticReadWriteLockTest, WriteLockWhileWriteLockedThreaded) {
|
|
||||||
static StaticReadWriteLock lock;
|
|
||||||
writeLockWhileWriteLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename RW> void tryReadLockWhileWriteLockedThreaded(RW &lock) {
|
|
||||||
lock.writeLock();
|
|
||||||
|
|
||||||
std::atomic<bool> done(false);
|
|
||||||
threadedExecute(10,
|
|
||||||
[&](int) {
|
|
||||||
// Always perform at least one iteration of this loop to
|
|
||||||
// avoid spurious failures if this thread is slow to run.
|
|
||||||
do {
|
|
||||||
ASSERT_FALSE(lock.try_readLock());
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
} while (!done);
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
done = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
lock.writeUnlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ReadWriteLockTest, TryReadLockWhileWriteLockedThreaded) {
|
|
||||||
ReadWriteLock lock;
|
|
||||||
tryReadLockWhileWriteLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StaticReadWriteLockTest, TryReadLockWhileWriteLockedThreaded) {
|
|
||||||
static StaticReadWriteLock lock;
|
|
||||||
tryReadLockWhileWriteLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename RW> void tryReadLockWhileReadLockedThreaded(RW &lock) {
|
|
||||||
lock.readLock();
|
|
||||||
|
|
||||||
const int threadCount = 10;
|
|
||||||
|
|
||||||
std::atomic<bool> results[threadCount] = {};
|
|
||||||
|
|
||||||
std::atomic<bool> done(false);
|
|
||||||
threadedExecute(threadCount,
|
|
||||||
[&](int index) {
|
|
||||||
// Always perform at least one iteration of this loop to
|
|
||||||
// avoid spurious failures if this thread is slow to run.
|
|
||||||
do {
|
|
||||||
ASSERT_TRUE(lock.try_readLock());
|
|
||||||
results[index] = true;
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
||||||
lock.readUnlock();
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
} while (!done);
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
done = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
lock.readUnlock();
|
|
||||||
|
|
||||||
for (auto &result : results) {
|
|
||||||
ASSERT_TRUE(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ReadWriteLockTest, TryReadLockWhileReadLockedThreaded) {
|
|
||||||
ReadWriteLock lock;
|
|
||||||
tryReadLockWhileReadLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StaticReadWriteLockTest, TryReadLockWhileReadLockedThreaded) {
|
|
||||||
static StaticReadWriteLock lock;
|
|
||||||
tryReadLockWhileReadLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename RW> void tryWriteLockWhileWriteLockedThreaded(RW &lock) {
|
|
||||||
lock.writeLock();
|
|
||||||
|
|
||||||
std::atomic<bool> done(false);
|
|
||||||
threadedExecute(10,
|
|
||||||
[&](int) {
|
|
||||||
// Always perform at least one iteration of this loop to
|
|
||||||
// avoid spurious failures if this thread is slow to run.
|
|
||||||
do {
|
|
||||||
ASSERT_FALSE(lock.try_writeLock());
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
} while (!done);
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
done = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
lock.writeUnlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ReadWriteLockTest, TryWriteLockWhileWriteLockedThreaded) {
|
|
||||||
ReadWriteLock lock;
|
|
||||||
tryWriteLockWhileWriteLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StaticReadWriteLockTest, TryWriteLockWhileWriteLockedThreaded) {
|
|
||||||
static StaticReadWriteLock lock;
|
|
||||||
tryWriteLockWhileWriteLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename RW> void tryWriteLockWhileReadLockedThreaded(RW &lock) {
|
|
||||||
lock.readLock();
|
|
||||||
|
|
||||||
std::atomic<bool> done(false);
|
|
||||||
threadedExecute(10,
|
|
||||||
[&](int) {
|
|
||||||
// Always perform at least one iteration of this loop to
|
|
||||||
// avoid spurious failures if this thread is slow to run.
|
|
||||||
do {
|
|
||||||
ASSERT_FALSE(lock.try_writeLock());
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
} while (!done);
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
done = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
lock.readUnlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ReadWriteLockTest, TryWriteLockWhileReadLockedThreaded) {
|
|
||||||
ReadWriteLock lock;
|
|
||||||
tryWriteLockWhileReadLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StaticReadWriteLockTest, TryWriteLockWhileReadLockedThreaded) {
|
|
||||||
static StaticReadWriteLock lock;
|
|
||||||
tryWriteLockWhileReadLockedThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename RW> void readWriteLockCacheExampleThreaded(RW &lock) {
|
|
||||||
std::map<uint8_t, uint32_t> cache;
|
|
||||||
std::vector<std::thread> workers;
|
|
||||||
std::vector<std::set<uint8_t>> workerHistory;
|
|
||||||
|
|
||||||
std::random_device rd;
|
|
||||||
std::mt19937 gen(rd());
|
|
||||||
std::uniform_int_distribution<> dis(0, UINT8_MAX);
|
|
||||||
|
|
||||||
workerHistory.push_back(std::set<uint8_t>());
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
uint8_t key = dis(gen);
|
|
||||||
cache[key] = 0;
|
|
||||||
workerHistory[0].insert(key);
|
|
||||||
|
|
||||||
if (trace)
|
|
||||||
printf("WarmUp create for key = %d, value = %d.\n", key, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block the threads we are about to create.
|
|
||||||
const int threadCount = 20;
|
|
||||||
std::atomic<bool> spinWait(true);
|
|
||||||
std::atomic<int> readyCount(0);
|
|
||||||
|
|
||||||
for (int i = 1; i <= threadCount; ++i) {
|
|
||||||
workerHistory.push_back(std::set<uint8_t>());
|
|
||||||
workers.push_back(std::thread([&, i] {
|
|
||||||
readyCount++;
|
|
||||||
|
|
||||||
// Block ourself until we are released to start working.
|
|
||||||
while (spinWait) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
|
|
||||||
for (int j = 0; j < 50; j++) {
|
|
||||||
uint8_t key = dis(gen);
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
auto cacheLookupSection = [&] {
|
|
||||||
auto value = cache.find(key);
|
|
||||||
if (value == cache.end()) {
|
|
||||||
if (trace)
|
|
||||||
printf("Worker[%d] miss for key = %d.\n", i, key);
|
|
||||||
found = false; // cache miss, need to grab write lock
|
|
||||||
}
|
|
||||||
if (trace)
|
|
||||||
printf("Worker[%d] HIT for key = %d, value = %d.\n", i, key,
|
|
||||||
value->second);
|
|
||||||
found = true; // cache hit, no need to grab write lock
|
|
||||||
};
|
|
||||||
|
|
||||||
lock.withReadLock(cacheLookupSection);
|
|
||||||
if (found) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock.withWriteLock([&] {
|
|
||||||
cacheLookupSection();
|
|
||||||
if (!found) {
|
|
||||||
if (trace)
|
|
||||||
printf("Worker[%d] create for key = %d, value = %d.\n", i, key,
|
|
||||||
i);
|
|
||||||
cache[key] = i;
|
|
||||||
workerHistory[i].insert(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trace)
|
|
||||||
printf("### Worker[%d] thread exiting.\n", i);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
while (readyCount < threadCount) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow our threads to fight for the lock.
|
|
||||||
spinWait = false;
|
|
||||||
|
|
||||||
// Wait until all of our workers threads have finished.
|
|
||||||
for (auto &thread : workers) {
|
|
||||||
thread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &entry : cache) {
|
|
||||||
if (trace)
|
|
||||||
printf("### Cache dump key = %d, value = %d.\n", entry.first,
|
|
||||||
entry.second);
|
|
||||||
ASSERT_EQ(workerHistory[entry.second].count(entry.first), 1U);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ReadWriteLockTest, ReadWriteLockCacheExampleThreaded) {
|
|
||||||
ReadWriteLock lock;
|
|
||||||
readWriteLockCacheExampleThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StaticReadWriteLockTest, ReadWriteLockCacheExampleThreaded) {
|
|
||||||
static StaticReadWriteLock lock;
|
|
||||||
readWriteLockCacheExampleThreaded(lock);
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user