VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/generic/semspinmutex-r0drv-generic.c@ 27005

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

iprt/semaphore.h: RT_LOCK_CHECK_ORDER && IN_RING3 -> wrap RTSemRWCreate and RTSemMutexCreate so automatic order validation is performed.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.8 KB
Line 
1/* $Id: semspinmutex-r0drv-generic.c 25759 2010-01-12 13:06:06Z vboxsync $ */
2/** @file
3 * IPRT - Spinning Mutex Semaphores, Ring-0 Driver, Generic.
4 */
5
6/*
7 * Copyright (C) 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#ifdef RT_OS_WINDOWS
36# include "../nt/the-nt-kernel.h"
37#endif
38#include "internal/iprt.h"
39
40#include <iprt/semaphore.h>
41#include <iprt/asm.h>
42#include <iprt/assert.h>
43#include <iprt/err.h>
44#include <iprt/mem.h>
45#include <iprt/thread.h>
46#include "internal/magics.h"
47
48
49/*******************************************************************************
50* Structures and Typedefs *
51*******************************************************************************/
52/**
53 * Saved state information.
54 */
55typedef struct RTSEMSPINMUTEXSTATE
56{
57 /** Saved flags register. */
58 RTCCUINTREG fSavedFlags;
59 /** Preemption state. */
60 RTTHREADPREEMPTSTATE PreemptState;
61 /** Whether to spin or sleep. */
62 bool fSpin;
63 /** Whether the flags have been saved. */
64 bool fValidFlags;
65} RTSEMSPINMUTEXSTATE;
66
67/**
68 * Spinning mutex semaphore.
69 */
70typedef struct RTSEMSPINMUTEXINTERNAL
71{
72 /** Magic value (RTSEMSPINMUTEX_MAGIC)
73 * RTCRITSECT_MAGIC is the value of an initialized & operational section. */
74 uint32_t volatile u32Magic;
75 /** Flags. This is a combination of RTSEMSPINMUTEX_FLAGS_XXX and
76 * RTSEMSPINMUTEX_INT_FLAGS_XXX. */
77 uint32_t volatile fFlags;
78 /** The owner thread.
79 * This is NIL if the semaphore is not owned by anyone. */
80 RTNATIVETHREAD volatile hOwner;
81 /** Number of threads that are fighting for the lock. */
82 int32_t volatile cLockers;
83 /** The semaphore to block on. */
84 RTSEMEVENT hEventSem;
85 /** Saved state information of the owner.
86 * This will be restored by RTSemSpinRelease. */
87 RTSEMSPINMUTEXSTATE SavedState;
88} RTSEMSPINMUTEXINTERNAL;
89
90
91/*******************************************************************************
92* Defined Constants And Macros *
93*******************************************************************************/
94//#define RTSEMSPINMUTEX_INT_FLAGS_MUST
95
96/** Validates the handle, returning if invalid. */
97#define RTSEMSPINMUTEX_VALIDATE_RETURN(pThis) \
98 do \
99 { \
100 uint32_t u32Magic; \
101 AssertPtr(pThis); \
102 u32Magic = (pThis)->u32Magic; \
103 if (u32Magic != RTSEMSPINMUTEX_MAGIC) \
104 { \
105 AssertMsgFailed(("u32Magic=%#x pThis=%p\n", u32Magic, pThis)); \
106 return u32Magic == RTSEMSPINMUTEX_MAGIC_DEAD ? VERR_SEM_DESTROYED : VERR_INVALID_HANDLE; \
107 } \
108 } while (0)
109
110
111RTDECL(int) RTSemSpinMutexCreate(PRTSEMSPINMUTEX phSpinMtx, uint32_t fFlags)
112{
113 RTSEMSPINMUTEXINTERNAL *pThis;
114 int rc;
115
116 AssertReturn(!(fFlags & ~RTSEMSPINMUTEX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
117 AssertPtr(phSpinMtx);
118
119 /*
120 * Allocate and initialize the structure.
121 */
122 pThis = (RTSEMSPINMUTEXINTERNAL *)RTMemAllocZ(sizeof(*pThis));
123 if (!pThis)
124 return VERR_NO_MEMORY;
125 pThis->u32Magic = RTSEMSPINMUTEX_MAGIC;
126 pThis->fFlags = fFlags;
127 pThis->hOwner = NIL_RTNATIVETHREAD;
128 pThis->cLockers = 0;
129 rc = RTSemEventCreateEx(&pThis->hEventSem, RTSEMEVENT_FLAGS_NO_LOCK_VAL, NIL_RTLOCKVALCLASS, NULL);
130 if (RT_SUCCESS(rc))
131 {
132 *phSpinMtx = pThis;
133 return VINF_SUCCESS;
134 }
135
136 RTMemFree(pThis);
137 return rc;
138}
139RT_EXPORT_SYMBOL(RTSemSpinMutexCreate);
140
141
142/**
143 * Helper for RTSemSpinMutexTryRequest and RTSemSpinMutexRequest.
144 *
145 * This will check the current context and see if it's usui
146 *
147 * @returns VINF_SUCCESS or VERR_SEM_BAD_CONTEXT.
148 * @param pState Output structure.
149 */
150static int rtSemSpinMutexEnter(RTSEMSPINMUTEXSTATE *pState, RTSEMSPINMUTEXINTERNAL *pThis)
151{
152#ifndef RT_OS_WINDOWS
153 RTTHREADPREEMPTSTATE const StateInit = RTTHREADPREEMPTSTATE_INITIALIZER;
154#endif
155 int rc = VINF_SUCCESS;
156
157 /** @todo Later #1: When entering in interrupt context and we're not able to
158 * wake up threads from it, we could try switch the lock into pure
159 * spinlock mode. This would require that there are no other threads
160 * currently waiting on it and that the RTSEMSPINMUTEX_FLAGS_IRQ_SAFE
161 * flag is set.
162 *
163 * Later #2: Similarly, it is possible to turn on the
164 * RTSEMSPINMUTEX_FLAGS_IRQ_SAFE at run time if we manage to grab the
165 * semaphore ownership at interrupt time. We might want to try delay the
166 * RTSEMSPINMUTEX_FLAGS_IRQ_SAFE even, since we're fine if we get it...
167 */
168
169#ifdef RT_OS_WINDOWS
170 /*
171 * NT: IRQL <= DISPATCH_LEVEL for waking up threads; IRQL < DISPATCH_LEVEL for sleeping.
172 */
173 pState->PreemptState.uchOldIrql = KeGetCurrentIrql();
174 if (pState->PreemptState.uchOldIrql > DISPATCH_LEVEL)
175 return VERR_SEM_BAD_CONTEXT;
176
177 if (pState->PreemptState.uchOldIrql >= DISPATCH_LEVEL)
178 pState->fSpin = true;
179 else
180 {
181 pState->fSpin = false;
182 KeRaiseIrql(DISPATCH_LEVEL, &pState->PreemptState.uchOldIrql);
183 Assert(pState->PreemptState.uchOldIrql < DISPATCH_LEVEL);
184 }
185
186#elif defined(RT_OS_SOLARIS)
187 /*
188 * Solaris: RTSemEventSignal will do bad stuff on S10 if interrupts are disabled.
189 */
190 if (!ASMIntAreEnabled())
191 return VERR_SEM_BAD_CONTEXT;
192
193 pState->fSpin = !RTThreadPreemptIsEnabled(NIL_RTTHREAD);
194 if (RTThreadIsInInterrupt(NIL_RTTHREAD))
195 {
196 if (!(pThis->fFlags & RTSEMSPINMUTEX_FLAGS_IRQ_SAFE))
197 rc = VINF_SEM_BAD_CONTEXT; /* Try, but owner might be interrupted. */
198 pState->fSpin = true;
199 }
200 pState->PreemptState = StateInit;
201 RTThreadPreemptDisable(&pState->PreemptState);
202
203#elif defined(RT_OS_LINUX) || defined(RT_OS_OS2)
204 /*
205 * OSes on which RTSemEventSignal can be called from any context.
206 */
207 pState->fSpin = !RTThreadPreemptIsEnabled(NIL_RTTHREAD);
208 if (RTThreadIsInInterrupt(NIL_RTTHREAD))
209 {
210 if (!(pThis->fFlags & RTSEMSPINMUTEX_FLAGS_IRQ_SAFE))
211 rc = VINF_SEM_BAD_CONTEXT; /* Try, but owner might be interrupted. */
212 pState->fSpin = true;
213 }
214 pState->PreemptState = StateInit;
215 RTThreadPreemptDisable(&pState->PreemptState);
216
217#else /* PORTME: Check for context where we cannot wake up threads. */
218 /*
219 * Default: ASSUME thread can be woken up if interrupts are enabled and
220 * we're not in an interrupt context.
221 * ASSUME that we can go to sleep if preemption is enabled.
222 */
223 if ( RTThreadIsInInterrupt(NIL_RTTHREAD)
224 || !ASMIntAreEnabled())
225 return VERR_SEM_BAD_CONTEXT;
226
227 pState->fSpin = !RTThreadPreemptIsEnabled(NIL_RTTHREAD);
228 pState->PreemptState = StateInit;
229 RTThreadPreemptDisable(&pState->PreemptState);
230#endif
231
232 /*
233 * Disable interrupts if necessary.
234 */
235 pState->fValidFlags = !!(pThis->fFlags & RTSEMSPINMUTEX_FLAGS_IRQ_SAFE);
236 if (pState->fValidFlags)
237 pState->fSavedFlags = ASMIntDisableFlags();
238 else
239 pState->fSavedFlags = 0;
240
241 return rc;
242}
243
244
245/**
246 * Helper for RTSemSpinMutexTryRequest, RTSemSpinMutexRequest and
247 * RTSemSpinMutexRelease.
248 *
249 * @param pState
250 */
251DECL_FORCE_INLINE(void) rtSemSpinMutexLeave(RTSEMSPINMUTEXSTATE *pState)
252{
253 /*
254 * Restore the interrupt flag.
255 */
256 if (pState->fValidFlags)
257 ASMSetFlags(pState->fSavedFlags);
258
259#ifdef RT_OS_WINDOWS
260 /*
261 * NT: Lower the IRQL if we raised it.
262 */
263 if (pState->PreemptState.uchOldIrql < DISPATCH_LEVEL)
264 KeLowerIrql(pState->PreemptState.uchOldIrql);
265#else
266 /*
267 * Default: Restore preemption.
268 */
269 RTThreadPreemptRestore(&pState->PreemptState);
270#endif
271}
272
273
274RTDECL(int) RTSemSpinMutexTryRequest(RTSEMSPINMUTEX hSpinMtx)
275{
276 RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
277 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
278 RTSEMSPINMUTEXSTATE State;
279 bool fRc;
280 int rc;
281
282 Assert(hSelf != NIL_RTNATIVETHREAD);
283 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
284
285 /*
286 * Check context, disable preemption and save flags if necessary.
287 */
288 rc = rtSemSpinMutexEnter(&State, pThis);
289 if (RT_FAILURE(rc))
290 return rc;
291
292 /*
293 * Try take the ownership.
294 */
295 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
296 if (!fRc)
297 {
298 /* Busy, too bad. Check for attempts at nested access. */
299 rc = VERR_SEM_BUSY;
300 if (RT_UNLIKELY(pThis->hOwner == hSelf))
301 {
302 AssertMsgFailed(("%p attempt at nested access\n"));
303 rc = VERR_SEM_NESTED;
304 }
305
306 rtSemSpinMutexLeave(&State);
307 return rc;
308 }
309
310 /*
311 * We're the semaphore owner.
312 */
313 ASMAtomicIncS32(&pThis->cLockers);
314 pThis->SavedState = State;
315 return VINF_SUCCESS;
316}
317RT_EXPORT_SYMBOL(RTSemSpinMutexTryRequest);
318
319
320RTDECL(int) RTSemSpinMutexRequest(RTSEMSPINMUTEX hSpinMtx)
321{
322 RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
323 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
324 RTSEMSPINMUTEXSTATE State;
325 bool fRc;
326 int rc;
327
328 Assert(hSelf != NIL_RTNATIVETHREAD);
329 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
330
331 /*
332 * Check context, disable preemption and save flags if necessary.
333 */
334 rc = rtSemSpinMutexEnter(&State, pThis);
335 if (RT_FAILURE(rc))
336 return rc;
337
338 /*
339 * Try take the ownership.
340 */
341 ASMAtomicIncS32(&pThis->cLockers);
342 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
343 if (!fRc)
344 {
345 uint32_t cSpins;
346
347 /*
348 * It's busy. Check if it's an attempt at nested access.
349 */
350 if (RT_UNLIKELY(pThis->hOwner == hSelf))
351 {
352 AssertMsgFailed(("%p attempt at nested access\n"));
353 rtSemSpinMutexLeave(&State);
354 return VERR_SEM_NESTED;
355 }
356
357 /*
358 * Return if we're in interrupt context and the semaphore isn't
359 * configure to be interrupt safe.
360 */
361 if (rc == VINF_SEM_BAD_CONTEXT)
362 {
363 rtSemSpinMutexLeave(&State);
364 return VERR_SEM_BAD_CONTEXT;
365 }
366
367 /*
368 * Ok, we have to wait.
369 */
370 if (State.fSpin)
371 {
372 for (cSpins = 0; ; cSpins++)
373 {
374 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
375 if (fRc)
376 break;
377 ASMNopPause();
378 if (RT_UNLIKELY(pThis->u32Magic != RTSEMSPINMUTEX_MAGIC))
379 {
380 rtSemSpinMutexLeave(&State);
381 return VERR_SEM_DESTROYED;
382 }
383
384 /*
385 * "Yield" once in a while. This may lower our IRQL/PIL which
386 * may preempting us, and it will certainly stop the hammering
387 * of hOwner for a little while.
388 */
389 if ((cSpins & 0x7f) == 0x1f)
390 {
391 rtSemSpinMutexLeave(&State);
392 rtSemSpinMutexEnter(&State, pThis);
393 Assert(State.fSpin);
394 }
395 }
396 }
397 else
398 {
399 for (cSpins = 0;; cSpins++)
400 {
401 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
402 if (fRc)
403 break;
404 ASMNopPause();
405 if (RT_UNLIKELY(pThis->u32Magic != RTSEMSPINMUTEX_MAGIC))
406 {
407 rtSemSpinMutexLeave(&State);
408 return VERR_SEM_DESTROYED;
409 }
410
411 if ((cSpins & 15) == 15) /* spin a bit before going sleep (again). */
412 {
413 rtSemSpinMutexLeave(&State);
414
415 rc = RTSemEventWait(pThis->hEventSem, RT_INDEFINITE_WAIT);
416 ASMCompilerBarrier();
417 if (RT_SUCCESS(rc))
418 AssertReturn(pThis->u32Magic == RTSEMSPINMUTEX_MAGIC, VERR_SEM_DESTROYED);
419 else if (rc == VERR_INTERRUPTED)
420 AssertRC(rc); /* shouldn't happen */
421 else
422 {
423 AssertRC(rc);
424 return rc;
425 }
426
427 rc = rtSemSpinMutexEnter(&State, pThis);
428 AssertRCReturn(rc, rc);
429 Assert(!State.fSpin);
430 }
431 }
432 }
433 }
434
435 /*
436 * We're the semaphore owner.
437 */
438 pThis->SavedState = State;
439 Assert(pThis->hOwner == hSelf);
440 return VINF_SUCCESS;
441}
442RT_EXPORT_SYMBOL(RTSemSpinMutexRequest);
443
444
445RTDECL(int) RTSemSpinMutexRelease(RTSEMSPINMUTEX hSpinMtx)
446{
447 RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
448 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
449 uint32_t cLockers;
450 RTSEMSPINMUTEXSTATE State;
451 bool fRc;
452
453 Assert(hSelf != NIL_RTNATIVETHREAD);
454 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
455
456 /*
457 * Get the saved state and try release the semaphore.
458 */
459 State = pThis->SavedState;
460 ASMCompilerBarrier();
461 ASMAtomicCmpXchgHandle(&pThis->hOwner, NIL_RTNATIVETHREAD, hSelf, fRc);
462 AssertMsgReturn(fRc,
463 ("hOwner=%p hSelf=%p cLockers=%d\n", pThis->hOwner, hSelf, pThis->cLockers),
464 VERR_NOT_OWNER);
465
466 cLockers = ASMAtomicDecS32(&pThis->cLockers);
467 rtSemSpinMutexLeave(&State);
468 if (cLockers > 0)
469 {
470 int rc = RTSemEventSignal(pThis->hEventSem);
471 AssertReleaseMsg(RT_SUCCESS(rc), ("RTSemEventSignal -> %Rrc\n", rc));
472 }
473 return VINF_SUCCESS;
474}
475RT_EXPORT_SYMBOL(RTSemSpinMutexRelease);
476
477
478RTDECL(int) RTSemSpinMutexDestroy(RTSEMSPINMUTEX hSpinMtx)
479{
480 RTSEMSPINMUTEXINTERNAL *pThis;
481 RTSEMEVENT hEventSem;
482 int rc;
483
484 if (hSpinMtx == NIL_RTSEMSPINMUTEX)
485 return VINF_SUCCESS;
486 pThis = hSpinMtx;
487 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
488
489 /* No destruction races allowed! */
490 AssertMsg( pThis->cLockers == 0
491 && pThis->hOwner == NIL_RTNATIVETHREAD,
492 ("pThis=%p cLockers=%d hOwner=%p\n", pThis, pThis->cLockers, pThis->hOwner));
493
494 /*
495 * Invalidate the structure, free the mutex and free the structure.
496 */
497 ASMAtomicWriteU32(&pThis->u32Magic, RTSEMSPINMUTEX_MAGIC_DEAD);
498 hEventSem = pThis->hEventSem;
499 pThis->hEventSem = NIL_RTSEMEVENT;
500 rc = RTSemEventDestroy(hEventSem); AssertRC(rc);
501
502 RTMemFree(pThis);
503 return rc;
504}
505RT_EXPORT_SYMBOL(RTSemSpinMutexDestroy);
506
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