Files
swift-mirror/unittests/runtime/Mutex.cpp
Alastair Houghton 66b9d21000 [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
2022-06-07 07:39:51 +01:00

257 lines
6.3 KiB
C++

//===--- Mutex.cpp - Mutex Tests ------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "swift/Runtime/Mutex.h"
#include "gtest/gtest.h"
#include <atomic>
#include <chrono>
#include <map>
#include <random>
#include "ThreadingHelpers.h"
using namespace swift;
// -----------------------------------------------------------------------------
template <typename M> void basicLockableThreaded(M &mutex) {
int count1 = 0;
int count2 = 0;
threadedExecute(10, [&](int) {
for (int j = 0; j < 50; ++j) {
mutex.lock();
auto count = count2;
count1++;
count2 = count + 1;
mutex.unlock();
}
});
ASSERT_EQ(count1, 500);
ASSERT_EQ(count2, 500);
}
TEST(MutexTest, BasicLockableThreaded) {
Mutex mutex(/* checked = */ true);
basicLockableThreaded(mutex);
}
TEST(StaticMutexTest, BasicLockableThreaded) {
static StaticMutex mutex;
basicLockableThreaded(mutex);
}
TEST(StaticUnsafeMutexTest, BasicLockableThreaded) {
static StaticUnsafeMutex mutex;
basicLockableThreaded(mutex);
}
TEST(SmallMutex, BasicLockableThreaded) {
SmallMutex mutex;
basicLockableThreaded(mutex);
}
template <typename M> void lockableThreaded(M &mutex) {
mutex.lock();
threadedExecute(5, [&](int) { ASSERT_FALSE(mutex.try_lock()); });
mutex.unlock();
threadedExecute(1, [&](int) {
ASSERT_TRUE(mutex.try_lock());
mutex.unlock();
});
int count1 = 0;
int count2 = 0;
threadedExecute(10, [&](int) {
for (int j = 0; j < 50; ++j) {
if (mutex.try_lock()) {
auto count = count2;
count1++;
count2 = count + 1;
mutex.unlock();
} else {
j--;
}
}
});
ASSERT_EQ(count1, 500);
ASSERT_EQ(count2, 500);
}
TEST(MutexTest, LockableThreaded) {
Mutex mutex(/* checked = */ true);
lockableThreaded(mutex);
}
TEST(StaticMutexTest, LockableThreaded) {
static StaticMutex Mutex;
lockableThreaded(Mutex);
}
TEST(SmallMutexTest, LockableThreaded) {
SmallMutex Mutex;
lockableThreaded(Mutex);
}
template <typename SL, typename M> void scopedLockThreaded(M &mutex) {
int count1 = 0;
int count2 = 0;
threadedExecute(10, [&](int) {
for (int j = 0; j < 50; ++j) {
SL guard(mutex);
auto count = count2;
count1++;
count2 = count + 1;
}
});
ASSERT_EQ(count1, 500);
ASSERT_EQ(count2, 500);
}
TEST(MutexTest, ScopedLockThreaded) {
Mutex mutex(/* checked = */ true);
scopedLockThreaded<Mutex::ScopedLock>(mutex);
}
TEST(StaticMutexTest, ScopedLockThreaded) {
static StaticMutex Mutex;
scopedLockThreaded<StaticMutex::ScopedLock>(Mutex);
}
TEST(SmallMutexTest, ScopedLockThreaded) {
SmallMutex mutex(/* checked = */ true);
scopedLockThreaded<ScopedLockT<SmallMutex, false>>(mutex);
}
template <typename SL, typename SU, typename M>
void scopedUnlockUnderScopedLockThreaded(M &mutex) {
int count1 = 0;
int count2 = 0;
int badCount = 0;
threadedExecute(10, [&](int) {
for (int j = 0; j < 50; ++j) {
SL guard(mutex);
{
SU unguard(mutex);
badCount++;
}
auto count = count2;
count1++;
count2 = count + 1;
}
});
ASSERT_EQ(count1, 500);
ASSERT_EQ(count2, 500);
}
TEST(MutexTest, ScopedUnlockUnderScopedLockThreaded) {
Mutex mutex(/* checked = */ true);
scopedUnlockUnderScopedLockThreaded<Mutex::ScopedLock, Mutex::ScopedUnlock>(
mutex);
}
TEST(StaticMutexTest, ScopedUnlockUnderScopedLockThreaded) {
static StaticMutex Mutex;
scopedUnlockUnderScopedLockThreaded<StaticMutex::ScopedLock,
StaticMutex::ScopedUnlock>(Mutex);
}
TEST(SmallMutexTest, ScopedUnlockUnderScopedLockThreaded) {
SmallMutex mutex(/* checked = */ true);
scopedUnlockUnderScopedLockThreaded<SmallMutex::ScopedLock,
SmallMutex::ScopedUnlock>(mutex);
}
template <typename M> void criticalSectionThreaded(M &mutex) {
int count1 = 0;
int count2 = 0;
threadedExecute(10, [&](int) {
for (int j = 0; j < 50; ++j) {
mutex.withLock([&] {
auto count = count2;
count1++;
count2 = count + 1;
});
}
});
ASSERT_EQ(count1, 500);
ASSERT_EQ(count2, 500);
}
TEST(MutexTest, CriticalSectionThreaded) {
Mutex mutex(/* checked = */ true);
criticalSectionThreaded(mutex);
}
TEST(StaticMutexTest, CriticalSectionThreaded) {
static StaticMutex Mutex;
criticalSectionThreaded(Mutex);
}
template <typename SRL, bool Locking, typename RW>
void scopedReadThreaded(RW &lock) {
const int threadCount = 10;
std::set<int> writerHistory;
std::vector<std::set<int>> readerHistory;
readerHistory.assign(threadCount, std::set<int>());
int protectedValue = 0;
writerHistory.insert(protectedValue);
threadedExecute(threadCount,
[&](int index) {
if (Locking) {
for (int i = 0; i < 50; ++i) {
{
SRL guard(lock);
readerHistory[index].insert(protectedValue);
}
std::this_thread::yield();
}
} else {
lock.readLock();
for (int i = 0; i < 50; ++i) {
readerHistory[index].insert(protectedValue);
{
SRL unguard(lock);
std::this_thread::yield();
}
}
lock.readUnlock();
}
},
[&] {
for (int i = 0; i < 25; ++i) {
lock.writeLock();
protectedValue += i;
writerHistory.insert(protectedValue);
lock.writeUnlock();
}
});
for (auto &history : readerHistory) {
for (auto value : history) {
ASSERT_EQ(writerHistory.count(value), 1U);
}
}
}