From fc00569db6b1b9d45464ecbcf42b82e6f2150d21 Mon Sep 17 00:00:00 2001 From: Vitaly Baranov Date: Sun, 3 Dec 2023 13:44:35 +0100 Subject: [PATCH] Add more tests for RWLock. --- src/Common/tests/gtest_rw_lock.cpp | 230 +++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) diff --git a/src/Common/tests/gtest_rw_lock.cpp b/src/Common/tests/gtest_rw_lock.cpp index 5ea50f70d4e..7de3ced2d0d 100644 --- a/src/Common/tests/gtest_rw_lock.cpp +++ b/src/Common/tests/gtest_rw_lock.cpp @@ -24,6 +24,39 @@ namespace DB } +namespace +{ + class Events + { + public: + Events() : start_time(std::chrono::steady_clock::now()) {} + + void add(String && event) + { + String timepoint = std::to_string(std::chrono::duration_cast(std::chrono::steady_clock::now() - start_time).count()); + if (timepoint.length() < 5) + timepoint.insert(0, 5 - timepoint.length(), ' '); + std::lock_guard lock{mutex}; + std::cout << timepoint << " : " << event << std::endl; + events.emplace_back(std::move(event)); + } + + void check(const Strings & expected_events) + { + std::lock_guard lock{mutex}; + EXPECT_EQ(events.size(), expected_events.size()); + for (size_t i = 0; i != events.size(); ++i) + EXPECT_EQ(events[i], (i < expected_events.size() ? expected_events[i] : "")); + } + + private: + const std::chrono::time_point start_time; + Strings events TSA_GUARDED_BY(mutex); + mutable std::mutex mutex; + }; +} + + TEST(Common, RWLock1) { /// Tests with threads require this, because otherwise @@ -287,3 +320,200 @@ TEST(Common, RWLockNotUpgradeableWithNoQuery) read_thread.join(); } + + +TEST(Common, RWLockWriteLockTimeoutDuringRead) +{ + static auto rw_lock = RWLockImpl::create(); + Events events; + + std::thread ra_thread([&] () + { + events.add("Locking ra"); + auto ra = rw_lock->getLock(RWLockImpl::Read, "ra"); + events.add(ra ? "Locked ra" : "Failed to lock ra"); + EXPECT_NE(ra, nullptr); + + std::this_thread::sleep_for(std::chrono::duration(400)); + + events.add("Unlocking ra"); + ra.reset(); + events.add("Unlocked ra"); + }); + + std::thread wc_thread([&] () + { + std::this_thread::sleep_for(std::chrono::duration(100)); + events.add("Locking wc"); + auto wc = rw_lock->getLock(RWLockImpl::Write, "wc", std::chrono::milliseconds(200)); + events.add(wc ? "Locked wc" : "Failed to lock wc"); + EXPECT_EQ(wc, nullptr); + }); + + ra_thread.join(); + wc_thread.join(); + + { + events.add("Locking wd"); + auto wd = rw_lock->getLock(RWLockImpl::Write, "wd", std::chrono::milliseconds(1000)); + events.add(wd ? "Locked wd" : "Failed to lock wd"); + EXPECT_NE(wd, nullptr); + events.add("Unlocking wd"); + wd.reset(); + events.add("Unlocked wd"); + } + + events.check( + {"Locking ra", + "Locked ra", + "Locking wc", + "Failed to lock wc", + "Unlocking ra", + "Unlocked ra", + "Locking wd", + "Locked wd", + "Unlocking wd", + "Unlocked wd"}); +} + + +TEST(Common, RWLockWriteLockTimeoutDuringTwoReads) +{ + static auto rw_lock = RWLockImpl::create(); + Events events; + + std::thread ra_thread([&] () + { + events.add("Locking ra"); + auto ra = rw_lock->getLock(RWLockImpl::Read, "ra"); + events.add(ra ? "Locked ra" : "Failed to lock ra"); + EXPECT_NE(ra, nullptr); + + std::this_thread::sleep_for(std::chrono::duration(400)); + + events.add("Unlocking ra"); + ra.reset(); + events.add("Unlocked ra"); + }); + + std::thread rb_thread([&] () + { + std::this_thread::sleep_for(std::chrono::duration(200)); + events.add("Locking rb"); + + auto rb = rw_lock->getLock(RWLockImpl::Read, "rb"); + events.add(rb ? "Locked rb" : "Failed to lock rb"); + EXPECT_NE(rb, nullptr); + + std::this_thread::sleep_for(std::chrono::duration(200)); + events.add("Unlocking rb"); + rb.reset(); + events.add("Unlocked rb"); + }); + + std::thread wc_thread([&] () + { + std::this_thread::sleep_for(std::chrono::duration(100)); + events.add("Locking wc"); + auto wc = rw_lock->getLock(RWLockImpl::Write, "wc", std::chrono::milliseconds(200)); + events.add(wc ? "Locked wc" : "Failed to lock wc"); + EXPECT_EQ(wc, nullptr); + }); + + ra_thread.join(); + rb_thread.join(); + wc_thread.join(); + + { + events.add("Locking wd"); + auto wd = rw_lock->getLock(RWLockImpl::Write, "wd", std::chrono::milliseconds(1000)); + events.add(wd ? "Locked wd" : "Failed to lock wd"); + EXPECT_NE(wd, nullptr); + events.add("Unlocking wd"); + wd.reset(); + events.add("Unlocked wd"); + } + + events.check( + {"Locking ra", + "Locked ra", + "Locking wc", + "Locking rb", + "Failed to lock wc", + "Locked rb", + "Unlocking ra", + "Unlocked ra", + "Unlocking rb", + "Unlocked rb", + "Locking wd", + "Locked wd", + "Unlocking wd", + "Unlocked wd"}); +} + + +TEST(Common, RWLockWriteLockTimeoutDuringWriteWithWaitingRead) +{ + static auto rw_lock = RWLockImpl::create(); + Events events; + + std::thread wa_thread([&] () + { + events.add("Locking wa"); + auto wa = rw_lock->getLock(RWLockImpl::Write, "wa"); + events.add(wa ? "Locked wa" : "Failed to lock wa"); + EXPECT_NE(wa, nullptr); + + std::this_thread::sleep_for(std::chrono::duration(500)); + + events.add("Unlocking wa"); + wa.reset(); + events.add("Unlocked wa"); + }); + + std::thread wb_thread([&] () + { + std::this_thread::sleep_for(std::chrono::duration(100)); + events.add("Locking wb"); + auto wc = rw_lock->getLock(RWLockImpl::Write, "wc", std::chrono::milliseconds(200)); + events.add(wc ? "Locked wb" : "Failed to lock wb"); + EXPECT_EQ(wc, nullptr); + }); + + std::thread rc_thread([&] () + { + std::this_thread::sleep_for(std::chrono::duration(200)); + events.add("Locking rc"); + auto rc = rw_lock->getLock(RWLockImpl::Read, "rc", std::chrono::milliseconds(200)); + events.add(rc ? "Locked rc" : "Failed to lock rc"); + EXPECT_EQ(rc, nullptr); + }); + + wa_thread.join(); + wb_thread.join(); + rc_thread.join(); + + { + events.add("Locking wd"); + auto wd = rw_lock->getLock(RWLockImpl::Write, "wd", std::chrono::milliseconds(1000)); + events.add(wd ? "Locked wd" : "Failed to lock wd"); + EXPECT_NE(wd, nullptr); + events.add("Unlocking wd"); + wd.reset(); + events.add("Unlocked wd"); + } + + events.check( + {"Locking wa", + "Locked wa", + "Locking wb", + "Locking rc", + "Failed to lock wb", + "Failed to lock rc", + "Unlocking wa", + "Unlocked wa", + "Locking wd", + "Locked wd", + "Unlocking wd", + "Unlocked wd"}); +}