VirtualBox

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

Last change on this file since 102335 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

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