VirtualBox

source: vbox/trunk/src/VBox/Runtime/generic/critsect-generic.cpp@ 6079

Last change on this file since 6079 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.3 KB
Line 
1/* $Id: critsect-generic.cpp 5999 2007-12-07 15:05:06Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - Critical Section, Generic.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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* Header Files *
29*******************************************************************************/
30#include <iprt/critsect.h>
31#include <iprt/semaphore.h>
32#include <iprt/thread.h>
33#include <iprt/assert.h>
34#include <iprt/asm.h>
35#include <iprt/err.h>
36#include "internal/thread.h"
37
38/** @def RTCRITSECT_STRICT
39 * Define this to enable deadlock detection.
40 *
41 * @remark This won't work safely on L4 since we have to traverse the AVL tree
42 * in order to get the RT thread structure there and this tree is
43 * protected by a critsect atm.
44 */
45#if !defined(RTCRITSECT_STRICT) && defined(RT_STRICT) && !defined(RT_OS_L4)
46# define RTCRITSECT_STRICT
47#endif
48
49/* in strict mode we're redefining this, so undefine it now for the implementation. */
50#undef RTCritSectEnter
51#undef RTCritSectTryEnter
52#undef RTCritSectEnterMultiple
53
54
55/**
56 * Initialize a critical section.
57 */
58RTDECL(int) RTCritSectInit(PRTCRITSECT pCritSect)
59{
60 return RTCritSectInitEx(pCritSect, 0);
61}
62
63
64/**
65 * Initialize a critical section.
66 *
67 * @returns iprt status code.
68 * @param pCritSect Pointer to the critical section structure.
69 * @param fFlags Flags, any combination of the RTCRITSECT_FLAGS \#defines.
70 */
71RTDECL(int) RTCritSectInitEx(PRTCRITSECT pCritSect, uint32_t fFlags)
72{
73 /*
74 * Initialize the structure and
75 */
76 pCritSect->u32Magic = RTCRITSECT_MAGIC;
77 pCritSect->fFlags = fFlags;
78 pCritSect->cNestings = 0;
79 pCritSect->cLockers = -1;
80 pCritSect->NativeThreadOwner = NIL_RTNATIVETHREAD;
81 pCritSect->Strict.ThreadOwner = NIL_RTTHREAD;
82 pCritSect->Strict.pszEnterFile = NULL;
83 pCritSect->Strict.u32EnterLine = 0;
84 pCritSect->Strict.uEnterId = 0;
85 int rc = RTSemEventCreate(&pCritSect->EventSem);
86 if (RT_SUCCESS(rc))
87 return VINF_SUCCESS;
88
89 AssertRC(rc);
90 pCritSect->EventSem = NULL;
91 pCritSect->u32Magic = (uint32_t)rc;
92 return rc;
93}
94
95
96/**
97 * Enter multiple critical sections.
98 *
99 * This function will enter ALL the specified critical sections before returning.
100 *
101 * @returns VINF_SUCCESS on success.
102 * @returns VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
103 * @returns VERR_SEM_DESTROYED if RTCritSectDelete was called while waiting.
104 * @param cCritSects Number of critical sections in the array.
105 * @param papCritSects Array of critical section pointers.
106 *
107 * @remark Please note that this function will not necessarily come out favourable in a
108 * fight with other threads which are using the normal RTCritSectEnter() function.
109 * Therefore, avoid having to enter multiple critical sections!
110 */
111RTDECL(int) RTCritSectEnterMultiple(unsigned cCritSects, PRTCRITSECT *papCritSects)
112#ifdef RTCRITSECT_STRICT
113{
114 return RTCritSectEnterMultipleDebug(cCritSects, papCritSects, __FILE__, __LINE__, 0);
115}
116RTDECL(int) RTCritSectEnterMultipleDebug(unsigned cCritSects, PRTCRITSECT *papCritSects, const char *pszFile, unsigned uLine, RTUINTPTR uId)
117#endif /* RTCRITSECT_STRICT */
118{
119 Assert(cCritSects > 0);
120 Assert(VALID_PTR(papCritSects));
121
122 /*
123 * Try get them all.
124 */
125 int rc = VERR_INVALID_PARAMETER;
126 unsigned i;
127 for (i = 0; i < cCritSects; i++)
128 {
129#ifdef RTCRITSECT_STRICT
130 rc = RTCritSectTryEnterDebug(papCritSects[i], pszFile, uLine, uId);
131#else
132 rc = RTCritSectTryEnter(papCritSects[i]);
133#endif
134 if (RT_FAILURE(rc))
135 break;
136 }
137 if (RT_SUCCESS(rc))
138 return rc;
139
140 /*
141 * The retry loop.
142 */
143 for (unsigned cTries = 0; ; cTries++)
144 {
145 /*
146 * We've failed, release any locks we might have gotten. ('i' is the lock that failed btw.)
147 */
148 unsigned j = i;
149 while (j-- > 0)
150 {
151 int rc2 = RTCritSectLeave(papCritSects[j]);
152 AssertRC(rc2);
153 }
154 if (rc != VERR_SEM_BUSY)
155 return rc;
156
157 /*
158 * Try prevent any theoretical synchronous races with other threads.
159 */
160 Assert(cTries < 1000000);
161 if (cTries > 10000)
162 RTThreadSleep(cTries % 3);
163
164 /*
165 * Wait on the one we failed to get.
166 */
167#ifdef RTCRITSECT_STRICT
168 rc = RTCritSectEnterDebug(papCritSects[i], pszFile, uLine, uId);
169#else
170 rc = RTCritSectEnter(papCritSects[i]);
171#endif
172 if (RT_FAILURE(rc))
173 return rc;
174
175 /*
176 * Try take the others.
177 */
178 for (j = 0; j < cCritSects; j++)
179 {
180 if (j != i)
181 {
182#ifdef RTCRITSECT_STRICT
183 rc = RTCritSectTryEnterDebug(papCritSects[j], pszFile, uLine, uId);
184#else
185 rc = RTCritSectTryEnter(papCritSects[j]);
186#endif
187 if (RT_FAILURE(rc))
188 break;
189 }
190 }
191 if (RT_SUCCESS(rc))
192 return rc;
193
194 /*
195 * We failed.
196 */
197 if (i > j)
198 {
199 int rc2 = RTCritSectLeave(papCritSects[i]);
200 AssertRC(rc2);
201 }
202 i = j;
203 }
204}
205
206
207/**
208 * Try enter a critical section.
209 *
210 * @returns VINF_SUCCESS on success.
211 * @returns VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
212 * @returns VERR_SEM_DESTROYED if RTCritSectDelete was called while waiting.
213 * @param pCritSect The critical section.
214 */
215RTDECL(int) RTCritSectTryEnter(PRTCRITSECT pCritSect)
216#ifdef RTCRITSECT_STRICT
217{
218 return RTCritSectTryEnterDebug(pCritSect, __FILE__, __LINE__, 0);
219}
220RTDECL(int) RTCritSectTryEnterDebug(PRTCRITSECT pCritSect, const char *pszFile, unsigned uLine, RTUINTPTR uId)
221#endif /* RTCRITSECT_STRICT */
222{
223 Assert(pCritSect);
224 Assert(pCritSect->u32Magic == RTCRITSECT_MAGIC);
225 RTNATIVETHREAD NativeThreadSelf = RTThreadNativeSelf();
226#ifdef RTCRITSECT_STRICT
227 RTTHREAD ThreadSelf = RTThreadSelf();
228 if (ThreadSelf == NIL_RTTHREAD)
229 RTThreadAdopt(RTTHREADTYPE_DEFAULT, 0, NULL, &ThreadSelf);
230#endif
231
232 /*
233 * Try take the lock. (cLockers is -1 if it's free)
234 */
235 if (!ASMAtomicCmpXchgS32(&pCritSect->cLockers, 0, -1))
236 {
237 /*
238 * Somebody is owning it (or will be soon). Perhaps it's us?
239 */
240 if (pCritSect->NativeThreadOwner == NativeThreadSelf)
241 {
242 if (!(pCritSect->fFlags & RTCRITSECT_FLAGS_NO_NESTING))
243 {
244 ASMAtomicIncS32(&pCritSect->cLockers);
245 pCritSect->cNestings++;
246 return VINF_SUCCESS;
247 }
248 AssertMsgFailed(("Nested entry of critsect %p\n", pCritSect));
249 return VERR_SEM_NESTED;
250 }
251 return VERR_SEM_BUSY;
252 }
253
254 /*
255 * First time
256 */
257 pCritSect->cNestings = 1;
258 ASMAtomicXchgSize(&pCritSect->NativeThreadOwner, NativeThreadSelf);
259#ifdef RTCRITSECT_STRICT
260 pCritSect->Strict.pszEnterFile = pszFile;
261 pCritSect->Strict.u32EnterLine = uLine;
262 pCritSect->Strict.uEnterId = uId;
263 ASMAtomicXchgSize(&pCritSect->Strict.ThreadOwner, (RTUINTPTR)ThreadSelf); /* screw gcc and its pedantic warnings. */
264#endif
265
266 return VINF_SUCCESS;
267}
268
269
270/**
271 * Enter a critical section.
272 *
273 * @returns VINF_SUCCESS on success.
274 * @returns VERR_SEM_NESTED if nested enter on a no nesting section. (Asserted.)
275 * @returns VERR_SEM_DESTROYED if RTCritSectDelete was called while waiting.
276 * @param pCritSect The critical section.
277 */
278RTDECL(int) RTCritSectEnter(PRTCRITSECT pCritSect)
279#ifdef RTCRITSECT_STRICT
280{
281 return RTCritSectEnterDebug(pCritSect, __FILE__, __LINE__, 0);
282}
283RTDECL(int) RTCritSectEnterDebug(PRTCRITSECT pCritSect, const char *pszFile, unsigned uLine, RTUINTPTR uId)
284#endif /* RTCRITSECT_STRICT */
285{
286 Assert(pCritSect);
287 Assert(pCritSect->u32Magic == RTCRITSECT_MAGIC);
288 RTNATIVETHREAD NativeThreadSelf = RTThreadNativeSelf();
289#ifdef RTCRITSECT_STRICT
290 RTTHREAD ThreadSelf = RTThreadSelf();
291 if (ThreadSelf == NIL_RTTHREAD)
292 RTThreadAdopt(RTTHREADTYPE_DEFAULT, 0, NULL, &ThreadSelf);
293#endif
294
295 /** If the critical section has already been destroyed, then inform the caller. */
296 if (pCritSect->u32Magic != RTCRITSECT_MAGIC)
297 return VERR_SEM_DESTROYED;
298
299 /*
300 * Increment the waiter counter.
301 * This becomes 0 when the section is free.
302 */
303 if (ASMAtomicIncS32(&pCritSect->cLockers) > 0)
304 {
305 /*
306 * Nested?
307 */
308 if (pCritSect->NativeThreadOwner == NativeThreadSelf)
309 {
310 if (!(pCritSect->fFlags & RTCRITSECT_FLAGS_NO_NESTING))
311 {
312 pCritSect->cNestings++;
313 return VINF_SUCCESS;
314 }
315 else
316 {
317 AssertMsgFailed(("Nested entry of critsect %p\n", pCritSect));
318 ASMAtomicDecS32(&pCritSect->cLockers);
319 return VERR_SEM_NESTED;
320 }
321 }
322
323 for (;;)
324 {
325#ifdef RTCRITSECT_STRICT
326 rtThreadBlocking(ThreadSelf, RTTHREADSTATE_CRITSECT, (uintptr_t)pCritSect, pszFile, uLine, uId);
327#endif
328 int rc = RTSemEventWait(pCritSect->EventSem, RT_INDEFINITE_WAIT);
329#ifdef RTCRITSECT_STRICT
330 rtThreadUnblocked(ThreadSelf, RTTHREADSTATE_CRITSECT);
331#endif
332 if (pCritSect->u32Magic != RTCRITSECT_MAGIC)
333 return VERR_SEM_DESTROYED;
334 if (rc == VINF_SUCCESS)
335 break;
336 AssertMsg(rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED, ("rc=%Vrc\n", rc));
337 }
338 AssertMsg(pCritSect->NativeThreadOwner == NIL_RTNATIVETHREAD, ("pCritSect->NativeThreadOwner=%p\n", pCritSect->NativeThreadOwner));
339 }
340
341 /*
342 * First time
343 */
344 pCritSect->cNestings = 1;
345 ASMAtomicXchgSize(&pCritSect->NativeThreadOwner, NativeThreadSelf);
346#ifdef RTCRITSECT_STRICT
347 pCritSect->Strict.pszEnterFile = pszFile;
348 pCritSect->Strict.u32EnterLine = uLine;
349 pCritSect->Strict.uEnterId = uId;
350 ASMAtomicXchgSize(&pCritSect->Strict.ThreadOwner, (RTUINTPTR)ThreadSelf); /* screw gcc and its pedantic warnings. */
351#endif
352
353 return VINF_SUCCESS;
354}
355
356
357/**
358 * Leave a critical section.
359 *
360 * @returns VINF_SUCCESS.
361 * @param pCritSect The critical section.
362 */
363RTDECL(int) RTCritSectLeave(PRTCRITSECT pCritSect)
364{
365 /*
366 * Assert ownership and so on.
367 */
368 Assert(pCritSect);
369 Assert(pCritSect->u32Magic == RTCRITSECT_MAGIC);
370 Assert(pCritSect->cNestings > 0);
371 Assert(pCritSect->cLockers >= 0);
372 Assert(pCritSect->NativeThreadOwner == RTThreadNativeSelf());
373
374 /*
375 * Decrement nestings, if <= 0 when we'll release the critsec.
376 */
377 pCritSect->cNestings--;
378 if (pCritSect->cNestings > 0)
379 ASMAtomicDecS32(&pCritSect->cLockers);
380 else
381 {
382 /*
383 * Set owner to zero.
384 * Decrement waiters, if >= 0 then we have to wake one of them up.
385 */
386#ifdef RTCRITSECT_STRICT
387 ASMAtomicXchgSize(&pCritSect->Strict.ThreadOwner, NIL_RTTHREAD);
388#endif
389 ASMAtomicXchgSize(&pCritSect->NativeThreadOwner, NIL_RTNATIVETHREAD);
390 if (ASMAtomicDecS32(&pCritSect->cLockers) >= 0)
391 {
392 int rc = RTSemEventSignal(pCritSect->EventSem);
393 AssertReleaseMsg(RT_SUCCESS(rc), ("RTSemEventSignal -> %Vrc\n", rc));
394 }
395 }
396 return VINF_SUCCESS;
397}
398
399
400/**
401 * Leave multiple critical sections.
402 *
403 * @returns VINF_SUCCESS.
404 * @param cCritSects Number of critical sections in the array.
405 * @param papCritSects Array of critical section pointers.
406 */
407RTDECL(int) RTCritSectLeaveMultiple(unsigned cCritSects, PRTCRITSECT *papCritSects)
408{
409 int rc = VINF_SUCCESS;
410 for (unsigned i = 0; i < cCritSects; i++)
411 {
412 int rc2 = RTCritSectLeave(papCritSects[i]);
413 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
414 rc = rc2;
415 }
416 return rc;
417}
418
419
420#ifndef RTCRITSECT_STRICT
421RTDECL(int) RTCritSectEnterDebug(PRTCRITSECT pCritSect, const char *pszFile, unsigned uLine, RTUINTPTR uId)
422{
423 return RTCritSectEnter(pCritSect);
424}
425
426RTDECL(int) RTCritSectTryEnterDebug(PRTCRITSECT pCritSect, const char *pszFile, unsigned uLine, RTUINTPTR uId)
427{
428 return RTCritSectTryEnter(pCritSect);
429}
430
431RTDECL(int) RTCritSectEnterMultipleDebug(unsigned cCritSects, PRTCRITSECT *papCritSects, const char *pszFile, unsigned uLine, RTUINTPTR uId)
432{
433 return RTCritSectEnterMultiple(cCritSects, papCritSects);
434}
435#endif /* RT_STRICT */
436
437
438/**
439 * Deletes a critical section.
440 *
441 * @returns VINF_SUCCESS.
442 * @param pCritSect The critical section.
443 */
444RTDECL(int) RTCritSectDelete(PRTCRITSECT pCritSect)
445{
446 /*
447 * Assert free waiters and so on.
448 */
449 Assert(pCritSect);
450 Assert(pCritSect->u32Magic == RTCRITSECT_MAGIC);
451 Assert(pCritSect->cNestings == 0);
452 Assert(pCritSect->cLockers == -1);
453 Assert(pCritSect->NativeThreadOwner == NIL_RTNATIVETHREAD);
454
455 /*
456 * Invalidate the structure and free the mutex.
457 * In case someone is waiting we'll signal the semaphore cLockers + 1 times.
458 */
459 ASMAtomicXchgU32(&pCritSect->u32Magic, 0);
460 pCritSect->fFlags = 0;
461 pCritSect->cNestings = 0;
462 pCritSect->NativeThreadOwner= NIL_RTNATIVETHREAD;
463 RTSEMEVENT EventSem = pCritSect->EventSem;
464 pCritSect->EventSem = NULL;
465 while (pCritSect->cLockers-- >= 0)
466 RTSemEventSignal(EventSem);
467 ASMAtomicXchgS32(&pCritSect->cLockers, -1);
468 int rc = RTSemEventDestroy(EventSem);
469 AssertRC(rc);
470
471 return rc;
472}
473
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