TestCallback.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include <EAStdC/internal/Config.h>
  5. #include <EAStdCTest/EAStdCTest.h>
  6. #include <EAStdC/EACallback.h>
  7. #include <EAStdC/EAStopwatch.h>
  8. #include <EAStdC/EARandom.h>
  9. #include <EAStdC/EARandomDistribution.h>
  10. #include <EATest/EATest.h>
  11. #include <eathread/eathread.h>
  12. #include <EAAssert/eaassert.h>
  13. #ifdef _MSC_VER
  14. #pragma warning(push, 0)
  15. #endif
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #ifdef _MSC_VER
  19. #pragma warning(pop)
  20. #endif
  21. #if defined(EA_PLATFORM_DESKTOP) // We need to test/debug this module in detail on machines before we enable the test formally for them. Given how threads work on other platforms this test or EACallback could fail on other platforms in async mode.
  22. #define EASTDC_EACALLBACK_TESTS_ENABLED 1
  23. #else
  24. #define EASTDC_EACALLBACK_TESTS_ENABLED 0
  25. #endif
  26. #if EASTDC_EACALLBACK_TESTS_ENABLED
  27. ///////////////////////////////////////////////////////////////////////////////
  28. // CallbackTest
  29. ///////////////////////////////////////////////////////////////////////////////
  30. class CallbackTest;
  31. class CallbackTest : public EA::StdC::CallbackT<CallbackTest>
  32. {
  33. public :
  34. CallbackTest();
  35. void Reset();
  36. float GetAccuracyRate() const;
  37. void CallbackFunction(EA::StdC::Callback* pCallback, uint64_t absoluteValue, uint64_t deltaValue);
  38. public:
  39. enum TestParams
  40. {
  41. kTestTicks = 100,
  42. kTestTime = 1000,
  43. kUserModeEventPulseCount = 20
  44. };
  45. uint32_t mIndex; // Which test we are in the array of concurrent tests.
  46. EA::StdC::Stopwatch mTestStopwatch; // Causes us to stop the callbacks after kTestTime has passed.
  47. EA::StdC::Stopwatch mStopwatch; // Used to make sure that the deltaValue argument matches the actual time delta since last called.
  48. uint64_t mNextUnitsMin; // The minimum time we should be next called back.
  49. uint64_t mNextUnitsMax; // The maximum time we should be next called back.
  50. uint64_t mLastUnits; // The last time we were called back.
  51. uint32_t mGoodSampleCount; // Total number of times our callback function was called and it happened within the time period expected.
  52. uint32_t mBadSampleCount; // Total number of times our callback function was called and it didn't happen within the time period expected.
  53. uint32_t mTotalSampleCount; // Total number of times our callback function was called. Equals mGoodSampleCount + mBadSampleCount.
  54. uint32_t mAddRefCount; // Number of times the AddRef message was sent.
  55. uint32_t mReleaseCount; // Number of times the Release message was sent.
  56. Mode mMode; // Used so we can tell how to validate the tests.
  57. };
  58. CallbackTest::CallbackTest()
  59. : mIndex(0),
  60. mTestStopwatch(EA::StdC::Stopwatch::kUnitsNanoseconds),
  61. mStopwatch(EA::StdC::Stopwatch::kUnitsNanoseconds),
  62. mNextUnitsMin(0),
  63. mNextUnitsMax(0),
  64. mLastUnits(0),
  65. mGoodSampleCount(0),
  66. mBadSampleCount(0),
  67. mTotalSampleCount(0),
  68. mAddRefCount(0),
  69. mReleaseCount(0),
  70. mMode(EA::StdC::Callback::kModeSync)
  71. {
  72. SetFunctionInfo(&CallbackTest::CallbackFunction, this, true);
  73. }
  74. void CallbackTest::Reset()
  75. {
  76. // Leave mIndex as-is.
  77. mNextUnitsMin = 0;
  78. mNextUnitsMax = 0;
  79. mTotalSampleCount = 0;
  80. mGoodSampleCount = 0;
  81. mBadSampleCount = 0;
  82. mLastUnits = 0;
  83. mAddRefCount = 0;
  84. mReleaseCount = 0;
  85. // Leave mMode as-is.
  86. }
  87. float CallbackTest::GetAccuracyRate() const
  88. {
  89. // Returns a value between 0.0 and 100.0.
  90. return (float)(100.0 * (mTotalSampleCount ? ((double)mGoodSampleCount / (double)mTotalSampleCount) : 1.0));
  91. }
  92. void CallbackTest::CallbackFunction(EA::StdC::Callback* /*pCallback*/, uint64_t absoluteValue, uint64_t deltaValue)
  93. {
  94. using namespace EA::StdC;
  95. if(absoluteValue == Callback::kMessageAddRef)
  96. mAddRefCount++;
  97. else if(absoluteValue == Callback::kMessageRelease)
  98. mReleaseCount++;
  99. else
  100. {
  101. const Callback::Type type = GetType();
  102. const uint64_t period = GetPeriod();
  103. const uint64_t precision = GetPrecision();
  104. if(mLastUnits == 0) // If this is the first time being called...
  105. mTestStopwatch.Restart();
  106. else
  107. {
  108. ++mTotalSampleCount;
  109. if((absoluteValue >= mNextUnitsMin) && (absoluteValue <= mNextUnitsMax))
  110. {
  111. if(type == Callback::kTypeTime)
  112. {
  113. const uint64_t elapsedTime = mStopwatch.GetElapsedTime();
  114. const uint64_t difference = (deltaValue > elapsedTime) ? (deltaValue - elapsedTime) : (elapsedTime - deltaValue);
  115. const double diffPercent = ((double)(int64_t)difference / (double)(int64_t)elapsedTime) * 100.0; // Cast to int64_t because some compilers/platforms can't handle int64_t to double conversions.
  116. if((mMode == Callback::kModeAsync) && (diffPercent < 10)) // In threaded mode there's more room for failure.
  117. ++mGoodSampleCount;
  118. else if((mMode == Callback::kModeSync) && (diffPercent < 3))
  119. ++mGoodSampleCount;
  120. else
  121. ++mBadSampleCount;
  122. }
  123. else // Else kTypeTick or kTypeUserEvent.
  124. ++mGoodSampleCount;
  125. }
  126. else
  127. {
  128. // It turns out that with kModeAsync (threaded), we can get a lot of these failures with kTypeUserEvent.
  129. // This is because the main thread is updating the user event counter (because it has to) while this
  130. // callback thread is running independently. It's possible for the main thread to bump up the user event
  131. // counter while this callback thread is in the process of issuing the next call. So timeNS could
  132. ++mBadSampleCount;
  133. }
  134. }
  135. mStopwatch.Restart();
  136. // Set the next expectation.
  137. mLastUnits = absoluteValue;
  138. mNextUnitsMin = ((absoluteValue + period) > precision) ? (absoluteValue + period - precision) : 0; // Extra logic here to make sure mNextUnitsMin doesn't go negative and wrap around to a very large uint64_t number.
  139. // How to decide the next max expected value is not simple because in async mode we have
  140. // two threads, one of which is updating the user event count and the other which is
  141. // servicing callbacks. The former could increase the event count by theoretically any
  142. // number while the servicing thread is in the middle of a single callback call.
  143. // We can fix this by having the main thread wait until the servicing thread is not
  144. // busy, or we can put some cap on the expected value. Currently we do the latter,
  145. // because it's simpler, but the former is probably better because it lets us test more precisely.
  146. if(type == Callback::kTypeUserEvent)
  147. mNextUnitsMax = absoluteValue + period + precision + (kUserModeEventPulseCount * 3);
  148. else
  149. mNextUnitsMax = absoluteValue + period + precision + (int)(1 + (double)period * 0.05f); // Add a little extra slop to account for CPU stalls and what-not.
  150. // See if the test is complete
  151. switch (type)
  152. {
  153. case Callback::kTypeTick:
  154. if(mTotalSampleCount >= kTestTicks)
  155. Stop();
  156. break;
  157. case Callback::kTypeUserEvent:
  158. if(mTotalSampleCount >= kTestTicks)
  159. Stop();
  160. break;
  161. case Callback::kTypeTime:
  162. {
  163. const uint64_t elapsedTime = mTestStopwatch.GetElapsedTime();
  164. if(elapsedTime > kTestTime)
  165. Stop();
  166. break;
  167. }
  168. }
  169. }
  170. }
  171. ///////////////////////////////////////////////////////////////////////////////
  172. // TestCallback
  173. ///////////////////////////////////////////////////////////////////////////////
  174. struct TestControlInfo
  175. {
  176. uint32_t mPeriodStart;
  177. uint32_t mPeriodEnd;
  178. uint32_t mPeriodStep;
  179. uint32_t mPrecisionStart;
  180. uint32_t mPrecisionEnd;
  181. uint32_t mPrecisionStep;
  182. EA::StdC::Callback::Type mType;
  183. uint32_t mTestTimeMs;
  184. };
  185. #if defined(EA_PLATFORM_DESKTOP)
  186. const TestControlInfo tci[] =
  187. {
  188. // Period Precision Type Test max time ms
  189. // -------------------------------- ------------------------------ ---------------------------------- ----------------
  190. { 1, 51, 50, 0, 1, 1, EA::StdC::Callback::kTypeTick, 10000 },
  191. { 1, 51, 50, 0, 1, 1, EA::StdC::Callback::kTypeUserEvent, 10000 },
  192. { 20000000, 50000000, 30000000, 15000000, 30000000, 15000000, EA::StdC::Callback::kTypeTime, 10000 }, // These are big numbers because time is in nanoseconds.
  193. { 100000000, 200000000, 100000000, 50000000, 100000000, 50000000, EA::StdC::Callback::kTypeTime, 10000 },
  194. { 1000000000, 2000000000, 1000000000, 200000000, 400000000, 200000000, EA::StdC::Callback::kTypeTime, 10000 }
  195. };
  196. #else
  197. const TestControlInfo tci[] =
  198. {
  199. // Period Precision Type Test max time ms
  200. // -------------------------------- ------------------------------ ---------------------------------- ----------------
  201. { 1, 5, 4, 1, 1, 1, EA::StdC::Callback::kTypeTick, 60000 },
  202. { 1, 5, 4, 1, 1, 1, EA::StdC::Callback::kTypeUserEvent, 60000 },
  203. { 50000000, 50000000, 1, 30000000, 30000000, 1, EA::StdC::Callback::kTypeTime, 60000 }, // These are big numbers because time is in nanoseconds.
  204. { 500000000, 500000000, 1, 200000000, 200000000, 1, EA::StdC::Callback::kTypeTime, 60000 }
  205. };
  206. #endif
  207. #endif // EASTDC_EACALLBACK_TESTS_ENABLED
  208. int TestCallback()
  209. {
  210. using namespace EA::StdC;
  211. EA::UnitTest::Report("TestCallback\n");
  212. int nErrorCount(0);
  213. #if EASTDC_EACALLBACK_TESTS_ENABLED
  214. EA::StdC::RandomFast random;
  215. EA::StdC::CallbackManager callbackManager;
  216. const uint32_t kCallbackCount = (uint32_t)(100 * EA::UnitTest::GetSystemSpeed(EA::UnitTest::kSpeedTypeCPU)); // Determines the number of callbacks in the callback tests.
  217. CallbackTest* pCallbackTestArray = new CallbackTest[kCallbackCount];
  218. EA::StdC::SetCallbackManager(&callbackManager);
  219. EA::UnitTest::ReportVerbosity(1, "Callback test using %u callbacks. The test shows how well (on average)\n"
  220. "the callback system is able to satisfy the demands of each callback object.\n", (unsigned)kCallbackCount);
  221. // For both kModeAsync kModeSync...
  222. for(int a = 0; a < 2; a++)
  223. {
  224. #if EASTDC_THREADING_SUPPORTED
  225. const Callback::Mode mode = ((a % 2) ? Callback::kModeAsync : Callback::kModeSync);
  226. #else
  227. const Callback::Mode mode = Callback::kModeSync;
  228. #endif
  229. callbackManager.Shutdown();
  230. switch(mode)
  231. {
  232. case Callback::kModeAsync:
  233. {
  234. callbackManager.Init(true, true); // Run in asynchronous (threaded) mode.
  235. break;
  236. }
  237. case Callback::kModeSync:
  238. default:
  239. {
  240. callbackManager.Init(false, false); // Run in synchronous (polled, non-threaded) mode.
  241. break;
  242. }
  243. }
  244. // For each type of TestControlInfo above...
  245. for(size_t cn = 0, n = EAArrayCount(tci); cn < n; ++cn)
  246. {
  247. const TestControlInfo& ci = tci[cn];
  248. for(uint32_t t = 0; t < kCallbackCount; ++t)
  249. {
  250. CallbackTest& callbackTest = pCallbackTestArray[t];
  251. callbackTest.mIndex = t;
  252. callbackTest.mMode = mode;
  253. callbackTest.SetType(ci.mType);
  254. }
  255. // For an array of periods between begin and end period...
  256. for(uint32_t period = ci.mPeriodStart; period <= ci.mPeriodEnd; period += ci.mPeriodStep)
  257. {
  258. // For an array of precisions between begin and end precision...
  259. for(uint32_t precision = ci.mPrecisionStart; precision <= ci.mPrecisionEnd; precision += ci.mPrecisionStep)
  260. {
  261. // print to avoid test timeout
  262. EA::UnitTest::ReportVerbosity(0, "%s", ".");
  263. EA::UnitTest::ReportVerbosity(1, "Callback test: mode = %s, type = %s, period = %i, precision = %i\n",
  264. (mode == Callback::kModeSync) ? "Sync" : "Async",
  265. (ci.mType == Callback::kTypeTick) ? "Tick" : ((ci.mType == Callback::kTypeTime) ? "Time" : "UserEvent"),
  266. period,
  267. precision);
  268. for(uint32_t t = 0; t < kCallbackCount; ++t)
  269. {
  270. CallbackTest& callbackTest = pCallbackTestArray[t];
  271. callbackTest.SetPeriod(period);
  272. callbackTest.SetPrecision(precision);
  273. callbackTest.Start(NULL, false);
  274. }
  275. // Run the test for N seconds, during which our callback function will be called
  276. // repeatedly. After the callback function has been called for an amount
  277. // of kTestTicks or kTestTime, the callback function calls CallbackTimer::Stop.
  278. bool bCallbacksAreStillRunning = true;
  279. bool bShouldContinue = true;
  280. EA::Thread::ThreadTime endTime = EA::Thread::GetThreadTime() + ci.mTestTimeMs;
  281. uint64_t loopCount; // This is just to help debugging.
  282. for(loopCount = 0; bShouldContinue && bCallbacksAreStillRunning && (loopCount < UINT64_C(0x0fffffffffffffff)); ++loopCount)
  283. {
  284. bCallbacksAreStillRunning = false;
  285. // Randomly trigger a user event.
  286. if(EA::StdC::RandomBool(random))
  287. {
  288. for(int e = 0; e < CallbackTest::kUserModeEventPulseCount; e++) // Trigger multiple events at once.
  289. callbackManager.OnUserEvent();
  290. }
  291. // If we are in synchronous (i.e. polled) mode, then we need to manually call Update.
  292. // We don't need to call Update in synchronous (threaded) mode, but it's supported so
  293. // we test calling Update occasionally to make sure it works.
  294. switch(mode)
  295. {
  296. case Callback::kModeSync:
  297. {
  298. callbackManager.Update();
  299. break;
  300. }
  301. case Callback::kModeAsync:
  302. default:
  303. {
  304. if(random.RandomUint32Uniform(100) == 0)
  305. {
  306. callbackManager.Update();
  307. }
  308. break;
  309. }
  310. }
  311. // Sleep for a little bit.
  312. EA::Thread::ThreadTime sleepTime = EA::Thread::kTimeoutImmediate;
  313. #if EASTDC_THREADING_SUPPORTED
  314. // Since we are in a seperate thread from the callback manager and sicne we are updating On UserEvent
  315. // ourselves here, it might be useful to sleep here to handle the case that our OnUserEvent calls get
  316. // ahead of what the callback manager can keep up with and "CallbackTest low accuracy rate" reports
  317. // can result, though these reports wouldn't be indicative of bugs or failures of EACallback.
  318. if((mode == Callback::kModeAsync) && (ci.mType != EA::StdC::Callback::kTypeTime))
  319. sleepTime = EA::Thread::ThreadTime(10);
  320. #endif
  321. EA::Thread::ThreadSleep(sleepTime);
  322. // See if the alotted time has passed.
  323. EA::Thread::ThreadTime currentTime = EA::Thread::GetThreadTime();
  324. bShouldContinue = (currentTime < endTime);
  325. // See if the callbacks have all been called the number of times we expected and are thus done now.
  326. for(uint32_t t = 0; (t < kCallbackCount) && !bCallbacksAreStillRunning; ++t)
  327. {
  328. const CallbackTest& callbackTest = pCallbackTestArray[t];
  329. if(callbackTest.IsRunning())
  330. {
  331. bCallbacksAreStillRunning = true;
  332. if(!bShouldContinue) // If the time is up, yet this callback is still running...
  333. {
  334. // The callback function didn't get called the expected number of times before our loop time above expired.
  335. //++nErrorCount; Hard to enable this because it's easy for this test to have some failures. Probably what we should do is count failures and expect some success rate.
  336. EA::UnitTest::ReportVerbosity(1, "CallbackTest timeout. The callback didn't fire the expected number of\n"
  337. "times during our test period. Expected: %u, Actual: %u\n",
  338. (callbackTest.GetType() == Callback::kTypeTime) ? (unsigned)CallbackTest::kTestTicks : (unsigned)CallbackTest::kTestTime, (unsigned)callbackTest.mTotalSampleCount);
  339. }
  340. }
  341. }
  342. }
  343. uint32_t successSum = 0;
  344. for(uint32_t t = 0; t < kCallbackCount; ++t)
  345. {
  346. CallbackTest& callbackTest = pCallbackTestArray[t];
  347. successSum += (uint32_t)callbackTest.GetAccuracyRate(); // GetAccuracyRate returns a value in the range of [0, 100]
  348. callbackTest.Reset();
  349. }
  350. const uint32_t successRate = (successSum / kCallbackCount); // successRate is a value in the range of [0, 100].
  351. if(successRate < 90) // If less than 90% of the callbacks occurred within the expected time...
  352. {
  353. // ++nErrorCount; Hard to enable this because it's easy for this test to have some failures. Probably what we should do is count failures and expect some success rate.
  354. EA::UnitTest::ReportVerbosity(1, "CallbackTest low accuracy rate of %i%%.\n", successRate);
  355. }
  356. }
  357. }
  358. }
  359. }
  360. delete[] pCallbackTestArray;
  361. #endif // EA_PLATFORM_DESKTOP
  362. // to avoid test timeout
  363. EA::EAMain::ReportVerbosity(0, "%s", "\n");
  364. return nErrorCount;
  365. }