mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Read/write locks are not as good as you'd think; a simple mutex is better in almost all cases. rdar://90776105
257 lines
6.3 KiB
C++
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);
|
|
}
|
|
}
|
|
}
|