TestCallback.cpp 17 KB

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