VirtualBox

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

Last change on this file since 28267 was 26517, checked in by vboxsync, 15 years ago

*: RTGetOpt cleanup related to --help and --version (now standard option). Use RTGetOptPrintError.

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