summaryrefslogtreecommitdiffstats
path: root/xbmc/threads/test/TestSharedSection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/threads/test/TestSharedSection.cpp')
-rw-r--r--xbmc/threads/test/TestSharedSection.cpp215
1 files changed, 215 insertions, 0 deletions
diff --git a/xbmc/threads/test/TestSharedSection.cpp b/xbmc/threads/test/TestSharedSection.cpp
new file mode 100644
index 0000000..fc2ca08
--- /dev/null
+++ b/xbmc/threads/test/TestSharedSection.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "threads/Event.h"
+#include "threads/IRunnable.h"
+#include "threads/SharedSection.h"
+#include "threads/test/TestHelpers.h"
+
+#include <mutex>
+#include <shared_mutex>
+#include <stdio.h>
+
+using namespace std::chrono_literals;
+
+//=============================================================================
+// Helper classes
+//=============================================================================
+
+template<class L>
+class locker : public IRunnable
+{
+ CSharedSection& sec;
+ CEvent* wait;
+
+ std::atomic<long>* mutex;
+public:
+ volatile bool haslock;
+ volatile bool obtainedlock;
+
+ inline locker(CSharedSection& o, std::atomic<long>* mutex_ = NULL, CEvent* wait_ = NULL) :
+ sec(o), wait(wait_), mutex(mutex_), haslock(false), obtainedlock(false) {}
+
+ inline locker(CSharedSection& o, CEvent* wait_ = NULL) :
+ sec(o), wait(wait_), mutex(NULL), haslock(false), obtainedlock(false) {}
+
+ void Run() override
+ {
+ AtomicGuard g(mutex);
+ L lock(sec);
+ haslock = true;
+ obtainedlock = true;
+ if (wait)
+ wait->Wait();
+ haslock = false;
+ }
+};
+
+TEST(TestCritSection, General)
+{
+ CCriticalSection sec;
+
+ std::unique_lock<CCriticalSection> l1(sec);
+ std::unique_lock<CCriticalSection> l2(sec);
+}
+
+TEST(TestSharedSection, General)
+{
+ CSharedSection sec;
+
+ std::shared_lock<CSharedSection> l1(sec);
+ std::shared_lock<CSharedSection> l2(sec);
+}
+
+TEST(TestSharedSection, GetSharedLockWhileTryingExclusiveLock)
+{
+ std::atomic<long> mutex(0L);
+ CEvent event;
+
+ CSharedSection sec;
+
+ std::shared_lock<CSharedSection> l1(sec); // get a shared lock
+
+ locker<std::unique_lock<CSharedSection>> l2(sec, &mutex);
+ thread waitThread1(l2); // try to get an exclusive lock
+
+ EXPECT_TRUE(waitForThread(mutex, 1, 10000ms));
+ std::this_thread::sleep_for(10ms); // still need to give it a chance to move ahead
+
+ EXPECT_TRUE(!l2.haslock); // this thread is waiting ...
+ EXPECT_TRUE(!l2.obtainedlock); // this thread is waiting ...
+
+ // now try and get a SharedLock
+ locker<std::shared_lock<CSharedSection>> l3(sec, &mutex, &event);
+ thread waitThread3(l3); // try to get a shared lock
+ EXPECT_TRUE(waitForThread(mutex, 2, 10000ms));
+ std::this_thread::sleep_for(10ms);
+ EXPECT_TRUE(l3.haslock);
+
+ event.Set();
+ EXPECT_TRUE(waitThread3.timed_join(10000ms));
+
+ // l3 should have released.
+ EXPECT_TRUE(!l3.haslock);
+
+ // but the exclusive lock should still not have happened
+ EXPECT_TRUE(!l2.haslock); // this thread is waiting ...
+ EXPECT_TRUE(!l2.obtainedlock); // this thread is waiting ...
+
+ // let it go
+ l1.unlock(); // the last shared lock leaves.
+
+ EXPECT_TRUE(waitThread1.timed_join(10000ms));
+
+ EXPECT_TRUE(l2.obtainedlock); // the exclusive lock was captured
+ EXPECT_TRUE(!l2.haslock); // ... but it doesn't have it anymore
+}
+
+TEST(TestSharedSection, TwoCase)
+{
+ CSharedSection sec;
+
+ CEvent event;
+ std::atomic<long> mutex(0L);
+
+ locker<std::shared_lock<CSharedSection>> l1(sec, &mutex, &event);
+
+ {
+ std::shared_lock<CSharedSection> lock(sec);
+ thread waitThread1(l1);
+
+ EXPECT_TRUE(waitForWaiters(event, 1, 10000ms));
+ EXPECT_TRUE(l1.haslock);
+
+ event.Set();
+
+ EXPECT_TRUE(waitThread1.timed_join(10000ms));
+ }
+
+ locker<std::shared_lock<CSharedSection>> l2(sec, &mutex, &event);
+ {
+ std::unique_lock<CSharedSection> lock(sec); // get exclusive lock
+ thread waitThread2(l2); // thread should block
+
+ EXPECT_TRUE(waitForThread(mutex, 1, 10000ms));
+ std::this_thread::sleep_for(10ms);
+
+ EXPECT_TRUE(!l2.haslock);
+
+ lock.unlock();
+
+ EXPECT_TRUE(waitForWaiters(event, 1, 10000ms));
+ std::this_thread::sleep_for(10ms);
+ EXPECT_TRUE(l2.haslock);
+
+ event.Set();
+
+ EXPECT_TRUE(waitThread2.timed_join(10000ms));
+ }
+}
+
+TEST(TestMultipleSharedSection, General)
+{
+ CSharedSection sec;
+
+ CEvent event;
+ std::atomic<long> mutex(0L);
+
+ locker<std::shared_lock<CSharedSection>> l1(sec, &mutex, &event);
+
+ {
+ std::shared_lock<CSharedSection> lock(sec);
+ thread waitThread1(l1);
+
+ EXPECT_TRUE(waitForThread(mutex, 1, 10000ms));
+ std::this_thread::sleep_for(10ms);
+
+ EXPECT_TRUE(l1.haslock);
+
+ event.Set();
+
+ EXPECT_TRUE(waitThread1.timed_join(10000ms));
+ }
+
+ locker<std::shared_lock<CSharedSection>> l2(sec, &mutex, &event);
+ locker<std::shared_lock<CSharedSection>> l3(sec, &mutex, &event);
+ locker<std::shared_lock<CSharedSection>> l4(sec, &mutex, &event);
+ locker<std::shared_lock<CSharedSection>> l5(sec, &mutex, &event);
+ {
+ std::unique_lock<CSharedSection> lock(sec);
+ thread waitThread1(l2);
+ thread waitThread2(l3);
+ thread waitThread3(l4);
+ thread waitThread4(l5);
+
+ EXPECT_TRUE(waitForThread(mutex, 4, 10000ms));
+ std::this_thread::sleep_for(10ms);
+
+ EXPECT_TRUE(!l2.haslock);
+ EXPECT_TRUE(!l3.haslock);
+ EXPECT_TRUE(!l4.haslock);
+ EXPECT_TRUE(!l5.haslock);
+
+ lock.unlock();
+
+ EXPECT_TRUE(waitForWaiters(event, 4, 10000ms));
+
+ EXPECT_TRUE(l2.haslock);
+ EXPECT_TRUE(l3.haslock);
+ EXPECT_TRUE(l4.haslock);
+ EXPECT_TRUE(l5.haslock);
+
+ event.Set();
+
+ EXPECT_TRUE(waitThread1.timed_join(10000ms));
+ EXPECT_TRUE(waitThread2.timed_join(10000ms));
+ EXPECT_TRUE(waitThread3.timed_join(10000ms));
+ EXPECT_TRUE(waitThread4.timed_join(10000ms));
+ }
+}
+