mirror of
https://github.com/LizardByte/Sunshine.git
synced 2026-11-03 02:37:07 +09:00
93 lines
2.1 KiB
C++
93 lines
2.1 KiB
C++
/**
|
|
* @file tests/unit/test_sync.cpp
|
|
* @brief Regression coverage for synchronized-value lock lifetimes.
|
|
*/
|
|
|
|
// test includes
|
|
#include "../tests_common.h"
|
|
|
|
// standard includes
|
|
#include <array>
|
|
#include <stdexcept>
|
|
#include <system_error>
|
|
#include <thread>
|
|
|
|
// local includes
|
|
#include <src/sync.h>
|
|
|
|
namespace {
|
|
/**
|
|
* @brief BasicLockable mutex that detects an unreleased lock without requiring try_lock.
|
|
*/
|
|
class basic_mutex {
|
|
public:
|
|
/**
|
|
* @brief Acquire the mutex, rejecting recursive locking.
|
|
*
|
|
* @throws std::system_error If the mutex is already locked.
|
|
*/
|
|
void lock() {
|
|
if (held_) {
|
|
throw std::system_error {std::make_error_code(std::errc::resource_deadlock_would_occur)};
|
|
}
|
|
held_ = true;
|
|
}
|
|
|
|
/**
|
|
* @brief Release the mutex.
|
|
*/
|
|
void unlock() noexcept {
|
|
held_ = false;
|
|
}
|
|
|
|
private:
|
|
bool held_ {false}; ///< Whether a guard currently owns this mutex.
|
|
};
|
|
} // namespace
|
|
|
|
TEST(SyncLockTest, RejectsRecursiveLockingAndReleasesTheOriginalGuard) {
|
|
sync_util::sync_t<int, basic_mutex> value {0};
|
|
{
|
|
const auto guard = value.lock();
|
|
EXPECT_THROW(value.lock(), std::system_error);
|
|
}
|
|
EXPECT_NO_THROW(value.lock());
|
|
}
|
|
|
|
TEST(SyncLockTest, SupportsBasicLockableAndReleasesOnException) {
|
|
sync_util::sync_t<int, basic_mutex> value {0};
|
|
EXPECT_THROW(
|
|
[&value]() {
|
|
const auto guard = value.lock();
|
|
++*value;
|
|
throw std::runtime_error {"unwind the guard"};
|
|
}(),
|
|
std::runtime_error
|
|
);
|
|
|
|
EXPECT_NO_THROW([&value]() {
|
|
const auto guard = value.lock();
|
|
EXPECT_EQ(*value, 1);
|
|
}());
|
|
EXPECT_NO_THROW([&value]() {
|
|
const auto guard = value.lock();
|
|
EXPECT_EQ(*value, 1);
|
|
}());
|
|
}
|
|
|
|
TEST(SyncLockTest, SerializesConcurrentUpdates) {
|
|
sync_util::sync_t<int> value {0};
|
|
{
|
|
std::array<std::jthread, 4> workers;
|
|
for (auto &worker : workers) {
|
|
worker = std::jthread {[&value]() {
|
|
for (int i = 0; i < 250; ++i) {
|
|
const auto guard = value.lock();
|
|
++*value;
|
|
}
|
|
}};
|
|
}
|
|
}
|
|
EXPECT_EQ(*value, 1000);
|
|
}
|