VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstRTCritSect.cpp@ 41661

Last change on this file since 41661 was 36492, checked in by vboxsync, 14 years ago

iprt: CRITSECT_WITHOUT_REMAPPING -> RTCRITSECT_WITHOUT_REMAPPING.

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