VirtualBox

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

Last change on this file since 25776 was 25766, checked in by vboxsync, 15 years ago

build fix.

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