lock.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /*
  6. ** File: lock.c
  7. ** Purpose: test basic locking functions
  8. **
  9. ** Modification History:
  10. ** 14-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
  11. ** The debug mode will print all of the printfs associated with this test.
  12. ** The regress mode will be the default mode. Since the regress tool limits
  13. ** the output to a one line status:PASS or FAIL,all of the printf statements
  14. ** have been handled with an if (debug_mode) statement.
  15. ** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
  16. ** recognize the return code from tha main program.
  17. **
  18. ** 11-Aug-97 LarryH. Win16 port of NSPR.
  19. ** - Added "PASS", "FAIL" messages on completion.
  20. ** - Change stack variables to static scope variables
  21. ** because of shadow-stack use by Win16
  22. ** - Added PR_CALLBACK attribute to functions called by NSPR
  23. ** - Added command line arguments:
  24. ** - l <num> to control the number of loops
  25. ** - c <num> to control the number of CPUs.
  26. ** (was positional argv).
  27. **
  28. **
  29. ***********************************************************************/
  30. /***********************************************************************
  31. ** Includes
  32. ***********************************************************************/
  33. /* Used to get the command line option */
  34. #include "plgetopt.h"
  35. #include "prio.h"
  36. #include "prcmon.h"
  37. #include "prinit.h"
  38. #include "prinrval.h"
  39. #include "prprf.h"
  40. #include "prlock.h"
  41. #include "prlog.h"
  42. #include "prmon.h"
  43. #include "prmem.h"
  44. #include "prthread.h"
  45. #include "prtypes.h"
  46. #include "plstr.h"
  47. #include <stdlib.h>
  48. #if defined(XP_UNIX)
  49. #include <string.h>
  50. #endif
  51. static PRIntn failed_already=0;
  52. static PRFileDesc *std_err = NULL;
  53. static PRBool verbosity = PR_FALSE;
  54. static PRBool debug_mode = PR_FALSE;
  55. const static PRIntervalTime contention_interval = 50;
  56. typedef struct LockContentious_s {
  57. PRLock *ml;
  58. PRInt32 loops;
  59. PRUint32 contender;
  60. PRUint32 contentious;
  61. PRIntervalTime overhead;
  62. PRIntervalTime interval;
  63. } LockContentious_t;
  64. typedef struct MonitorContentious_s {
  65. PRMonitor *ml;
  66. PRInt32 loops;
  67. PRUint32 contender;
  68. PRUint32 contentious;
  69. PRIntervalTime overhead;
  70. PRIntervalTime interval;
  71. } MonitorContentious_t;
  72. static PRIntervalTime Sleeper(PRUint32 loops)
  73. {
  74. PRIntervalTime predicted = 0;
  75. while (loops-- > 0)
  76. {
  77. predicted += contention_interval;
  78. (void)PR_Sleep(contention_interval);
  79. }
  80. return predicted;
  81. } /* Sleeper */
  82. /*
  83. ** BASIC LOCKS
  84. */
  85. static PRIntervalTime MakeLock(PRUint32 loops)
  86. {
  87. PRLock *ml = NULL;
  88. while (loops-- > 0)
  89. {
  90. ml = PR_NewLock();
  91. PR_DestroyLock(ml);
  92. ml = NULL;
  93. }
  94. return 0;
  95. } /* MakeLock */
  96. static PRIntervalTime NonContentiousLock(PRUint32 loops)
  97. {
  98. PRLock *ml = NULL;
  99. ml = PR_NewLock();
  100. while (loops-- > 0)
  101. {
  102. PR_Lock(ml);
  103. PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(ml);
  104. PR_Unlock(ml);
  105. }
  106. PR_DestroyLock(ml);
  107. return 0;
  108. } /* NonContentiousLock */
  109. static void PR_CALLBACK LockContender(void *arg)
  110. {
  111. LockContentious_t *contention = (LockContentious_t*)arg;
  112. while (contention->loops-- > 0)
  113. {
  114. PR_Lock(contention->ml);
  115. PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
  116. contention->contender+= 1;
  117. contention->overhead += contention->interval;
  118. PR_Sleep(contention->interval);
  119. PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
  120. PR_Unlock(contention->ml);
  121. }
  122. } /* LockContender */
  123. static PRIntervalTime ContentiousLock(PRUint32 loops)
  124. {
  125. PRStatus status;
  126. PRThread *thread = NULL;
  127. LockContentious_t * contention;
  128. PRIntervalTime rv, overhead, timein = PR_IntervalNow();
  129. contention = PR_NEWZAP(LockContentious_t);
  130. contention->loops = loops;
  131. contention->overhead = 0;
  132. contention->ml = PR_NewLock();
  133. contention->interval = contention_interval;
  134. thread = PR_CreateThread(
  135. PR_USER_THREAD, LockContender, contention,
  136. PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
  137. PR_ASSERT(thread != NULL);
  138. overhead = PR_IntervalNow() - timein;
  139. while (contention->loops-- > 0)
  140. {
  141. PR_Lock(contention->ml);
  142. PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
  143. contention->contentious+= 1;
  144. contention->overhead += contention->interval;
  145. PR_Sleep(contention->interval);
  146. PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(contention->ml);
  147. PR_Unlock(contention->ml);
  148. }
  149. timein = PR_IntervalNow();
  150. status = PR_JoinThread(thread);
  151. PR_DestroyLock(contention->ml);
  152. overhead += (PR_IntervalNow() - timein);
  153. rv = overhead + contention->overhead;
  154. if (verbosity)
  155. PR_fprintf(
  156. std_err, "Access ratio: %u to %u\n",
  157. contention->contentious, contention->contender);
  158. PR_Free(contention);
  159. return rv;
  160. } /* ContentiousLock */
  161. /*
  162. ** MONITORS
  163. */
  164. static PRIntervalTime MakeMonitor(PRUint32 loops)
  165. {
  166. PRMonitor *ml = NULL;
  167. while (loops-- > 0)
  168. {
  169. ml = PR_NewMonitor();
  170. PR_DestroyMonitor(ml);
  171. ml = NULL;
  172. }
  173. return 0;
  174. } /* MakeMonitor */
  175. static PRIntervalTime NonContentiousMonitor(PRUint32 loops)
  176. {
  177. PRMonitor *ml = NULL;
  178. ml = PR_NewMonitor();
  179. while (loops-- > 0)
  180. {
  181. PR_EnterMonitor(ml);
  182. PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
  183. PR_ExitMonitor(ml);
  184. }
  185. PR_DestroyMonitor(ml);
  186. return 0;
  187. } /* NonContentiousMonitor */
  188. static void PR_CALLBACK TryEntry(void *arg)
  189. {
  190. PRMonitor *ml = (PRMonitor*)arg;
  191. if (debug_mode) {
  192. PR_fprintf(std_err, "Reentrant thread created\n");
  193. }
  194. PR_EnterMonitor(ml);
  195. PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
  196. if (debug_mode) {
  197. PR_fprintf(std_err, "Reentrant thread acquired monitor\n");
  198. }
  199. PR_ExitMonitor(ml);
  200. if (debug_mode) {
  201. PR_fprintf(std_err, "Reentrant thread released monitor\n");
  202. }
  203. } /* TryEntry */
  204. static PRIntervalTime ReentrantMonitor(PRUint32 loops)
  205. {
  206. PRStatus status;
  207. PRThread *thread;
  208. PRMonitor *ml = PR_NewMonitor();
  209. if (debug_mode) {
  210. PR_fprintf(std_err, "\nMonitor created for reentrant test\n");
  211. }
  212. PR_EnterMonitor(ml);
  213. PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
  214. PR_EnterMonitor(ml);
  215. PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
  216. if (debug_mode) {
  217. PR_fprintf(std_err, "Monitor acquired twice\n");
  218. }
  219. thread = PR_CreateThread(
  220. PR_USER_THREAD, TryEntry, ml,
  221. PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
  222. PR_ASSERT(thread != NULL);
  223. PR_Sleep(PR_SecondsToInterval(1));
  224. PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
  225. PR_ExitMonitor(ml);
  226. PR_ASSERT_CURRENT_THREAD_IN_MONITOR(ml);
  227. if (debug_mode) {
  228. PR_fprintf(std_err, "Monitor released first time\n");
  229. }
  230. PR_ExitMonitor(ml);
  231. if (debug_mode) {
  232. PR_fprintf(std_err, "Monitor released second time\n");
  233. }
  234. status = PR_JoinThread(thread);
  235. if (debug_mode) PR_fprintf(std_err,
  236. "Reentrant thread joined %s\n",
  237. (status == PR_SUCCESS) ? "successfully" : "in error");
  238. PR_DestroyMonitor(ml);
  239. return 0;
  240. } /* ReentrantMonitor */
  241. static void PR_CALLBACK MonitorContender(void *arg)
  242. {
  243. MonitorContentious_t *contention = (MonitorContentious_t*)arg;
  244. while (contention->loops-- > 0)
  245. {
  246. PR_EnterMonitor(contention->ml);
  247. PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
  248. contention->contender+= 1;
  249. contention->overhead += contention->interval;
  250. PR_Sleep(contention->interval);
  251. PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
  252. PR_ExitMonitor(contention->ml);
  253. }
  254. } /* MonitorContender */
  255. static PRUint32 ContentiousMonitor(PRUint32 loops)
  256. {
  257. PRStatus status;
  258. PRThread *thread = NULL;
  259. MonitorContentious_t * contention;
  260. PRIntervalTime rv, overhead, timein = PR_IntervalNow();
  261. contention = PR_NEWZAP(MonitorContentious_t);
  262. contention->loops = loops;
  263. contention->overhead = 0;
  264. contention->ml = PR_NewMonitor();
  265. contention->interval = contention_interval;
  266. thread = PR_CreateThread(
  267. PR_USER_THREAD, MonitorContender, contention,
  268. PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
  269. PR_ASSERT(thread != NULL);
  270. overhead = PR_IntervalNow() - timein;
  271. while (contention->loops-- > 0)
  272. {
  273. PR_EnterMonitor(contention->ml);
  274. PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
  275. contention->contentious+= 1;
  276. contention->overhead += contention->interval;
  277. PR_Sleep(contention->interval);
  278. PR_ASSERT_CURRENT_THREAD_IN_MONITOR(contention->ml);
  279. PR_ExitMonitor(contention->ml);
  280. }
  281. timein = PR_IntervalNow();
  282. status = PR_JoinThread(thread);
  283. PR_DestroyMonitor(contention->ml);
  284. overhead += (PR_IntervalNow() - timein);
  285. rv = overhead + contention->overhead;
  286. if (verbosity)
  287. PR_fprintf(
  288. std_err, "Access ratio: %u to %u\n",
  289. contention->contentious, contention->contender);
  290. PR_Free(contention);
  291. return rv;
  292. } /* ContentiousMonitor */
  293. /*
  294. ** CACHED MONITORS
  295. */
  296. static PRIntervalTime NonContentiousCMonitor(PRUint32 loops)
  297. {
  298. MonitorContentious_t contention;
  299. while (loops-- > 0)
  300. {
  301. PR_CEnterMonitor(&contention);
  302. PR_CExitMonitor(&contention);
  303. }
  304. return 0;
  305. } /* NonContentiousCMonitor */
  306. static void PR_CALLBACK Contender(void *arg)
  307. {
  308. MonitorContentious_t *contention = (MonitorContentious_t*)arg;
  309. while (contention->loops-- > 0)
  310. {
  311. PR_CEnterMonitor(contention);
  312. contention->contender+= 1;
  313. contention->overhead += contention->interval;
  314. PR_Sleep(contention->interval);
  315. PR_CExitMonitor(contention);
  316. }
  317. } /* Contender */
  318. static PRIntervalTime ContentiousCMonitor(PRUint32 loops)
  319. {
  320. PRStatus status;
  321. PRThread *thread = NULL;
  322. MonitorContentious_t * contention;
  323. PRIntervalTime overhead, timein = PR_IntervalNow();
  324. contention = PR_NEWZAP(MonitorContentious_t);
  325. contention->ml = NULL;
  326. contention->loops = loops;
  327. contention->interval = contention_interval;
  328. thread = PR_CreateThread(
  329. PR_USER_THREAD, Contender, contention,
  330. PR_PRIORITY_LOW, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
  331. PR_ASSERT(thread != NULL);
  332. overhead = PR_IntervalNow() - timein;
  333. while (contention->loops-- > 0)
  334. {
  335. PR_CEnterMonitor(contention);
  336. contention->contentious+= 1;
  337. contention->overhead += contention->interval;
  338. PR_Sleep(contention->interval);
  339. PR_CExitMonitor(contention);
  340. }
  341. timein = PR_IntervalNow();
  342. status = PR_JoinThread(thread);
  343. overhead += (PR_IntervalNow() - timein);
  344. overhead += overhead + contention->overhead;
  345. if (verbosity)
  346. PR_fprintf(
  347. std_err, "Access ratio: %u to %u\n",
  348. contention->contentious, contention->contender);
  349. PR_Free(contention);
  350. return overhead;
  351. } /* ContentiousCMonitor */
  352. static PRIntervalTime Test(
  353. const char* msg, PRUint32 (*test)(PRUint32 loops),
  354. PRUint32 loops, PRIntervalTime overhead)
  355. {
  356. /*
  357. * overhead - overhead not measured by the test.
  358. * duration - wall clock time it took to perform test.
  359. * predicted - extra time test says should not be counted
  360. *
  361. * Time accountable to the test is duration - overhead - predicted
  362. * All times are Intervals and accumulated for all iterations.
  363. */
  364. PRFloat64 elapsed;
  365. PRIntervalTime accountable, duration;
  366. PRUintn spaces = PL_strlen(msg);
  367. PRIntervalTime timeout, timein = PR_IntervalNow();
  368. PRIntervalTime predicted = test(loops);
  369. timeout = PR_IntervalNow();
  370. duration = timeout - timein;
  371. if (debug_mode)
  372. {
  373. accountable = duration - predicted;
  374. accountable -= overhead;
  375. elapsed = (PRFloat64)PR_IntervalToMicroseconds(accountable);
  376. PR_fprintf(PR_STDOUT, "%s:", msg);
  377. while (spaces++ < 50) {
  378. PR_fprintf(PR_STDOUT, " ");
  379. }
  380. if ((PRInt32)accountable < 0) {
  381. PR_fprintf(PR_STDOUT, "*****.** usecs/iteration\n");
  382. }
  383. else {
  384. PR_fprintf(PR_STDOUT, "%8.2f usecs/iteration\n", elapsed/loops);
  385. }
  386. }
  387. return duration;
  388. } /* Test */
  389. int main(int argc, char **argv)
  390. {
  391. PRBool rv = PR_TRUE;
  392. PRIntervalTime duration;
  393. PRUint32 cpu, cpus = 2, loops = 100;
  394. PR_STDIO_INIT();
  395. PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
  396. {
  397. /* The command line argument: -d is used to determine if the test is being run
  398. in debug mode. The regress tool requires only one line output:PASS or FAIL.
  399. All of the printfs associated with this test has been handled with a if (debug_mode)
  400. test.
  401. Command line argument -l <num> sets the number of loops.
  402. Command line argument -c <num> sets the number of cpus.
  403. Usage: lock [-d] [-l <num>] [-c <num>]
  404. */
  405. PLOptStatus os;
  406. PLOptState *opt = PL_CreateOptState(argc, argv, "dvl:c:");
  407. while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
  408. {
  409. if (PL_OPT_BAD == os) {
  410. continue;
  411. }
  412. switch (opt->option)
  413. {
  414. case 'd': /* debug mode */
  415. debug_mode = PR_TRUE;
  416. break;
  417. case 'v': /* debug mode */
  418. verbosity = PR_TRUE;
  419. break;
  420. case 'l': /* number of loops */
  421. loops = atoi(opt->value);
  422. break;
  423. case 'c': /* number of cpus */
  424. cpus = atoi(opt->value);
  425. break;
  426. default:
  427. break;
  428. }
  429. }
  430. PL_DestroyOptState(opt);
  431. }
  432. /* main test */
  433. PR_SetConcurrency(8);
  434. if (loops == 0) {
  435. loops = 100;
  436. }
  437. if (debug_mode)
  438. {
  439. std_err = PR_STDERR;
  440. PR_fprintf(std_err, "Lock: Using %d loops\n", loops);
  441. }
  442. if (cpus == 0) {
  443. cpus = 2;
  444. }
  445. if (debug_mode) {
  446. PR_fprintf(std_err, "Lock: Using %d cpu(s)\n", cpus);
  447. }
  448. (void)Sleeper(10); /* try filling in the caches */
  449. for (cpu = 1; cpu <= cpus; ++cpu)
  450. {
  451. if (debug_mode) {
  452. PR_fprintf(std_err, "\nLock: Using %d CPU(s)\n", cpu);
  453. }
  454. PR_SetConcurrency(cpu);
  455. duration = Test("Overhead of PR_Sleep", Sleeper, loops, 0);
  456. duration = 0;
  457. (void)Test("Lock creation/deletion", MakeLock, loops, 0);
  458. (void)Test("Lock non-contentious locking/unlocking", NonContentiousLock, loops, 0);
  459. (void)Test("Lock contentious locking/unlocking", ContentiousLock, loops, duration);
  460. (void)Test("Monitor creation/deletion", MakeMonitor, loops, 0);
  461. (void)Test("Monitor non-contentious locking/unlocking", NonContentiousMonitor, loops, 0);
  462. (void)Test("Monitor contentious locking/unlocking", ContentiousMonitor, loops, duration);
  463. (void)Test("Cached monitor non-contentious locking/unlocking", NonContentiousCMonitor, loops, 0);
  464. (void)Test("Cached monitor contentious locking/unlocking", ContentiousCMonitor, loops, duration);
  465. (void)ReentrantMonitor(loops);
  466. }
  467. if (debug_mode)
  468. PR_fprintf(
  469. std_err, "%s: test %s\n", "Lock(mutex) test",
  470. ((rv) ? "passed" : "failed"));
  471. else {
  472. if (!rv) {
  473. failed_already=1;
  474. }
  475. }
  476. if(failed_already)
  477. {
  478. PR_fprintf(PR_STDOUT, "FAIL\n");
  479. return 1;
  480. }
  481. else
  482. {
  483. PR_fprintf(PR_STDOUT, "PASS\n");
  484. return 0;
  485. }
  486. } /* main */
  487. /* testlock.c */