VirtualBox

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

Last change on this file since 19953 was 19953, checked in by vboxsync, 16 years ago

tstCritSect: Converted to RTTest and disable the distribution test by default (as it's busted for ages).

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