diff --git a/src/Port/WorkerThreadStd.cpp b/src/Port/WorkerThreadStd.cpp index 881cca3..ad2e12b 100644 --- a/src/Port/WorkerThreadStd.cpp +++ b/src/Port/WorkerThreadStd.cpp @@ -73,6 +73,15 @@ std::thread::id WorkerThread::GetCurrentThreadId() return this_thread::get_id(); } +//---------------------------------------------------------------------------- +// GetQueueSize +//---------------------------------------------------------------------------- +size_t WorkerThread::GetQueueSize() +{ + lock_guard lock(m_mutex); + return m_queue.size(); +} + //---------------------------------------------------------------------------- // ExitThread //---------------------------------------------------------------------------- diff --git a/src/Port/WorkerThreadStd.h b/src/Port/WorkerThreadStd.h index 404f7c7..a195f69 100644 --- a/src/Port/WorkerThreadStd.h +++ b/src/Port/WorkerThreadStd.h @@ -36,6 +36,9 @@ class WorkerThread : public DelegateLib::DelegateThread /// Get the ID of the currently executing thread static std::thread::id GetCurrentThreadId(); + /// Get size of thread message queue. + size_t GetQueueSize(); + virtual void DispatchDelegate(std::shared_ptr msg); private: diff --git a/tests/UnitTests/DelegateAsyncWait_UT.cpp b/tests/UnitTests/DelegateAsyncWait_UT.cpp index 7f1192a..69d5a66 100644 --- a/tests/UnitTests/DelegateAsyncWait_UT.cpp +++ b/tests/UnitTests/DelegateAsyncWait_UT.cpp @@ -2,9 +2,6 @@ #include "UnitTestCommon.h" #include #include "WorkerThreadStd.h" -#ifdef WIN32 -#include -#endif using namespace DelegateLib; using namespace std; diff --git a/tests/UnitTests/DelegateAsync_UT.cpp b/tests/UnitTests/DelegateAsync_UT.cpp index 13592f1..1e2d4e7 100644 --- a/tests/UnitTests/DelegateAsync_UT.cpp +++ b/tests/UnitTests/DelegateAsync_UT.cpp @@ -2,9 +2,6 @@ #include "UnitTestCommon.h" #include #include "WorkerThreadStd.h" -#ifdef WIN32 -#include -#endif using namespace DelegateLib; using namespace std; diff --git a/tests/UnitTests/DelegateThreads_UT.cpp b/tests/UnitTests/DelegateThreads_UT.cpp new file mode 100644 index 0000000..65cb53a --- /dev/null +++ b/tests/UnitTests/DelegateThreads_UT.cpp @@ -0,0 +1,292 @@ +#include "DelegateLib.h" +#include "UnitTestCommon.h" +#include +#include "WorkerThreadStd.h" +#include +#include + +using namespace DelegateLib; +using namespace std; +using namespace UnitTestData; + +static WorkerThread workerThread1("DelegateThreads1_UT"); +static WorkerThread workerThread2("DelegateThreads2_UT"); + +static std::mutex m_lock; +static const int LOOPS = 10; +static const int CNT_MAX = 7; +static int callerCnt[CNT_MAX] = { 0 }; + +static void Wait() +{ + // Wait for tests to complete. Test complete when thread queues empty. + while (workerThread1.GetQueueSize() != 0 && + workerThread2.GetQueueSize() != 0) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); +} + +static std::chrono::milliseconds getRandomTime() +{ + // Create a random number generator and a uniform distribution + std::random_device rd; // Non-deterministic random number generator + std::mt19937 gen(rd()); // Mersenne Twister engine initialized with rd + std::uniform_int_distribution<> dis(0, 10); // Uniform distribution between 0 and 10 + + // Generate a random number (between 0 and 10 milliseconds) + int random_ms = dis(gen); + + // Return the random number as a chrono::milliseconds object + return std::chrono::milliseconds(random_ms); +} + +static void FreeThreadSafe(std::chrono::milliseconds delay, int idx) +{ + const std::lock_guard lock(m_lock); + callerCnt[idx]++; + std::this_thread::sleep_for(delay); +} + +static std::function LambdaThreadSafe = [](std::chrono::milliseconds delay, int idx) +{ + const std::lock_guard lock(m_lock); + callerCnt[idx]++; + std::this_thread::sleep_for(delay); +}; + +class TestClass +{ +public: + void MemberThreadSafe(std::chrono::milliseconds delay, int idx) + { + const std::lock_guard lock(m_lock); + callerCnt[idx]++; + std::this_thread::sleep_for(delay); + } +}; + +static void FreeTests() +{ + std::memset(callerCnt, 0, sizeof(callerCnt)); + + auto delegateSync1 = MakeDelegate(&FreeThreadSafe); + auto delegateSync2 = MakeDelegate(&FreeThreadSafe); + auto delegateAsync1 = MakeDelegate(&FreeThreadSafe, workerThread1); + auto delegateAsync2 = MakeDelegate(&FreeThreadSafe, workerThread2); + auto delegateAsyncWait1 = MakeDelegate(&FreeThreadSafe, workerThread1, std::chrono::milliseconds(500)); + auto delegateAsyncWait2 = MakeDelegate(&FreeThreadSafe, workerThread1, std::chrono::milliseconds(500)); + + MulticastDelegateSafe container; + container += delegateSync1; + container += delegateSync2; + container += delegateAsync1; + container += delegateAsync2; + container += delegateAsyncWait1; + container += delegateAsyncWait2; + + int cnt = 0; + int cnt2 = 0; + while (cnt++ < LOOPS) + { + delegateSync1(getRandomTime(), 0); + delegateSync2(getRandomTime(), 1); + + auto retVal1 = delegateAsyncWait1.AsyncInvoke(getRandomTime(), 4); + ASSERT_TRUE(retVal1.has_value()); + auto retVal2 = delegateAsyncWait2.AsyncInvoke(getRandomTime(), 5); + ASSERT_TRUE(retVal2.has_value()); + + while (cnt2++ < 5) + { + delegateAsync1(getRandomTime(), 2); + delegateAsync2(getRandomTime(), 3); + } + cnt2 = 0; + container(getRandomTime(), 6); + } + + Wait(); + + ASSERT_TRUE(callerCnt[0] == LOOPS); + ASSERT_TRUE(callerCnt[1] == LOOPS); + ASSERT_TRUE(callerCnt[2] == LOOPS * 5); + ASSERT_TRUE(callerCnt[3] == LOOPS * 5); + ASSERT_TRUE(callerCnt[4] == LOOPS); + ASSERT_TRUE(callerCnt[5] == LOOPS); + ASSERT_TRUE(callerCnt[6] == LOOPS * 6); + std::cout << "FreeTests() complete!" << std::endl; +} + +static void MemberTests() +{ + std::memset(callerCnt, 0, sizeof(callerCnt)); + TestClass testClass; + + auto delegateSync1 = MakeDelegate(&testClass, &TestClass::MemberThreadSafe); + auto delegateSync2 = MakeDelegate(&testClass, &TestClass::MemberThreadSafe); + auto delegateAsync1 = MakeDelegate(&testClass, &TestClass::MemberThreadSafe, workerThread1); + auto delegateAsync2 = MakeDelegate(&testClass, &TestClass::MemberThreadSafe, workerThread2); + auto delegateAsyncWait1 = MakeDelegate(&testClass, &TestClass::MemberThreadSafe, workerThread1, std::chrono::milliseconds(500)); + auto delegateAsyncWait2 = MakeDelegate(&testClass, &TestClass::MemberThreadSafe, workerThread1, std::chrono::milliseconds(500)); + + MulticastDelegateSafe container; + container += delegateSync1; + container += delegateSync2; + container += delegateAsync1; + container += delegateAsync2; + container += delegateAsyncWait1; + container += delegateAsyncWait2; + + int cnt = 0; + int cnt2 = 0; + while (cnt++ < LOOPS) + { + delegateSync1(getRandomTime(), 0); + delegateSync2(getRandomTime(), 1); + + auto retVal1 = delegateAsyncWait1.AsyncInvoke(getRandomTime(), 4); + ASSERT_TRUE(retVal1.has_value()); + auto retVal2 = delegateAsyncWait2.AsyncInvoke(getRandomTime(), 5); + ASSERT_TRUE(retVal2.has_value()); + + while (cnt2++ < 5) + { + delegateAsync1(getRandomTime(), 2); + delegateAsync2(getRandomTime(), 3); + } + cnt2 = 0; + container(getRandomTime(), 6); + } + + Wait(); + + ASSERT_TRUE(callerCnt[0] == LOOPS); + ASSERT_TRUE(callerCnt[1] == LOOPS); + ASSERT_TRUE(callerCnt[2] == LOOPS * 5); + ASSERT_TRUE(callerCnt[3] == LOOPS * 5); + ASSERT_TRUE(callerCnt[4] == LOOPS); + ASSERT_TRUE(callerCnt[5] == LOOPS); + ASSERT_TRUE(callerCnt[6] == LOOPS * 6); + std::cout << "MemberTests() complete!" << std::endl; +} + +static void MemberSpTests() +{ + std::memset(callerCnt, 0, sizeof(callerCnt)); + auto testClass = std::make_shared(); + + auto delegateSync1 = MakeDelegate(testClass, &TestClass::MemberThreadSafe); + auto delegateSync2 = MakeDelegate(testClass, &TestClass::MemberThreadSafe); + auto delegateAsync1 = MakeDelegate(testClass, &TestClass::MemberThreadSafe, workerThread1); + auto delegateAsync2 = MakeDelegate(testClass, &TestClass::MemberThreadSafe, workerThread2); + auto delegateAsyncWait1 = MakeDelegate(testClass, &TestClass::MemberThreadSafe, workerThread1, std::chrono::milliseconds(500)); + auto delegateAsyncWait2 = MakeDelegate(testClass, &TestClass::MemberThreadSafe, workerThread1, std::chrono::milliseconds(500)); + + MulticastDelegateSafe container; + container += delegateSync1; + container += delegateSync2; + container += delegateAsync1; + container += delegateAsync2; + container += delegateAsyncWait1; + container += delegateAsyncWait2; + + int cnt = 0; + int cnt2 = 0; + while (cnt++ < LOOPS) + { + delegateSync1(getRandomTime(), 0); + delegateSync2(getRandomTime(), 1); + + auto retVal1 = delegateAsyncWait1.AsyncInvoke(getRandomTime(), 4); + ASSERT_TRUE(retVal1.has_value()); + auto retVal2 = delegateAsyncWait2.AsyncInvoke(getRandomTime(), 5); + ASSERT_TRUE(retVal2.has_value()); + + while (cnt2++ < 5) + { + delegateAsync1(getRandomTime(), 2); + delegateAsync2(getRandomTime(), 3); + } + cnt2 = 0; + container(getRandomTime(), 6); + } + + Wait(); + + ASSERT_TRUE(callerCnt[0] == LOOPS); + ASSERT_TRUE(callerCnt[1] == LOOPS); + ASSERT_TRUE(callerCnt[2] == LOOPS * 5); + ASSERT_TRUE(callerCnt[3] == LOOPS * 5); + ASSERT_TRUE(callerCnt[4] == LOOPS); + ASSERT_TRUE(callerCnt[5] == LOOPS); + ASSERT_TRUE(callerCnt[6] == LOOPS * 6); + std::cout << "MemberSpTests() complete!" << std::endl; +} + +static void FunctionTests() +{ + std::memset(callerCnt, 0, sizeof(callerCnt)); + + auto delegateSync1 = MakeDelegate(LambdaThreadSafe); + auto delegateSync2 = MakeDelegate(LambdaThreadSafe); + auto delegateAsync1 = MakeDelegate(LambdaThreadSafe, workerThread1); + auto delegateAsync2 = MakeDelegate(LambdaThreadSafe, workerThread2); + auto delegateAsyncWait1 = MakeDelegate(LambdaThreadSafe, workerThread1, std::chrono::milliseconds(500)); + auto delegateAsyncWait2 = MakeDelegate(LambdaThreadSafe, workerThread1, std::chrono::milliseconds(500)); + + MulticastDelegateSafe container; + container += delegateSync1; + container += delegateSync2; + container += delegateAsync1; + container += delegateAsync2; + container += delegateAsyncWait1; + container += delegateAsyncWait2; + + int cnt = 0; + int cnt2 = 0; + while (cnt++ < LOOPS) + { + delegateSync1(getRandomTime(), 0); + delegateSync2(getRandomTime(), 1); + + auto retVal1 = delegateAsyncWait1.AsyncInvoke(getRandomTime(), 4); + ASSERT_TRUE(retVal1.has_value()); + auto retVal2 = delegateAsyncWait2.AsyncInvoke(getRandomTime(), 5); + ASSERT_TRUE(retVal2.has_value()); + + while (cnt2++ < 5) + { + delegateAsync1(getRandomTime(), 2); + delegateAsync2(getRandomTime(), 3); + } + cnt2 = 0; + container(getRandomTime(), 6); + } + + Wait(); + + ASSERT_TRUE(callerCnt[0] == LOOPS); + ASSERT_TRUE(callerCnt[1] == LOOPS); + ASSERT_TRUE(callerCnt[2] == LOOPS * 5); + ASSERT_TRUE(callerCnt[3] == LOOPS * 5); + ASSERT_TRUE(callerCnt[4] == LOOPS); + ASSERT_TRUE(callerCnt[5] == LOOPS); + ASSERT_TRUE(callerCnt[6] == LOOPS * 6); + std::cout << "FunctionTests() complete!" << std::endl; +} + +void DelegateThreads_UT() +{ + workerThread1.CreateThread(); + workerThread2.CreateThread(); + + FreeTests(); + MemberTests(); + MemberSpTests(); + FunctionTests(); + + workerThread1.ExitThread(); + workerThread2.ExitThread(); +} \ No newline at end of file diff --git a/tests/UnitTests/DelegateUnitTests.cpp b/tests/UnitTests/DelegateUnitTests.cpp index 903dbd1..b85eaae 100644 --- a/tests/UnitTests/DelegateUnitTests.cpp +++ b/tests/UnitTests/DelegateUnitTests.cpp @@ -2575,12 +2575,21 @@ void DelegateMemberAsyncWaitTests() extern void Delegate_UT(); extern void DelegateAsync_UT(); extern void DelegateAsyncWait_UT(); +extern void DelegateThreads_UT(); void DelegateUnitTests() { - Delegate_UT(); - DelegateAsync_UT(); - DelegateAsyncWait_UT(); + try + { + Delegate_UT(); + DelegateAsync_UT(); + DelegateAsyncWait_UT(); + DelegateThreads_UT(); + } + catch (const std::exception& e) + { + std::cout << "Unit Tests Failed: " << e.what() << std::endl; + } testThread.CreateThread(); @@ -2613,7 +2622,6 @@ void DelegateUnitTests() std::cout << "Unit Tests Failed: " << e.what() << std::endl; } - #ifdef WIN32 QueryPerformanceCounter(&EndingTime); ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart; diff --git a/tests/UnitTests/Delegate_UT.cpp b/tests/UnitTests/Delegate_UT.cpp index 8960761..9bd94c0 100644 --- a/tests/UnitTests/Delegate_UT.cpp +++ b/tests/UnitTests/Delegate_UT.cpp @@ -2,9 +2,6 @@ #include "UnitTestCommon.h" #include #include "WorkerThreadStd.h" -#ifdef WIN32 -#include -#endif using namespace DelegateLib; using namespace std;