VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstRTLockValidator.cpp@ 25618

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

IPRT,pdmcritsect: More lock validator hacking.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 16.0 KB
Line 
1/* $Id: tstRTLockValidator.cpp 25618 2010-01-02 12:00:33Z vboxsync $ */
2/** @file
3 * IPRT Testcase - RTLockValidator.
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/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#include <iprt/lockvalidator.h>
36
37#include <iprt/asm.h> /* for return addresses */
38#include <iprt/critsect.h>
39#include <iprt/err.h>
40#include <iprt/semaphore.h>
41#include <iprt/test.h>
42#include <iprt/thread.h>
43#include <iprt/time.h>
44
45
46/*******************************************************************************
47* Global Variables *
48*******************************************************************************/
49/** The testcase handle. */
50static RTTEST g_hTest;
51/** Flip this in the debugger to get some peace to single step wild code. */
52bool volatile g_fDoNotSpin = false;
53
54static uint32_t g_cThreads;
55static uint32_t volatile g_iDeadlockThread;
56static RTTHREAD g_ahThreads[32];
57static RTCRITSECT g_aCritSects[32];
58static RTSEMRW g_ahSemRWs[32];
59
60/** When to stop testing. */
61static uint64_t g_NanoTSStop;
62/** The number of deadlocks. */
63static uint32_t volatile g_cDeadlocks;
64/** The number of loops. */
65static uint32_t volatile g_cLoops;
66
67
68/**
69 * Spin until someone else has taken ownership of the critical section.
70 *
71 * @returns true on success, false on abort.
72 * @param pCritSect The critical section.
73 */
74static bool testWaitForCritSectToBeOwned(PRTCRITSECT pCritSect)
75{
76 unsigned iLoop = 0;
77 while (!RTCritSectIsOwned(pCritSect))
78 {
79 if (!RTCritSectIsInitialized(pCritSect))
80 return false;
81 RTThreadSleep(g_fDoNotSpin ? 3600*1000 : iLoop > 256 ? 1 : 0);
82 iLoop++;
83 }
84 return true;
85}
86
87
88/**
89 * Spin until someone else has taken ownership (any kind) of the read-write
90 * semaphore.
91 *
92 * @returns true on success, false on abort.
93 * @param hSemRW The read-write semaphore.
94 */
95static bool testWaitForSemRWToBeOwned(RTSEMRW hSemRW)
96{
97 RTTEST_CHECK(g_hTest, RTThreadGetState(RTThreadSelf()) == RTTHREADSTATE_RUNNING);
98 unsigned iLoop = 0;
99 for (;;)
100 {
101 if (RTSemRWGetWriteRecursion(hSemRW) > 0)
102 return true;
103 if (RTSemRWGetReadCount(hSemRW) > 0)
104 return true;
105 RTThreadSleep(g_fDoNotSpin ? 3600*1000 : iLoop > 256 ? 1 : 0);
106 iLoop++;
107 }
108 return true;
109}
110
111
112/**
113 * Waits for a thread to enter a sleeping state.
114 *
115 * @returns true on success, false on abort.
116 * @param hThread The thread.
117 * @param enmDesiredState The desired thread sleep state.
118 * @param pvLock The lock it should be sleeping on.
119 */
120static bool testWaitForThreadToSleep(RTTHREAD hThread, RTTHREADSTATE enmDesiredState, void *pvLock)
121{
122 RTTEST_CHECK(g_hTest, RTThreadGetState(RTThreadSelf()) == RTTHREADSTATE_RUNNING);
123 for (unsigned iLoop = 0; ; iLoop++)
124 {
125 RTTHREADSTATE enmState = RTThreadGetState(hThread);
126 if (RTTHREAD_IS_SLEEPING(enmState))
127 {
128 if ( enmState == enmDesiredState
129 && ( !pvLock
130 || pvLock == RTLockValidatorQueryBlocking(hThread)))
131 return true;
132 }
133 else if (enmState != RTTHREADSTATE_RUNNING)
134 return false;
135 RTThreadSleep(g_fDoNotSpin ? 3600*1000 : iLoop > 256 ? 1 : 0);
136 }
137}
138
139
140/**
141 * Waits for all the other threads to enter sleeping states.
142 *
143 * @returns VINF_SUCCESS on success, VERR_INTERNAL_ERROR on failure.
144 * @param enmDesiredState The desired thread sleep state.
145 * @param cWaitOn The distance to the lock they'll be waiting on,
146 * the lock type is derived from the desired state.
147 * UINT32_MAX means no special lock.
148 */
149static int testWaitForAllOtherThreadsToSleep(RTTHREADSTATE enmDesiredState, uint32_t cWaitOn)
150{
151 RTTHREAD hThreadSelf = RTThreadSelf();
152 for (uint32_t i = 0; i < g_cThreads; i++)
153 {
154 RTTHREAD hThread = g_ahThreads[i];
155 if ( hThread != NIL_RTTHREAD
156 && hThread != hThreadSelf)
157 {
158 void *pvLock = NULL;
159 if (cWaitOn != UINT32_MAX)
160 {
161 uint32_t j = (i + cWaitOn) % g_cThreads;
162 switch (enmDesiredState)
163 {
164 case RTTHREADSTATE_CRITSECT: pvLock = &g_aCritSects[j]; break;
165 case RTTHREADSTATE_RW_WRITE:
166 case RTTHREADSTATE_RW_READ: pvLock = g_ahSemRWs[j]; break;
167 default: break;
168 }
169 }
170 bool fRet = testWaitForThreadToSleep(hThread, enmDesiredState, pvLock);
171 if (!fRet)
172 return VERR_INTERNAL_ERROR;
173 }
174 }
175 return VINF_SUCCESS;
176}
177
178
179/**
180 * Worker that starts the threads.
181 *
182 * @returns Same as RTThreadCreate.
183 * @param cThreads The number of threads to start.
184 * @param pfnThread Thread function.
185 */
186static int testStartThreads(uint32_t cThreads, PFNRTTHREAD pfnThread)
187{
188 uint32_t i;
189 for (i = 0; i < RT_ELEMENTS(g_ahThreads); i++)
190 g_ahThreads[i] = NIL_RTTHREAD;
191
192 for (i = 0; i < cThreads; i++)
193 RTTEST_CHECK_RC_OK_RET(g_hTest,
194 RTThreadCreateF(&g_ahThreads[i], pfnThread, (void *)(uintptr_t)i, 0,
195 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "thread-%02u", i),
196 rcCheck);
197 return VINF_SUCCESS;
198}
199
200
201/**
202 * Worker that waits for the threads to complete.
203 *
204 * @param cMillies How long to wait for each.
205 * @param fStopOnError Whether to stop on error and heed the thread
206 * return status.
207 */
208static void testWaitForThreads(uint32_t cMillies, bool fStopOnError)
209{
210 uint32_t i = RT_ELEMENTS(g_ahThreads);
211 while (i-- > 0)
212 if (g_ahThreads[i] != NIL_RTTHREAD)
213 {
214 int rcThread;
215 int rc2;
216 RTTEST_CHECK_RC_OK(g_hTest, rc2 = RTThreadWait(g_ahThreads[i], cMillies, &rcThread));
217 if (RT_SUCCESS(rc2))
218 g_ahThreads[i] = NIL_RTTHREAD;
219 if (fStopOnError && (RT_FAILURE(rc2) || RT_FAILURE(rcThread)))
220 return;
221 }
222}
223
224
225static DECLCALLBACK(int) test1Thread(RTTHREAD ThreadSelf, void *pvUser)
226{
227 uintptr_t i = (uintptr_t)pvUser;
228 PRTCRITSECT pMine = &g_aCritSects[i];
229 PRTCRITSECT pNext = &g_aCritSects[(i + 1) % g_cThreads];
230
231 RTTEST_CHECK_RC_RET(g_hTest, RTCritSectEnter(pMine), VINF_SUCCESS, rcCheck);
232 if (testWaitForCritSectToBeOwned(pNext))
233 {
234 int rc;
235 if (i != g_iDeadlockThread)
236 RTTEST_CHECK_RC(g_hTest, rc = RTCritSectEnter(pNext), VINF_SUCCESS);
237 else
238 {
239 RTTEST_CHECK_RC_OK(g_hTest, rc = testWaitForAllOtherThreadsToSleep(RTTHREADSTATE_CRITSECT, 1));
240 if (RT_SUCCESS(rc))
241 RTTEST_CHECK_RC(g_hTest, rc = RTCritSectEnter(pNext), VERR_SEM_LV_DEADLOCK);
242 }
243 RTTEST_CHECK(g_hTest, RTThreadGetState(RTThreadSelf()) == RTTHREADSTATE_RUNNING);
244 if (RT_SUCCESS(rc))
245 RTTEST_CHECK_RC(g_hTest, rc = RTCritSectLeave(pNext), VINF_SUCCESS);
246 RTTEST_CHECK_RC(g_hTest, RTCritSectLeave(pMine), VINF_SUCCESS);
247 }
248 return VINF_SUCCESS;
249}
250
251
252static DECLCALLBACK(int) test2Thread(RTTHREAD ThreadSelf, void *pvUser)
253{
254 uintptr_t i = (uintptr_t)pvUser;
255 RTSEMRW hMine = g_ahSemRWs[i];
256 RTSEMRW hNext = g_ahSemRWs[(i + 1) % g_cThreads];
257 int rc;
258
259 if (i & 1)
260 RTTEST_CHECK_RC_RET(g_hTest, RTSemRWRequestWrite(hMine, RT_INDEFINITE_WAIT), VINF_SUCCESS, rcCheck);
261 else
262 RTTEST_CHECK_RC_RET(g_hTest, RTSemRWRequestRead(hMine, RT_INDEFINITE_WAIT), VINF_SUCCESS, rcCheck);
263 if (testWaitForSemRWToBeOwned(hNext))
264 {
265 if (i != g_iDeadlockThread)
266 RTTEST_CHECK_RC(g_hTest, rc = RTSemRWRequestWrite(hNext, RT_INDEFINITE_WAIT), VINF_SUCCESS);
267 else
268 {
269 RTTEST_CHECK_RC_OK(g_hTest, rc = testWaitForAllOtherThreadsToSleep(RTTHREADSTATE_RW_WRITE, 1));
270 if (RT_SUCCESS(rc))
271 {
272 if (g_cThreads > 1)
273 RTTEST_CHECK_RC(g_hTest, rc = RTSemRWRequestWrite(hNext, RT_INDEFINITE_WAIT), VERR_SEM_LV_DEADLOCK);
274 else
275 RTTEST_CHECK_RC(g_hTest, rc = RTSemRWRequestWrite(hNext, RT_INDEFINITE_WAIT), VERR_SEM_LV_ILLEGAL_UPGRADE);
276 }
277 }
278 RTTEST_CHECK(g_hTest, RTThreadGetState(RTThreadSelf()) == RTTHREADSTATE_RUNNING);
279 if (RT_SUCCESS(rc))
280 RTTEST_CHECK_RC(g_hTest, RTSemRWReleaseWrite(hNext), VINF_SUCCESS);
281 }
282 if (i & 1)
283 RTTEST_CHECK_RC(g_hTest, RTSemRWReleaseWrite(hMine), VINF_SUCCESS);
284 else
285 RTTEST_CHECK_RC(g_hTest, RTSemRWReleaseRead(hMine), VINF_SUCCESS);
286 RTTEST_CHECK(g_hTest, RTThreadGetState(RTThreadSelf()) == RTTHREADSTATE_RUNNING);
287 return VINF_SUCCESS;
288}
289
290
291static DECLCALLBACK(int) test3Thread(RTTHREAD ThreadSelf, void *pvUser)
292{
293 uintptr_t i = (uintptr_t)pvUser;
294 RTSEMRW hMine = g_ahSemRWs[i];
295 RTSEMRW hNext = g_ahSemRWs[(i + 1) % g_cThreads];
296 int rc;
297
298 if (i & 1)
299 RTTEST_CHECK_RC_RET(g_hTest, RTSemRWRequestWrite(hMine, RT_INDEFINITE_WAIT), VINF_SUCCESS, rcCheck);
300 else
301 RTTEST_CHECK_RC_RET(g_hTest, RTSemRWRequestRead(hMine, RT_INDEFINITE_WAIT), VINF_SUCCESS, rcCheck);
302 if (testWaitForSemRWToBeOwned(hNext))
303 {
304 do
305 {
306 rc = RTSemRWRequestWrite(hNext, 60*1000);
307 if (rc != VINF_SUCCESS && rc != VERR_SEM_LV_DEADLOCK && rc != VERR_SEM_LV_ILLEGAL_UPGRADE)
308 {
309 RTTestFailed(g_hTest, "#%u: RTSemRWRequestWrite -> %Rrc\n", i, rc);
310 break;
311 }
312 if (RT_SUCCESS(rc))
313 {
314 RTTEST_CHECK_RC(g_hTest, rc = RTSemRWReleaseWrite(hNext), VINF_SUCCESS);
315 if (RT_FAILURE(rc))
316 break;
317 }
318 else
319 ASMAtomicIncU32(&g_cDeadlocks);
320 ASMAtomicIncU32(&g_cLoops);
321 } while (RTTimeNanoTS() < g_NanoTSStop);
322 }
323 if (i & 1)
324 RTTEST_CHECK_RC(g_hTest, RTSemRWReleaseWrite(hMine), VINF_SUCCESS);
325 else
326 RTTEST_CHECK_RC(g_hTest, RTSemRWReleaseRead(hMine), VINF_SUCCESS);
327 RTTEST_CHECK(g_hTest, RTThreadGetState(RTThreadSelf()) == RTTHREADSTATE_RUNNING);
328 return VINF_SUCCESS;
329}
330
331
332static void testIt(uint32_t cThreads, uint32_t cPasses, uint64_t cNanoSecs, PFNRTTHREAD pfnThread, const char *pszName)
333{
334 RTTestSubF(g_hTest, "%s, %u threads, %u passes", pszName, cThreads, cPasses);
335
336 RTTEST_CHECK_RETV(g_hTest, RT_ELEMENTS(g_ahThreads) >= cThreads);
337 RTTEST_CHECK_RETV(g_hTest, RT_ELEMENTS(g_aCritSects) >= cThreads);
338
339 g_cThreads = cThreads;
340 g_iDeadlockThread = cThreads - 1;
341
342 for (uint32_t i = 0; i < cThreads; i++)
343 {
344 RTTEST_CHECK_RC_RETV(g_hTest, RTCritSectInit(&g_aCritSects[i]), VINF_SUCCESS);
345 RTTEST_CHECK_RC_RETV(g_hTest, RTSemRWCreate(&g_ahSemRWs[i]), VINF_SUCCESS);
346 }
347
348 uint32_t cLoops = 0;
349 uint32_t cDeadlocks = 0;
350 uint32_t cErrors = RTTestErrorCount(g_hTest);
351 for (uint32_t iPass = 0; iPass < cPasses && RTTestErrorCount(g_hTest) == cErrors; iPass++)
352 {
353#if 0 /** @todo figure why this ain't working for either of the two tests! */
354 g_iDeadlockThread = (cThreads - 1 + iPass) % cThreads;
355#endif
356 g_cLoops = 0;
357 g_cDeadlocks = 0;
358 g_NanoTSStop = cNanoSecs ? RTTimeNanoTS() + cNanoSecs : 0;
359
360 int rc = testStartThreads(cThreads, pfnThread);
361 if (RT_SUCCESS(rc))
362 testWaitForThreads(30*1000 + cNanoSecs / 1000000, true);
363
364 RTTEST_CHECK(g_hTest, !cNanoSecs || g_cLoops > 0);
365 cLoops += g_cLoops;
366 RTTEST_CHECK(g_hTest, !cNanoSecs || g_cDeadlocks > 0);
367 cDeadlocks += g_cDeadlocks;
368 }
369
370 for (uint32_t i = 0; i < cThreads; i++)
371 {
372 RTTEST_CHECK_RC(g_hTest, RTCritSectDelete(&g_aCritSects[i]), VINF_SUCCESS);
373 RTTEST_CHECK_RC(g_hTest, RTSemRWDestroy(g_ahSemRWs[i]), VINF_SUCCESS);
374 }
375 testWaitForThreads(10*1000, false);
376
377 if (cNanoSecs)
378 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "cLoops=%u cDeadlocks=%u (%u%%)\n",
379 cLoops, cDeadlocks, cLoops ? cDeadlocks * 100 / cLoops : 0);
380}
381
382
383static void test1(uint32_t cThreads, uint32_t cPasses)
384{
385 testIt(cThreads, cPasses, 0, test1Thread, "critsect");
386}
387
388
389static void test2(uint32_t cThreads, uint32_t cPasses)
390{
391 testIt(cThreads, cPasses, 0, test2Thread, "read-write");
392}
393
394
395static void test3(uint32_t cThreads, uint32_t cPasses, uint64_t cNanoSecs)
396{
397 testIt(cThreads, cPasses, cNanoSecs, test3Thread, "read-write race");
398}
399
400
401static bool testIsLockValidationCompiledIn(void)
402{
403 RTCRITSECT CritSect;
404 RTTEST_CHECK_RC_OK_RET(g_hTest, RTCritSectInit(&CritSect), false);
405 RTTEST_CHECK_RC_OK_RET(g_hTest, RTCritSectEnter(&CritSect), false);
406 bool fRet = CritSect.pValidatorRec
407 && CritSect.pValidatorRec->hThread == RTThreadSelf();
408 RTTEST_CHECK_RC_OK_RET(g_hTest, RTCritSectLeave(&CritSect), false);
409 RTTEST_CHECK_RC_OK_RET(g_hTest, RTCritSectDelete(&CritSect), false);
410
411 RTSEMRW hSemRW;
412 RTTEST_CHECK_RC_OK_RET(g_hTest, RTSemRWCreate(&hSemRW), false);
413 RTTEST_CHECK_RC_OK_RET(g_hTest, RTSemRWRequestRead(hSemRW, 50), false);
414 int rc = RTSemRWRequestWrite(hSemRW, 1);
415 if (rc != VERR_SEM_LV_ILLEGAL_UPGRADE)
416 fRet = false;
417 RTTEST_CHECK_RET(g_hTest, RT_FAILURE_NP(rc), false);
418 RTTEST_CHECK_RC_OK_RET(g_hTest, RTSemRWReleaseRead(hSemRW), false);
419 RTTEST_CHECK_RC_OK_RET(g_hTest, RTSemRWDestroy(hSemRW), false);
420
421 return fRet;
422}
423
424int main()
425{
426 /*
427 * Init.
428 */
429 int rc = RTTestInitAndCreate("tstRTLockValidator", &g_hTest);
430 if (rc)
431 return rc;
432 RTTestBanner(g_hTest);
433
434 RTLockValidatorSetEnabled(true);
435 RTLockValidatorSetMayPanic(false);
436 RTLockValidatorSetQuiet(true);
437 if (!testIsLockValidationCompiledIn())
438 return RTTestErrorCount(g_hTest) > 0
439 ? RTTestSummaryAndDestroy(g_hTest)
440 : RTTestSkipAndDestroy(g_hTest, "deadlock detection is not compiled in");
441 RTLockValidatorSetQuiet(false);
442
443 /*
444 * Some initial tests with verbose output.
445 */
446 test1(3, 1);
447
448 test2(1, 1);
449 test2(3, 1);
450
451 /*
452 * More thorough testing without noisy output.
453 */
454 RTLockValidatorSetQuiet(true);
455#if 0
456 test1( 2, 1024);
457 test1( 3, 1024);
458 test1( 7, 896);
459 test1(10, 768);
460 test1(15, 512);
461 test1(30, 384);
462
463 test2( 1, 100);
464 test2( 2, 1024);
465 test2( 3, 1024);
466 test2( 7, 896);
467 test2(10, 768);
468 test2(15, 512);
469 test2(30, 384);
470#endif
471
472 test3( 2, 2, 5*UINT64_C(1000000000));
473 test3(10, 1, 5*UINT64_C(1000000000));
474
475 return RTTestSummaryAndDestroy(g_hTest);
476}
477
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