VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstCritSect.cpp@ 5606

Last change on this file since 5606 was 5525, checked in by vboxsync, 17 years ago

PDMUsb changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 17.5 KB
Line 
1/* $Id: tstCritSect.cpp 5525 2007-10-26 18:03:37Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime Testcase - Critical Sections.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#ifdef TRY_WIN32_CRIT
23# include <Windows.h>
24#endif
25#include <iprt/critsect.h>
26#include <iprt/lock.h>
27#include <iprt/thread.h>
28#include <iprt/log.h>
29#include <iprt/semaphore.h>
30#include <iprt/asm.h>
31#include <iprt/runtime.h>
32#include <iprt/time.h>
33#include <iprt/assert.h>
34#include <iprt/string.h>
35#include <iprt/err.h>
36
37#include <stdio.h>
38#include <stdlib.h>
39
40#ifndef TRY_WIN32_CRIT
41#define LOCKERS(sect) ((sect).cLockers)
42#else
43/* This is for comparing with the "real thing". */
44#define RTCRITSECT CRITICAL_SECTION
45#define PRTCRITSECT LPCRITICAL_SECTION
46#define LOCKERS(sect) (*(LONG volatile *)&(sect).LockCount)
47
48inline int RTCritSectInit(PCRITICAL_SECTION pCritSect)
49{
50 InitializeCriticalSection(pCritSect);
51 return VINF_SUCCESS;
52}
53
54#undef RTCritSectEnter
55inline int RTCritSectEnter(PCRITICAL_SECTION pCritSect)
56{
57 EnterCriticalSection(pCritSect);
58 return VINF_SUCCESS;
59}
60
61inline int RTCritSectLeave(PCRITICAL_SECTION pCritSect)
62{
63 LeaveCriticalSection(pCritSect);
64 return VINF_SUCCESS;
65}
66
67inline int RTCritSectDelete(PCRITICAL_SECTION pCritSect)
68{
69 DeleteCriticalSection(pCritSect);
70 return VINF_SUCCESS;
71}
72#endif
73
74/*******************************************************************************
75* Structures and Typedefs *
76*******************************************************************************/
77/**
78 * Arguments to ThreadTest1().
79 */
80typedef struct THREADTEST1ARGS
81{
82 /** The critical section. */
83 PRTCRITSECT pCritSect;
84 /** The thread ordinal. */
85 uint32_t iThread;
86 /** Pointer to the release counter. */
87 uint32_t volatile *pu32Release;
88} THREADTEST1ARGS, *PTHREADTEST1ARGS;
89
90
91/**
92 * Arguments to ThreadTest2().
93 */
94typedef struct THREADTEST2ARGS
95{
96 /** The critical section. */
97 PRTCRITSECT pCritSect;
98 /** The thread ordinal. */
99 uint32_t iThread;
100 /** Pointer to the release counter. */
101 uint32_t volatile *pu32Release;
102 /** Pointer to the alone indicator. */
103 uint32_t volatile *pu32Alone;
104 /** Pointer to the previous thread variable. */
105 uint32_t volatile *pu32Prev;
106 /** Pointer to the sequential enters counter. */
107 uint32_t volatile *pcSeq;
108 /** Pointer to the reordered enters counter. */
109 uint32_t volatile *pcReordered;
110 /** Pointer to the variable counting running threads. */
111 uint32_t volatile *pcThreadRunning;
112 /** Number of times this thread was inside the section. */
113 uint32_t volatile cTimes;
114 /** The number of threads. */
115 uint32_t cThreads;
116 /** Number of iterations (sum of all threads). */
117 uint32_t cIterations;
118 /** Yield while inside the section. */
119 unsigned cCheckLoops;
120 /** Signal this when done. */
121 RTSEMEVENT EventDone;
122} THREADTEST2ARGS, *PTHREADTEST2ARGS;
123
124
125/*******************************************************************************
126* Global Variables *
127*******************************************************************************/
128/** Error counter. */
129static volatile uint32_t g_cErrors = 0;
130
131/**
132 * Thread which goes to sleep on the critsect and checks that it's released in the right order.
133 */
134static DECLCALLBACK(int) ThreadTest1(RTTHREAD ThreadSelf, void *pvArg)
135{
136 PTHREADTEST1ARGS pArgs = (PTHREADTEST1ARGS)pvArg;
137 Log2(("ThreadTest1: Start - iThread=%d ThreadSelf=%p\n", pArgs->iThread, ThreadSelf));
138
139 /*
140 * Enter it.
141 */
142 int rc = RTCritSectEnter(pArgs->pCritSect);
143 if (RT_FAILURE(rc))
144 {
145 printf("tstCritSect: FATAL FAILURE - thread %d: RTCritSectEnter -> %d\n", pArgs->iThread, rc);
146 ASMAtomicIncU32(&g_cErrors);
147 exit(g_cErrors);
148 return 1;
149 }
150
151 /*
152 * Check release order.
153 */
154 if (*pArgs->pu32Release != pArgs->iThread)
155 {
156 printf("tstCritSect: FAILURE - thread %d: released as number %d\n", pArgs->iThread, *pArgs->pu32Release);
157 ASMAtomicIncU32(&g_cErrors);
158 }
159 ASMAtomicIncU32(pArgs->pu32Release);
160
161 /*
162 * Leave it.
163 */
164 rc = RTCritSectLeave(pArgs->pCritSect);
165 if (RT_FAILURE(rc))
166 {
167 printf("tstCritSect: FATAL FAILURE - thread %d: RTCritSectEnter -> %d\n", pArgs->iThread, rc);
168 ASMAtomicIncU32(&g_cErrors);
169 exit(g_cErrors);
170 return 1;
171 }
172
173 Log2(("ThreadTest1: End - iThread=%d ThreadSelf=%p\n", pArgs->iThread, ThreadSelf));
174 return 0;
175}
176
177
178int Test1(unsigned cThreads)
179{
180 /*
181 * Create a critical section.
182 */
183 RTCRITSECT CritSect;
184 int rc = RTCritSectInit(&CritSect);
185 if (RT_FAILURE(rc))
186 {
187 printf("tstCritSect: FATAL FAILURE - RTCritSectInit -> %d\n", rc);
188 return 1;
189 }
190
191 /*
192 * Enter, leave and enter again.
193 */
194 rc = RTCritSectEnter(&CritSect);
195 if (RT_FAILURE(rc))
196 {
197 printf("tstCritSect: FATAL FAILURE - RTCritSectEnter -> %d\n", rc);
198 return 1;
199 }
200 rc = RTCritSectLeave(&CritSect);
201 if (RT_FAILURE(rc))
202 {
203 printf("tstCritSect: FATAL FAILURE - RTCritSectLeave -> %d\n", rc);
204 return 1;
205 }
206 rc = RTCritSectEnter(&CritSect);
207 if (RT_FAILURE(rc))
208 {
209 printf("tstCritSect: FATAL FAILURE - RTCritSectEnter -> %d (2nd)\n", rc);
210 return 1;
211 }
212
213 /*
214 * Now spawn threads which will go to sleep entering the critsect.
215 */
216 uint32_t u32Release = 0;
217 for (uint32_t iThread = 0; iThread < cThreads; iThread++)
218 {
219 PTHREADTEST1ARGS pArgs = (PTHREADTEST1ARGS)calloc(sizeof(*pArgs), 1);
220 pArgs->iThread = iThread;
221 pArgs->pCritSect = &CritSect;
222 pArgs->pu32Release = &u32Release;
223 int32_t iLock = LOCKERS(CritSect);
224 char szThread[17];
225 RTStrPrintf(szThread, sizeof(szThread), "T%d", iThread);
226 RTTHREAD Thread;
227 rc = RTThreadCreate(&Thread, ThreadTest1, pArgs, 0, RTTHREADTYPE_DEFAULT, 0, szThread);
228 if (RT_FAILURE(rc))
229 {
230 printf("tstCritSect: FATAL FAILURE - RTThreadCreate -> %d\n", rc);
231 exit(1);
232 }
233 /* wait for it to get into waiting. */
234 while (LOCKERS(CritSect) == iLock)
235 RTThreadSleep(10);
236 RTThreadSleep(20);
237 }
238
239 /*
240 * Now we'll release the threads and wait for all of them to quit.
241 */
242 u32Release = 0;
243 rc = RTCritSectLeave(&CritSect);
244 if (RT_FAILURE(rc))
245 {
246 printf("tstCritSect: FATAL FAILURE - RTCritSectLeave -> %d (2nd)\n", rc);
247 return 1;
248 }
249 while (u32Release < cThreads)
250 RTThreadSleep(10);
251
252 rc = RTCritSectDelete(&CritSect);
253 if (RT_FAILURE(rc))
254 {
255 printf("tstCritSect: FAILURE - RTCritSectDelete -> %d\n", rc);
256 ASMAtomicIncU32(&g_cErrors);
257 }
258
259 return 0;
260}
261
262
263
264/**
265 * Thread which goes to sleep on the critsect and checks
266 * that it's released along and in the right order. This is done a number of times.
267 *
268 */
269static DECLCALLBACK(int) ThreadTest2(RTTHREAD ThreadSelf, void *pvArg)
270{
271 PTHREADTEST2ARGS pArgs = (PTHREADTEST2ARGS)pvArg;
272 Log2(("ThreadTest2: Start - iThread=%d ThreadSelf=%p\n", pArgs->iThread, ThreadSelf));
273 uint64_t u64TSStart = 0;
274 ASMAtomicIncU32(pArgs->pcThreadRunning);
275
276 for (unsigned i = 0; *pArgs->pu32Release < pArgs->cIterations; i++)
277 {
278 /*
279 * Enter it.
280 */
281 int rc = RTCritSectEnter(pArgs->pCritSect);
282 if (RT_FAILURE(rc))
283 {
284 printf("tstCritSect: FATAL FAILURE - Test 2 - thread %d, iteration %d: RTCritSectEnter -> %d\n", pArgs->iThread, i, rc);
285 ASMAtomicIncU32(&g_cErrors);
286 exit(g_cErrors);
287 return 1;
288 }
289 if (!u64TSStart)
290 u64TSStart = RTTimeNanoTS();
291
292 #if 0 /* We just check for sequences. */
293 /*
294 * Check release order.
295 */
296 if ((*pArgs->pu32Release % pArgs->cThreads) != pArgs->iThread)
297 {
298 printf("tstCritSect: FAILURE - Test 2 - thread %d, iteration %d: released as number %d (%d)\n",
299 pArgs->iThread, i, *pArgs->pu32Release % pArgs->cThreads, *pArgs->pu32Release);
300 ASMAtomicIncU32(&g_cErrors);
301 }
302 else
303 printf("tstCritSect: SUCCESS - Test 2 - thread %d, iteration %d: released as number %d (%d)\n",
304 pArgs->iThread, i, *pArgs->pu32Release % pArgs->cThreads, *pArgs->pu32Release);
305 #endif
306 pArgs->cTimes++;
307 ASMAtomicIncU32(pArgs->pu32Release);
308
309 /*
310 * Check distribution every now and again.
311 */
312#if 0
313 if (!(*pArgs->pu32Release % 879))
314 {
315 uint32_t u32Perfect = *pArgs->pu32Release / pArgs->cThreads;
316 for (int iThread = 0 ; iThread < (int)pArgs->cThreads; iThread++)
317 {
318 int cDiff = pArgs[iThread - pArgs->iThread].cTimes - u32Perfect;
319 if ((unsigned)RT_ABS(cDiff) > RT_MAX(u32Perfect / 10000, 2))
320 {
321 printf("tstCritSect: FAILURE - bad distribution thread %d u32Perfect=%d cTimes=%d cDiff=%d (runtime)\n",
322 iThread, u32Perfect, pArgs[iThread - pArgs->iThread].cTimes, cDiff);
323 ASMAtomicIncU32(&g_cErrors);
324 }
325 }
326 }
327#endif
328 /*
329 * Check alone and make sure we stay inside here a while
330 * so the other guys can get ready.
331 */
332 uint32_t u32;
333 for (u32 = 0; u32 < pArgs->cCheckLoops; u32++)
334 {
335 if (*pArgs->pu32Alone != ~0U)
336 {
337 printf("tstCritSect: FATAL FAILURE - Test 2 - thread %d, iteration %d: not alone!!!\n", pArgs->iThread, i);
338 AssertReleaseMsgFailed(("Not alone!\n"));
339 ASMAtomicIncU32(&g_cErrors);
340 exit(g_cErrors);
341 return 1;
342 }
343 }
344 ASMAtomicCmpXchgU32(pArgs->pu32Alone, pArgs->iThread, ~0);
345 for (u32 = 0; u32 < pArgs->cCheckLoops; u32++)
346 {
347 if (*pArgs->pu32Alone != pArgs->iThread)
348 {
349 printf("tstCritSect: FATAL FAILURE - Test 2 - thread %d, iteration %d: not alone!!!\n", pArgs->iThread, i);
350 AssertReleaseMsgFailed(("Not alone!\n"));
351 ASMAtomicIncU32(&g_cErrors);
352 exit(g_cErrors);
353 return 1;
354 }
355 }
356 ASMAtomicXchgU32(pArgs->pu32Alone, ~0);
357
358 /*
359 * Check for sequences.
360 */
361 if (*pArgs->pu32Prev == pArgs->iThread && pArgs->cThreads > 1)
362 ASMAtomicIncU32(pArgs->pcSeq);
363 else if ((*pArgs->pu32Prev + 1) % pArgs->cThreads != pArgs->iThread)
364 ASMAtomicIncU32(pArgs->pcReordered);
365 ASMAtomicXchgU32(pArgs->pu32Prev, pArgs->iThread);
366
367 /*
368 * Leave it.
369 */
370 rc = RTCritSectLeave(pArgs->pCritSect);
371 if (RT_FAILURE(rc))
372 {
373 printf("tstCritSect: FATAL FAILURE - Test 2 - thread %d, iteration %d: RTCritSectEnter -> %d\n", pArgs->iThread, i, rc);
374 ASMAtomicIncU32(&g_cErrors);
375 exit(g_cErrors);
376 return 1;
377 }
378 }
379
380 uint64_t u64TSEnd = RTTimeNanoTS(); NOREF(u64TSEnd);
381 ASMAtomicDecU32(pArgs->pcThreadRunning);
382 RTSemEventSignal(pArgs->EventDone);
383 Log2(("ThreadTest2: End - iThread=%d ThreadSelf=%p time=%lld\n", pArgs->iThread, ThreadSelf, u64TSEnd - u64TSStart));
384 return 0;
385}
386
387int Test2(unsigned cThreads, unsigned cIterations, unsigned cCheckLoops)
388{
389 printf("tstCritSect: Test2 - cThread=%d cIterations=%d cCheckLoops=%d...\n", cThreads, cIterations, cCheckLoops);
390
391 /*
392 * Create a critical section.
393 */
394 RTCRITSECT CritSect;
395 int rc = RTCritSectInit(&CritSect);
396 if (RT_FAILURE(rc))
397 {
398 printf("tstCritSect: FATAL FAILURE - Test 2 - RTCritSectInit -> %d\n", rc);
399 return 1;
400 }
401
402 /*
403 * Enter, leave and enter again.
404 */
405 rc = RTCritSectEnter(&CritSect);
406 if (RT_FAILURE(rc))
407 {
408 printf("tstCritSect: FATAL FAILURE - Test 2 - RTCritSectEnter -> %d\n", rc);
409 return 1;
410 }
411 rc = RTCritSectLeave(&CritSect);
412 if (RT_FAILURE(rc))
413 {
414 printf("tstCritSect: FATAL FAILURE - Test 2 - RTCritSectLeave -> %d\n", rc);
415 return 1;
416 }
417 rc = RTCritSectEnter(&CritSect);
418 if (RT_FAILURE(rc))
419 {
420 printf("tstCritSect: FATAL FAILURE - Test 2 - RTCritSectEnter -> %d (2nd)\n", rc);
421 return 1;
422 }
423
424 /*
425 * Now spawn threads which will go to sleep entering the critsect.
426 */
427 PTHREADTEST2ARGS paArgs = (PTHREADTEST2ARGS)calloc(sizeof(THREADTEST2ARGS), cThreads);
428 RTSEMEVENT EventDone;
429 rc = RTSemEventCreate(&EventDone);
430 uint32_t volatile u32Release = 0;
431 uint32_t volatile u32Alone = ~0;
432 uint32_t volatile u32Prev = ~0;
433 uint32_t volatile cSeq = 0;
434 uint32_t volatile cReordered = 0;
435 uint32_t volatile cThreadRunning = 0;
436 unsigned iThread;
437 for (iThread = 0; iThread < cThreads; iThread++)
438 {
439 paArgs[iThread].iThread = iThread;
440 paArgs[iThread].pCritSect = &CritSect;
441 paArgs[iThread].pu32Release = &u32Release;
442 paArgs[iThread].pu32Alone = &u32Alone;
443 paArgs[iThread].pu32Prev = &u32Prev;
444 paArgs[iThread].pcSeq = &cSeq;
445 paArgs[iThread].pcReordered = &cReordered;
446 paArgs[iThread].pcThreadRunning = &cThreadRunning;
447 paArgs[iThread].cTimes = 0;
448 paArgs[iThread].cThreads = cThreads;
449 paArgs[iThread].cIterations = cIterations;
450 paArgs[iThread].cCheckLoops = cCheckLoops;
451 paArgs[iThread].EventDone = EventDone;
452 int32_t iLock = LOCKERS(CritSect);
453 char szThread[17];
454 RTStrPrintf(szThread, sizeof(szThread), "T%d", iThread);
455 RTTHREAD Thread;
456 rc = RTThreadCreate(&Thread, ThreadTest2, &paArgs[iThread], 0, RTTHREADTYPE_DEFAULT, 0, szThread);
457 if (RT_FAILURE(rc))
458 {
459 printf("tstCritSect: FATAL FAILURE - Test 2 - RTThreadCreate -> %d\n", rc);
460 exit(1);
461 }
462 /* wait for it to get into waiting. */
463 while (LOCKERS(CritSect) == iLock)
464 RTThreadSleep(10);
465 RTThreadSleep(20);
466 }
467 printf("tstCritSect: Test2 - threads created...\n");
468
469 /*
470 * Now we'll release the threads and wait for all of them to quit.
471 */
472 u32Release = 0;
473 uint64_t u64TSStart = RTTimeNanoTS();
474 rc = RTCritSectLeave(&CritSect);
475 if (RT_FAILURE(rc))
476 {
477 printf("tstCritSect: FATAL FAILURE - RTCritSectLeave -> %d (2nd)\n", rc);
478 return 1;
479 }
480
481 while (cThreadRunning > 0)
482 RTSemEventWait(EventDone, RT_INDEFINITE_WAIT);
483 uint64_t u64TSEnd = RTTimeNanoTS();
484
485 /*
486 * Clean up and report results.
487 */
488 rc = RTCritSectDelete(&CritSect);
489 if (RT_FAILURE(rc))
490 {
491 printf("tstCritSect: FAILURE - RTCritSectDelete -> %d\n", rc);
492 ASMAtomicIncU32(&g_cErrors);
493 }
494
495 /* sequences */
496 if (cSeq > RT_MAX(u32Release / 10000, 1))
497 {
498 printf("tstCritSect: FAILURE - too many same thread sequences! cSeq=%d\n", cSeq);
499 ASMAtomicIncU32(&g_cErrors);
500 }
501
502 /* distribution caused by sequences / reordering. */
503 unsigned cDiffTotal = 0;
504 uint32_t u32Perfect = (u32Release + cThreads / 2) / cThreads;
505 for (iThread = 0; iThread < cThreads; iThread++)
506 {
507 int cDiff = paArgs[iThread].cTimes - u32Perfect;
508 if ((unsigned)RT_ABS(cDiff) > RT_MAX(u32Perfect / 10000, 2))
509 {
510 printf("tstCritSect: FAILURE - bad distribution thread %d u32Perfect=%d cTimes=%d cDiff=%d\n",
511 iThread, u32Perfect, paArgs[iThread].cTimes, cDiff);
512 ASMAtomicIncU32(&g_cErrors);
513 }
514 cDiffTotal += RT_ABS(cDiff);
515 }
516
517 uint32_t cMillies = (uint32_t)((u64TSEnd - u64TSStart) / 1000000);
518 printf("tstCritSect: Test2 - DONE. %d enter+leave in %dms cSeq=%d cReordered=%d cDiffTotal=%d\n",
519 u32Release, cMillies, cSeq, cReordered, cDiffTotal);
520 return 0;
521}
522
523
524int main(int argc, char *argv[])
525{
526 printf("tstCritSect: TESTING\n");
527
528 int rc = RTR3Init();
529 if (RT_FAILURE(rc))
530 {
531 printf("tstCritSect: FATAL FAILURE - RTR3Init -> %d\n", rc);
532 return 1;
533 }
534
535 printf("tstCritSect: Test1...\n");
536 if (Test1(1))
537 return 1;
538 if (Test1(3))
539 return 1;
540 if (Test1(10))
541 return 1;
542 if (Test1(63))
543 return 1;
544 if (Test2(1, 200000, 1000))
545 return 1;
546 if (Test2(2, 200000, 1000))
547 return 1;
548 if (Test2(3, 200000, 1000))
549 return 1;
550 if (Test2(4, 200000, 1000))
551 return 1;
552 if (Test2(5, 200000, 1000))
553 return 1;
554 if (Test2(7, 200000, 1000))
555 return 1;
556 if (Test2(67, 200000, 1000))
557 return 1;
558
559 /*
560 * Summary.
561 */
562 if (!g_cErrors)
563 printf("tstCritSect: SUCCESS\n");
564 else
565 printf("tstCritSect: FAILURE - %d errors\n", g_cErrors);
566
567 return !!g_cErrors;
568}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette