summaryrefslogtreecommitdiffstats
path: root/xbmc/threads/test/TestEvent.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xbmc/threads/test/TestEvent.cpp628
1 files changed, 628 insertions, 0 deletions
diff --git a/xbmc/threads/test/TestEvent.cpp b/xbmc/threads/test/TestEvent.cpp
new file mode 100644
index 0000000..c4526b3
--- /dev/null
+++ b/xbmc/threads/test/TestEvent.cpp
@@ -0,0 +1,628 @@
+/*
+ * 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/test/TestHelpers.h"
+
+#include <memory>
+#include <stdio.h>
+
+using namespace XbmcThreads;
+using namespace std::chrono_literals;
+
+//=============================================================================
+// Helper classes
+//=============================================================================
+
+class waiter : public IRunnable
+{
+ CEvent& event;
+public:
+ bool& result;
+
+ volatile bool waiting;
+
+ waiter(CEvent& o, bool& flag) : event(o), result(flag), waiting(false) {}
+
+ void Run() override
+ {
+ waiting = true;
+ result = event.Wait();
+ waiting = false;
+ }
+};
+
+class timed_waiter : public IRunnable
+{
+ CEvent& event;
+ std::chrono::milliseconds waitTime;
+
+public:
+ int& result;
+
+ volatile bool waiting;
+
+ timed_waiter(CEvent& o, int& flag, std::chrono::milliseconds waitTimeMillis)
+ : event(o), waitTime(waitTimeMillis), result(flag), waiting(false)
+ {
+ }
+
+ void Run() override
+ {
+ waiting = true;
+ result = 0;
+ result = event.Wait(waitTime) ? 1 : -1;
+ waiting = false;
+ }
+};
+
+class group_wait : public IRunnable
+{
+ CEventGroup& event;
+ std::chrono::milliseconds timeout;
+
+public:
+ CEvent* result;
+ bool waiting;
+
+ group_wait(CEventGroup& o) : event(o), timeout(-1ms), result(NULL), waiting(false) {}
+
+ group_wait(CEventGroup& o, std::chrono::milliseconds timeout_)
+ : event(o), timeout(timeout_), result(NULL), waiting(false)
+ {
+ }
+
+ void Run() override
+ {
+ waiting = true;
+ if (timeout == -1ms)
+ result = event.wait();
+ else
+ result = event.wait(timeout);
+ waiting = false;
+ }
+};
+
+//=============================================================================
+
+TEST(TestEvent, General)
+{
+ CEvent event;
+ bool result = false;
+ waiter w1(event,result);
+ thread waitThread(w1);
+
+ EXPECT_TRUE(waitForWaiters(event, 1, 10000ms));
+
+ EXPECT_TRUE(!result);
+
+ event.Set();
+
+ EXPECT_TRUE(waitThread.timed_join(10000ms));
+
+ EXPECT_TRUE(result);
+}
+
+TEST(TestEvent, TwoWaits)
+{
+ CEvent event;
+ bool result1 = false;
+ bool result2 = false;
+ waiter w1(event,result1);
+ waiter w2(event,result2);
+ thread waitThread1(w1);
+ thread waitThread2(w2);
+
+ EXPECT_TRUE(waitForWaiters(event, 2, 10000ms));
+
+ EXPECT_TRUE(!result1);
+ EXPECT_TRUE(!result2);
+
+ event.Set();
+
+ EXPECT_TRUE(waitThread1.timed_join(10000ms));
+ EXPECT_TRUE(waitThread2.timed_join(10000ms));
+
+ EXPECT_TRUE(result1);
+ EXPECT_TRUE(result2);
+
+}
+
+TEST(TestEvent, TimedWaits)
+{
+ CEvent event;
+ int result1 = 10;
+ timed_waiter w1(event, result1, 100ms);
+ thread waitThread1(w1);
+
+ EXPECT_TRUE(waitForWaiters(event, 1, 10000ms));
+
+ EXPECT_TRUE(result1 == 0);
+
+ event.Set();
+
+ EXPECT_TRUE(waitThread1.timed_join(10000ms));
+
+ EXPECT_TRUE(result1 == 1);
+}
+
+TEST(TestEvent, TimedWaitsTimeout)
+{
+ CEvent event;
+ int result1 = 10;
+ timed_waiter w1(event, result1, 50ms);
+ thread waitThread1(w1);
+
+ EXPECT_TRUE(waitForWaiters(event, 1, 100ms));
+
+ EXPECT_TRUE(result1 == 0);
+
+ EXPECT_TRUE(waitThread1.timed_join(10000ms));
+
+ EXPECT_TRUE(result1 == -1);
+}
+
+TEST(TestEvent, Group)
+{
+ CEvent event1;
+ CEvent event2;
+
+ CEventGroup group{&event1,&event2};
+
+ bool result1 = false;
+ bool result2 = false;
+
+ waiter w1(event1,result1);
+ waiter w2(event2,result2);
+ group_wait w3(group);
+
+ thread waitThread1(w1);
+ thread waitThread2(w2);
+ thread waitThread3(w3);
+
+ EXPECT_TRUE(waitForWaiters(event1, 1, 10000ms));
+ EXPECT_TRUE(waitForWaiters(event2, 1, 10000ms));
+ EXPECT_TRUE(waitForWaiters(group, 1, 10000ms));
+
+ EXPECT_TRUE(!result1);
+ EXPECT_TRUE(!result2);
+
+ EXPECT_TRUE(w3.waiting);
+ EXPECT_TRUE(w3.result == NULL);
+
+ event1.Set();
+
+ EXPECT_TRUE(waitThread1.timed_join(10000ms));
+ EXPECT_TRUE(waitThread3.timed_join(10000ms));
+ std::this_thread::sleep_for(10ms);
+
+ EXPECT_TRUE(result1);
+ EXPECT_TRUE(!w1.waiting);
+ EXPECT_TRUE(!result2);
+ EXPECT_TRUE(w2.waiting);
+ EXPECT_TRUE(!w3.waiting);
+ EXPECT_TRUE(w3.result == &event1);
+
+ event2.Set();
+
+ EXPECT_TRUE(waitThread2.timed_join(10000ms));
+}
+
+/* Test disabled for now, because it deadlocks
+TEST(TestEvent, GroupLimitedGroupScope)
+{
+ CEvent event1;
+ CEvent event2;
+
+ {
+ CEventGroup group(&event1,&event2,NULL);
+
+ bool result1 = false;
+ bool result2 = false;
+
+ waiter w1(event1,result1);
+ waiter w2(event2,result2);
+ group_wait w3(group);
+
+ thread waitThread1(w1);
+ thread waitThread2(w2);
+ thread waitThread3(w3);
+
+ EXPECT_TRUE(waitForWaiters(event1,1,10000ms));
+ EXPECT_TRUE(waitForWaiters(event2,1,10000ms));
+ EXPECT_TRUE(waitForWaiters(group,1,10000ms));
+
+ EXPECT_TRUE(!result1);
+ EXPECT_TRUE(!result2);
+
+ EXPECT_TRUE(w3.waiting);
+ EXPECT_TRUE(w3.result == NULL);
+
+ event1.Set();
+
+ EXPECT_TRUE(waitThread1.timed_join(10000ms));
+ EXPECT_TRUE(waitThread3.timed_join(10000ms));
+ std::this_thread::sleep_for(10ms);
+
+ EXPECT_TRUE(result1);
+ EXPECT_TRUE(!w1.waiting);
+ EXPECT_TRUE(!result2);
+ EXPECT_TRUE(w2.waiting);
+ EXPECT_TRUE(!w3.waiting);
+ EXPECT_TRUE(w3.result == &event1);
+ }
+
+ event2.Set();
+
+ std::this_thread::sleep_for(50ms); // give thread 2 a chance to exit
+}*/
+
+TEST(TestEvent, TwoGroups)
+{
+ CEvent event1;
+ CEvent event2;
+
+ CEventGroup group1{&event1,&event2};
+ CEventGroup group2{&event1,&event2};
+
+ bool result1 = false;
+ bool result2 = false;
+
+ waiter w1(event1,result1);
+ waiter w2(event2,result2);
+ group_wait w3(group1);
+ group_wait w4(group2);
+
+ thread waitThread1(w1);
+ thread waitThread2(w2);
+ thread waitThread3(w3);
+ thread waitThread4(w4);
+
+ EXPECT_TRUE(waitForWaiters(event1, 1, 10000ms));
+ EXPECT_TRUE(waitForWaiters(event2, 1, 10000ms));
+ EXPECT_TRUE(waitForWaiters(group1, 1, 10000ms));
+ EXPECT_TRUE(waitForWaiters(group2, 1, 10000ms));
+
+ EXPECT_TRUE(!result1);
+ EXPECT_TRUE(!result2);
+
+ EXPECT_TRUE(w3.waiting);
+ EXPECT_EQ(w3.result,(void*)NULL);
+ EXPECT_TRUE(w4.waiting);
+ EXPECT_EQ(w4.result,(void*)NULL);
+
+ event1.Set();
+
+ EXPECT_TRUE(waitThread1.timed_join(10000ms));
+ EXPECT_TRUE(waitThread3.timed_join(10000ms));
+ EXPECT_TRUE(waitThread4.timed_join(10000ms));
+ std::this_thread::sleep_for(10ms);
+
+ EXPECT_TRUE(result1);
+ EXPECT_TRUE(!w1.waiting);
+ EXPECT_TRUE(!result2);
+ EXPECT_TRUE(w2.waiting);
+ EXPECT_TRUE(!w3.waiting);
+ EXPECT_TRUE(w3.result == &event1);
+ EXPECT_TRUE(!w4.waiting);
+ EXPECT_TRUE(w4.result == &event1);
+
+ event2.Set();
+
+ EXPECT_TRUE(waitThread2.timed_join(10000ms));
+}
+
+TEST(TestEvent, AutoResetBehavior)
+{
+ CEvent event;
+
+ EXPECT_TRUE(!event.Wait(1ms));
+
+ event.Set(); // event will remain signaled if there are no waits
+
+ EXPECT_TRUE(event.Wait(1ms));
+}
+
+TEST(TestEvent, ManualReset)
+{
+ CEvent event(true);
+ bool result = false;
+ waiter w1(event,result);
+ thread waitThread(w1);
+
+ EXPECT_TRUE(waitForWaiters(event, 1, 10000ms));
+
+ EXPECT_TRUE(!result);
+
+ event.Set();
+
+ EXPECT_TRUE(waitThread.timed_join(10000ms));
+
+ EXPECT_TRUE(result);
+
+ // with manual reset, the state should remain signaled
+ EXPECT_TRUE(event.Wait(1ms));
+
+ event.Reset();
+
+ EXPECT_TRUE(!event.Wait(1ms));
+}
+
+TEST(TestEvent, InitVal)
+{
+ CEvent event(false,true);
+ EXPECT_TRUE(event.Wait(50ms));
+}
+
+TEST(TestEvent, SimpleTimeout)
+{
+ CEvent event;
+ EXPECT_TRUE(!event.Wait(50ms));
+}
+
+TEST(TestEvent, GroupChildSet)
+{
+ CEvent event1(true);
+ CEvent event2;
+
+ event1.Set();
+ CEventGroup group{&event1,&event2};
+
+ bool result1 = false;
+ bool result2 = false;
+
+ waiter w1(event1,result1);
+ waiter w2(event2,result2);
+ group_wait w3(group);
+
+ thread waitThread1(w1);
+ thread waitThread2(w2);
+ thread waitThread3(w3);
+
+ EXPECT_TRUE(waitForWaiters(event2, 1, 10000ms));
+ EXPECT_TRUE(waitThread1.timed_join(10000ms));
+ EXPECT_TRUE(waitThread3.timed_join(10000ms));
+ std::this_thread::sleep_for(10ms);
+
+ EXPECT_TRUE(result1);
+ EXPECT_TRUE(!result2);
+
+ EXPECT_TRUE(!w3.waiting);
+ EXPECT_TRUE(w3.result == &event1);
+
+ event2.Set();
+
+ EXPECT_TRUE(waitThread2.timed_join(10000ms));
+}
+
+TEST(TestEvent, GroupChildSet2)
+{
+ CEvent event1(true,true);
+ CEvent event2;
+
+ CEventGroup group{&event1,&event2};
+
+ bool result1 = false;
+ bool result2 = false;
+
+ waiter w1(event1,result1);
+ waiter w2(event2,result2);
+ group_wait w3(group);
+
+ thread waitThread1(w1);
+ thread waitThread2(w2);
+ thread waitThread3(w3);
+
+ EXPECT_TRUE(waitForWaiters(event2, 1, 10000ms));
+ EXPECT_TRUE(waitThread1.timed_join(10000ms));
+ EXPECT_TRUE(waitThread3.timed_join(10000ms));
+ std::this_thread::sleep_for(10ms);
+
+ EXPECT_TRUE(result1);
+ EXPECT_TRUE(!result2);
+
+ EXPECT_TRUE(!w3.waiting);
+ EXPECT_TRUE(w3.result == &event1);
+
+ event2.Set();
+
+ EXPECT_TRUE(waitThread2.timed_join(10000ms));
+}
+
+TEST(TestEvent, GroupWaitResetsChild)
+{
+ CEvent event1;
+ CEvent event2;
+
+ CEventGroup group{&event1,&event2};
+
+ group_wait w3(group);
+
+ thread waitThread3(w3);
+
+ EXPECT_TRUE(waitForWaiters(group, 1, 10000ms));
+
+ EXPECT_TRUE(w3.waiting);
+ EXPECT_TRUE(w3.result == NULL);
+
+ event2.Set();
+
+ EXPECT_TRUE(waitThread3.timed_join(10000ms));
+
+ EXPECT_TRUE(!w3.waiting);
+ EXPECT_TRUE(w3.result == &event2);
+ // event2 should have been reset.
+ EXPECT_TRUE(event2.Wait(1ms) == false);
+}
+
+TEST(TestEvent, GroupTimedWait)
+{
+ CEvent event1;
+ CEvent event2;
+ CEventGroup group{&event1,&event2};
+
+ bool result1 = false;
+ bool result2 = false;
+
+ waiter w1(event1,result1);
+ waiter w2(event2,result2);
+
+ thread waitThread1(w1);
+ thread waitThread2(w2);
+
+ EXPECT_TRUE(waitForWaiters(event1, 1, 10000ms));
+ EXPECT_TRUE(waitForWaiters(event2, 1, 10000ms));
+
+ EXPECT_TRUE(group.wait(20ms) == NULL); // waited ... got nothing
+
+ group_wait w3(group, 50ms);
+ thread waitThread3(w3);
+
+ EXPECT_TRUE(waitForWaiters(group, 1, 10000ms));
+
+ EXPECT_TRUE(!result1);
+ EXPECT_TRUE(!result2);
+
+ EXPECT_TRUE(w3.waiting);
+ EXPECT_TRUE(w3.result == NULL);
+
+ // this should end given the wait is for only 50 millis
+ EXPECT_TRUE(waitThread3.timed_join(200ms));
+
+ EXPECT_TRUE(!w3.waiting);
+ EXPECT_TRUE(w3.result == NULL);
+
+ group_wait w4(group, 50ms);
+ thread waitThread4(w4);
+
+ EXPECT_TRUE(waitForWaiters(group, 1, 10000ms));
+
+ EXPECT_TRUE(w4.waiting);
+ EXPECT_TRUE(w4.result == NULL);
+
+ event1.Set();
+
+ EXPECT_TRUE(waitThread1.timed_join(10000ms));
+ EXPECT_TRUE(waitThread4.timed_join(10000ms));
+ std::this_thread::sleep_for(10ms);
+
+ EXPECT_TRUE(result1);
+ EXPECT_TRUE(!result2);
+
+ EXPECT_TRUE(!w4.waiting);
+ EXPECT_TRUE(w4.result == &event1);
+
+ event2.Set();
+
+ EXPECT_TRUE(waitThread2.timed_join(10000ms));
+}
+
+#define TESTNUM 100000l
+#define NUMTHREADS 100l
+
+CEvent* g_event = NULL;
+std::atomic<long> g_mutex;
+
+class mass_waiter : public IRunnable
+{
+public:
+ CEvent& event;
+ bool result;
+
+ volatile bool waiting = false;
+
+ mass_waiter() : event(*g_event) {}
+
+ void Run() override
+ {
+ waiting = true;
+ AtomicGuard g(&g_mutex);
+ result = event.Wait();
+ waiting = false;
+ }
+};
+
+class poll_mass_waiter : public IRunnable
+{
+public:
+ CEvent& event;
+ bool result;
+
+ volatile bool waiting = false;
+
+ poll_mass_waiter() : event(*g_event) {}
+
+ void Run() override
+ {
+ waiting = true;
+ AtomicGuard g(&g_mutex);
+ while ((result = event.Wait(0ms)) == false)
+ ;
+ waiting = false;
+ }
+};
+
+template <class W> void RunMassEventTest(std::vector<std::shared_ptr<W>>& m, bool canWaitOnEvent)
+{
+ std::vector<std::shared_ptr<thread>> t(NUMTHREADS);
+ for(size_t i=0; i<NUMTHREADS; i++)
+ t[i].reset(new thread(*m[i]));
+
+ EXPECT_TRUE(waitForThread(g_mutex, NUMTHREADS, 10000ms));
+ if (canWaitOnEvent)
+ {
+ EXPECT_TRUE(waitForWaiters(*g_event, NUMTHREADS, 10000ms));
+ }
+
+ std::this_thread::sleep_for(100ms); // give them a little more time
+
+ for(size_t i=0; i<NUMTHREADS; i++)
+ {
+ EXPECT_TRUE(m[i]->waiting);
+ }
+
+ g_event->Set();
+
+ for(size_t i=0; i<NUMTHREADS; i++)
+ {
+ EXPECT_TRUE(t[i]->timed_join(10000ms));
+ }
+
+ for(size_t i=0; i<NUMTHREADS; i++)
+ {
+ EXPECT_TRUE(!m[i]->waiting);
+ EXPECT_TRUE(m[i]->result);
+ }
+}
+
+
+TEST(TestMassEvent, General)
+{
+ g_event = new CEvent();
+
+ std::vector<std::shared_ptr<mass_waiter>> m(NUMTHREADS);
+ for(size_t i=0; i<NUMTHREADS; i++)
+ m[i].reset(new mass_waiter());
+
+ RunMassEventTest(m,true);
+ delete g_event;
+}
+
+TEST(TestMassEvent, Polling)
+{
+ g_event = new CEvent(true); // polling needs to avoid the auto-reset
+
+ std::vector<std::shared_ptr<poll_mass_waiter>> m(NUMTHREADS);
+ for(size_t i=0; i<NUMTHREADS; i++)
+ m[i].reset(new poll_mass_waiter());
+
+ RunMassEventTest(m,false);
+ delete g_event;
+}
+