TestEnumerateThreads.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include <EATest/EATest.h>
  5. #include "TestThread.h"
  6. #include <eathread/eathread_thread.h>
  7. #include <eathread/eathread_semaphore.h>
  8. #include <eathread/eathread_sync.h>
  9. #include <eathread/eathread_atomic.h>
  10. EA_DISABLE_ALL_VC_WARNINGS()
  11. #include <string.h>
  12. EA_RESTORE_ALL_VC_WARNINGS()
  13. using namespace EA::Thread;
  14. using namespace EA::Thread::detail;
  15. using namespace EA::UnitTest;
  16. //-------------------------------------------------------------------------------
  17. // Globals
  18. static Semaphore gSemaphore;
  19. //-------------------------------------------------------------------------------
  20. //
  21. static intptr_t TestFunction1(void*)
  22. {
  23. // Wait until we are signaled by the unit test to complete.
  24. gSemaphore.Wait();
  25. return 0;
  26. }
  27. //-------------------------------------------------------------------------------
  28. //
  29. int TestSimpleEnumerateThreads()
  30. {
  31. int nErrorCount = 0;
  32. static EA::Thread::AtomicInt<size_t> snThreadStartCount;
  33. snThreadStartCount = 0;
  34. auto threadEntry = [](void*) -> intptr_t
  35. {
  36. snThreadStartCount++;
  37. // Wait until we are signaled by the unit test to complete.
  38. gSemaphore.Wait();
  39. return 0;
  40. };
  41. const size_t kMaxTestThreadEnumCount = 16;
  42. ThreadEnumData enumData[kMaxTestThreadEnumCount];
  43. // Prevents all threads from returning.
  44. gSemaphore.Init(0);
  45. // Startup all the threads we want to monitor.
  46. Thread threads[kMaxTestThreadEnumCount];
  47. for(size_t i = 0; i < kMaxTestThreadEnumCount; i++)
  48. {
  49. threads[i].Begin(threadEntry);
  50. }
  51. // Give all the threads a chance to start up.
  52. while(snThreadStartCount != kMaxTestThreadEnumCount)
  53. EA::Thread::ThreadSleep(0);
  54. // Enumerate the active threads
  55. size_t threadCount = EA::Thread::EnumerateThreads(enumData, EAArrayCount(enumData));
  56. EATEST_VERIFY_MSG(threadCount >= kMaxTestThreadEnumCount, "Incorrect number of threads reported.");
  57. // Report("Enumerated (at least) %d threads. Found (%d).\n", kMaxTestThreadEnumCount, threadCount);
  58. for(size_t j = 0; j < kMaxTestThreadEnumCount; j++)
  59. {
  60. //Report("\tThread id: %s\n", ThreadIdToStringBuffer(enumData[j].mpThreadDynamicData->mhThread).c_str());
  61. if(enumData[j].mpThreadDynamicData == NULL)
  62. continue;
  63. if(strcmp(enumData[j].mpThreadDynamicData->mName, "external") != 0) // Disabled because we can't guarantee across all platforms that a stack base is available. This will be fixed in a future release.
  64. {
  65. EATEST_VERIFY_MSG(enumData[j].mpThreadDynamicData->mpStackBase != NULL, "All thread meta data is expected to have the stack base address.");
  66. }
  67. enumData[j].Release();
  68. }
  69. // Signal the threads to complete.
  70. gSemaphore.Post(kMaxTestThreadEnumCount);
  71. // Wait for all threads to complete.
  72. for(size_t i = 0; i < kMaxTestThreadEnumCount; i++)
  73. {
  74. if(threads[i].GetStatus() != Thread::kStatusEnded)
  75. threads[i].WaitForEnd();
  76. }
  77. return nErrorCount;
  78. }
  79. //-------------------------------------------------------------------------------
  80. //
  81. int TestSimpleEnumerateThreads_KillThreadsEarly()
  82. {
  83. int nErrorCount = 0;
  84. const size_t kMaxTestThreadEnumCount = 16;
  85. ThreadEnumData enumData[kMaxTestThreadEnumCount];
  86. // Prevents all threads from returning.
  87. gSemaphore.Init(0);
  88. // Startup all the threads we want to monitor.
  89. Thread threads[kMaxTestThreadEnumCount];
  90. for(size_t i = 0; i < kMaxTestThreadEnumCount; i++)
  91. {
  92. threads[i].Begin(TestFunction1);
  93. }
  94. EA::Thread::ThreadSleep(300); // Give all the threads a chance to start up.
  95. // Enumerate the active threads
  96. size_t threadCount = EA::Thread::EnumerateThreads(enumData, EAArrayCount(enumData));
  97. EATEST_VERIFY_MSG(threadCount >= kMaxTestThreadEnumCount, "Incorrect number of threads reported.");
  98. // Report("Enumerated (at least) %d threads. Found (%d).\n", kMaxTestThreadEnumCount, threadCount);
  99. // Signal the threads to complete.
  100. gSemaphore.Post(kMaxTestThreadEnumCount);
  101. EA::Thread::ThreadSleep(500);
  102. // Terminate the threads before the user explicitly releases them.
  103. for(size_t i = 0; i < kMaxTestThreadEnumCount; i++)
  104. {
  105. if(threads[i].GetStatus() != Thread::kStatusEnded)
  106. threads[i].WaitForEnd();
  107. }
  108. for(size_t j = 0; j < kMaxTestThreadEnumCount; j++)
  109. {
  110. //Report("\tThread id: %s\n", ThreadIdToStringBuffer(enumData[j].mpThreadDynamicData->mhThread).c_str());
  111. enumData[j].Release();
  112. }
  113. return nErrorCount;
  114. }
  115. //-------------------------------------------------------------------------------
  116. //
  117. int TestEnumerateThreads_EnumerateMain()
  118. {
  119. int nErrorCount = 0;
  120. const size_t kMaxTestThreadEnumCount = 16;
  121. ThreadEnumData enumData[kMaxTestThreadEnumCount];
  122. size_t threadCount = EA::Thread::EnumerateThreads(enumData, EAArrayCount(enumData));
  123. EATEST_VERIFY_MSG(threadCount >= 1, "No threads found. We are expecting at least the main thread to be reported.");
  124. int compare_result = strcmp(enumData[0].mpThreadDynamicData->mName, "external");
  125. EATEST_VERIFY_MSG(compare_result == 0, "Not an externally created thread.");
  126. #if EA_USE_CPP11_CONCURRENCY
  127. ThreadUniqueId thisThreadId;
  128. EAThreadGetUniqueId(thisThreadId);
  129. ThreadUniqueId uniqueThreadId = enumData[0].mpThreadDynamicData->mUniqueThreadId;
  130. EATEST_VERIFY_MSG(uniqueThreadId == thisThreadId, "Did not return the threadId of this call context.");
  131. #elif defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE
  132. ThreadId enumThreadId = enumData[0].mpThreadDynamicData->mhThread; // No portable thread id available in the ThreadDynamicDataStructure.
  133. EATEST_VERIFY_MSG(enumThreadId == EA::Thread::GetThreadId(), "Did not return the threadId of this call context.");
  134. #else
  135. ThreadId enumThreadId = enumData[0].mpThreadDynamicData->mThreadId;
  136. EATEST_VERIFY_MSG(enumThreadId == EA::Thread::GetThreadId(), "Did not return the threadId of this call context.");
  137. #endif
  138. return nErrorCount;
  139. }
  140. //-------------------------------------------------------------------------------
  141. //
  142. EA_DISABLE_ALL_VC_WARNINGS()
  143. #include <thread>
  144. #include <chrono>
  145. #include <vector>
  146. #include <atomic>
  147. EA_RESTORE_ALL_VC_WARNINGS()
  148. #include <eathread/eathread_mutex.h>
  149. // TODO(rparolin): This forces the build-farm to timeout. Re-enable in the future.
  150. //
  151. // int TestHeavyLoadThreadRegisteration()
  152. // {
  153. // int nErrorCount = 0;
  154. // #ifdef EA_PLATFORM_MICROSOFT
  155. // // Only tested on Windows because its a reported regression in tools/pipeline related
  156. // // technologies leveraging a large number of non-eathread threads which would exhaust internal
  157. // // tracking system.
  158. // std::atomic<bool> isDone = false;
  159. // EA::Thread::Mutex s_mutex;
  160. // {
  161. // int loopCount = 170; // must exceed the value of EA::Thread::kMaxThreadDynamicDataCount.
  162. // std::vector<std::thread> threads;
  163. // while(loopCount--)
  164. // {
  165. // threads.emplace_back(std::thread([&]
  166. // {
  167. // while (!isDone)
  168. // {
  169. // // We lock an EA::Thread::Mutex because it used to force a
  170. // // non-EAThread thread to be registered due to debug functionality
  171. // // requesting a thread id. Verify that locking a mutex no longer requires
  172. // // external thread registration by locking more threads that we can track.
  173. // EA::Thread::AutoMutex _(s_mutex);
  174. // }
  175. // }));
  176. // }
  177. // std::this_thread::sleep_for(std::chrono::milliseconds(100));
  178. // isDone = true;
  179. // for (auto& th : threads)
  180. // th.join();
  181. // }
  182. // #endif
  183. // return nErrorCount;
  184. // }
  185. //-------------------------------------------------------------------------------
  186. //
  187. int TestEnumerateThreads()
  188. {
  189. int nErrorCount = 0;
  190. nErrorCount += TestSimpleEnumerateThreads();
  191. nErrorCount += TestSimpleEnumerateThreads_KillThreadsEarly();
  192. nErrorCount += TestEnumerateThreads_EnumerateMain();
  193. // nErrorCount += TestHeavyLoadThreadRegisteration();
  194. return nErrorCount;
  195. }