VirtualBox

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

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

iprt: Added lock validator testcase for read-write semaphore deadlocks. Fixed bugs found with it.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette