/* * 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/SingleLock.h" #include "threads/test/TestHelpers.h" #include //============================================================================= // Helper classes //============================================================================= template class locker : public IRunnable { CSharedSection& sec; CEvent* wait; std::atomic* mutex; public: volatile bool haslock; volatile bool obtainedlock; inline locker(CSharedSection& o, std::atomic* 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; CSingleLock l1(sec); CSingleLock l2(sec); } TEST(TestSharedSection, General) { CSharedSection sec; CSharedLock l1(sec); CSharedLock l2(sec); } TEST(TestSharedSection, GetSharedLockWhileTryingExclusiveLock) { std::atomic mutex(0L); CEvent event; CSharedSection sec; CSharedLock l1(sec); // get a shared lock locker l2(sec,&mutex); thread waitThread1(l2); // try to get an exclusive lock EXPECT_TRUE(waitForThread(mutex,1,10000)); SleepMillis(10); // 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 l3(sec,&mutex,&event); thread waitThread3(l3); // try to get a shared lock EXPECT_TRUE(waitForThread(mutex,2,10000)); SleepMillis(10); EXPECT_TRUE(l3.haslock); event.Set(); EXPECT_TRUE(waitThread3.timed_join(MILLIS(10000))); // 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.Leave(); // the last shared lock leaves. EXPECT_TRUE(waitThread1.timed_join(MILLIS(10000))); 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 mutex(0L); locker l1(sec,&mutex,&event); { CSharedLock lock(sec); thread waitThread1(l1); EXPECT_TRUE(waitForWaiters(event,1,10000)); EXPECT_TRUE(l1.haslock); event.Set(); EXPECT_TRUE(waitThread1.timed_join(MILLIS(10000))); } locker l2(sec,&mutex,&event); { CExclusiveLock lock(sec); // get exclusive lock thread waitThread2(l2); // thread should block EXPECT_TRUE(waitForThread(mutex,1,10000)); SleepMillis(10); EXPECT_TRUE(!l2.haslock); lock.Leave(); EXPECT_TRUE(waitForWaiters(event,1,10000)); SleepMillis(10); EXPECT_TRUE(l2.haslock); event.Set(); EXPECT_TRUE(waitThread2.timed_join(MILLIS(10000))); } } TEST(TestMultipleSharedSection, General) { CSharedSection sec; CEvent event; std::atomic mutex(0L); locker l1(sec,&mutex, &event); { CSharedLock lock(sec); thread waitThread1(l1); EXPECT_TRUE(waitForThread(mutex,1,10000)); SleepMillis(10); EXPECT_TRUE(l1.haslock); event.Set(); EXPECT_TRUE(waitThread1.timed_join(MILLIS(10000))); } locker l2(sec,&mutex,&event); locker l3(sec,&mutex,&event); locker l4(sec,&mutex,&event); locker l5(sec,&mutex,&event); { CExclusiveLock lock(sec); thread waitThread1(l2); thread waitThread2(l3); thread waitThread3(l4); thread waitThread4(l5); EXPECT_TRUE(waitForThread(mutex,4,10000)); SleepMillis(10); EXPECT_TRUE(!l2.haslock); EXPECT_TRUE(!l3.haslock); EXPECT_TRUE(!l4.haslock); EXPECT_TRUE(!l5.haslock); lock.Leave(); EXPECT_TRUE(waitForWaiters(event,4,10000)); EXPECT_TRUE(l2.haslock); EXPECT_TRUE(l3.haslock); EXPECT_TRUE(l4.haslock); EXPECT_TRUE(l5.haslock); event.Set(); EXPECT_TRUE(waitThread1.timed_join(MILLIS(10000))); EXPECT_TRUE(waitThread2.timed_join(MILLIS(10000))); EXPECT_TRUE(waitThread3.timed_join(MILLIS(10000))); EXPECT_TRUE(waitThread4.timed_join(MILLIS(10000))); } }