VirtualBox

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

Last change on this file since 62564 was 62477, checked in by vboxsync, 8 years ago

(C) 2016

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