VirtualBox

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

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

semspinmutex-r0drv-generic.c: Fixed missing wakup.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 13.5 KB
Line 
1/* $Id: semspinmutex-r0drv-generic.c 21554 2009-07-13 16:50:08Z 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 waiting 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 = -1;
129 rc = RTSemEventCreate(&pThis->hEventSem);
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 /** @todo Later #1: When entering in interrupt context and we're not able to
153 * wake up threads from it, we could try switch the lock into pure
154 * spinlock mode. This would require that there are no other threads
155 * currently waiting on it and that the RTSEMSPINMUTEX_FLAGS_IRQ_SAFE
156 * flag is set.
157 *
158 * Later #2: Similarly, it is possible to turn on the
159 * RTSEMSPINMUTEX_FLAGS_IRQ_SAFE at run time if we manage to grab the
160 * semaphore ownership at interrupt time. We might want to try delay the
161 * RTSEMSPINMUTEX_FLAGS_IRQ_SAFE even, since we're fine if we get it...
162 */
163
164#ifdef RT_OS_WINDOWS
165 /*
166 * NT: IRQL <= DISPATCH_LEVEL for waking up threads; IRQL < DISPATCH_LEVEL for sleeping.
167 */
168 pState->PreemptState.uchOldIrql = KeGetCurrentIrql();
169 if (pState->PreemptState.uchOldIrql > DISPATCH_LEVEL)
170 return VERR_SEM_BAD_CONTEXT;
171
172 if (pState->PreemptState.uchOldIrql >= DISPATCH_LEVEL)
173 pState->fSpin = true;
174 else
175 {
176 pState->fSpin = false;
177 KeRaiseIrql(DISPATCH_LEVEL, &pState->PreemptState.uchOldIrql);
178 Assert(pState->PreemptState.uchOldIrql < DISPATCH_LEVEL);
179 }
180
181#elif defined(RT_OS_LINUX) || defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
182 /*
183 * OSes on which RTSemEventSignal can be called from any context.
184 */
185 pState->fSpin = !RTThreadPreemptIsEnabled(NIL_RTTHREAD);
186 if (RTThreadIsInInterrupt(NIL_RTTHREAD))
187 {
188 if (!(pThis->fFlags & RTSEMSPINMUTEX_FLAGS_IRQ_SAFE))
189 return VERR_SEM_BAD_CONTEXT;
190 pState->fSpin = true;
191 }
192 RTThreadPreemptDisable(&pState->PreemptState);
193
194#else /* PORTME: Check for context where we cannot wake up threads. */
195 /*
196 * Default: ASSUME thread can be woken up from all context except interrupt.
197 * ASSUME that we can go to sleep if preemption is enabled.
198 */
199 if (RTThreadIsInInterrupt(NIL_RTTHREAD))
200 return VERR_SEM_BAD_CONTEXT;
201 pState->fSpin = !RTThreadPreemptIsEnabled(NIL_RTTHREAD);
202 RTThreadPreemptDisable(&pState->PreemptState);
203#endif
204
205 /*
206 * Disable interrupts if necessary.
207 */
208 pState->fValidFlags = !!(pThis->fFlags & RTSEMSPINMUTEX_FLAGS_IRQ_SAFE);
209 if (pState->fValidFlags)
210 pState->fSavedFlags = ASMIntDisableFlags();
211 else
212 pState->fSavedFlags = 0;
213
214 return VINF_SUCCESS;
215}
216
217
218/**
219 * Helper for RTSemSpinMutexTryRequest, RTSemSpinMutexRequest and
220 * RTSemSpinMutexRelease.
221 *
222 * @param pState
223 */
224DECL_FORCE_INLINE(void) rtSemSpinMutexLeave(RTSEMSPINMUTEXSTATE *pState)
225{
226 /*
227 * Restore the interrupt flag.
228 */
229 if (pState->fValidFlags)
230 ASMSetFlags(pState->fSavedFlags);
231
232#ifdef RT_OS_WINDOWS
233 /*
234 * NT: Lower the IRQL if we raised it.
235 */
236 if (pState->PreemptState.uchOldIrql < DISPATCH_LEVEL)
237 KeLowerIrql(pState->PreemptState.uchOldIrql);
238#else
239 /*
240 * Default: Restore preemption.
241 */
242 RTThreadPreemptRestore(&pState->PreemptState);
243#endif
244}
245
246
247RTDECL(int) RTSemSpinMutexTryRequest(RTSEMSPINMUTEX hSpinMtx)
248{
249 RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
250 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
251 RTSEMSPINMUTEXSTATE State;
252 bool fRc;
253 int rc;
254
255 Assert(hSelf != NIL_RTNATIVETHREAD);
256 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
257
258 /*
259 * Check context, disable preemption and save flags if necessary.
260 */
261 rc = rtSemSpinMutexEnter(&State, pThis);
262 if (RT_FAILURE(rc))
263 return rc;
264
265 /*
266 * Try take the ownership.
267 */
268 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
269 if (!fRc)
270 {
271 /* Busy, too bad. Check for attempts at nested access. */
272 int rc = VERR_SEM_BUSY;
273 if (RT_UNLIKELY(pThis->hOwner == hSelf))
274 {
275 AssertMsgFailed(("%p attempt at nested access\n"));
276 rc = VERR_SEM_NESTED;
277 }
278
279 rtSemSpinMutexLeave(&State);
280 return rc;
281 }
282
283 /*
284 * We're the semaphore owner.
285 */
286 ASMAtomicIncS32(&pThis->cLockers);
287 pThis->SavedState = State;
288 return VINF_SUCCESS;
289}
290RT_EXPORT_SYMBOL(RTSemSpinMutexTryRequest);
291
292
293RTDECL(int) RTSemSpinMutexRequest(RTSEMSPINMUTEX hSpinMtx)
294{
295 RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
296 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
297 RTSEMSPINMUTEXSTATE State;
298 bool fRc;
299 int rc;
300
301 Assert(hSelf != NIL_RTNATIVETHREAD);
302 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
303
304 /*
305 * Check context, disable preemption and save flags if necessary.
306 */
307 rc = rtSemSpinMutexEnter(&State, pThis);
308 if (RT_FAILURE(rc))
309 return rc;
310
311 /*
312 * Try take the ownership.
313 */
314 ASMAtomicIncS32(&pThis->cLockers);
315 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
316 if (!fRc)
317 {
318 uint32_t cSpins;
319
320 /*
321 * It's busy. Check if it's an attempt at nested access.
322 */
323 if (RT_UNLIKELY(pThis->hOwner == hSelf))
324 {
325 AssertMsgFailed(("%p attempt at nested access\n"));
326 rtSemSpinMutexLeave(&State);
327 return VERR_SEM_NESTED;
328 }
329
330 /*
331 * Ok, we have to wait.
332 */
333 for (cSpins = 0;; cSpins++)
334 {
335 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
336 if (fRc)
337 break;
338
339 if (RT_UNLIKELY(pThis->u32Magic != RTSEMSPINMUTEX_MAGIC))
340 {
341 rtSemSpinMutexLeave(&State);
342 return VERR_SEM_DESTROYED;
343 }
344
345 if ( State.fSpin
346 || (cSpins & 15) != 15 /* spin a bit everytime we wake up. */)
347 ASMNopPause();
348 else
349 {
350 rtSemSpinMutexLeave(&State);
351
352 rc = RTSemEventWait(pThis->hEventSem, RT_INDEFINITE_WAIT);
353 ASMCompilerBarrier();
354 if (RT_SUCCESS(rc))
355 AssertReturn(pThis->u32Magic == RTSEMSPINMUTEX_MAGIC, VERR_SEM_DESTROYED);
356 else if (rc == VERR_INTERRUPTED)
357 AssertRC(rc); /* shouldn't happen */
358 else
359 {
360 AssertRC(rc);
361 return rc;
362 }
363
364 rc = rtSemSpinMutexEnter(&State, pThis);
365 AssertRCReturn(rc, rc);
366 }
367 }
368 }
369
370 /*
371 * We're the semaphore owner.
372 */
373 pThis->SavedState = State;
374 Assert(pThis->hOwner == hSelf);
375 return VINF_SUCCESS;
376}
377RT_EXPORT_SYMBOL(RTSemSpinMutexRequest);
378
379
380RTDECL(int) RTSemSpinMutexRelease(RTSEMSPINMUTEX hSpinMtx)
381{
382 RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
383 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
384 uint32_t cLockers;
385 RTSEMSPINMUTEXSTATE State;
386 bool fRc;
387
388 Assert(hSelf != NIL_RTNATIVETHREAD);
389 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
390
391 /*
392 * Get the saved state and try release the semaphore.
393 */
394 State = pThis->SavedState;
395 ASMCompilerBarrier();
396 ASMAtomicCmpXchgHandle(&pThis->hOwner, NIL_RTNATIVETHREAD, hSelf, fRc);
397 AssertMsgReturn(fRc,
398 ("hOwner=%p hSelf=%p cLockers=%d\n", pThis->hOwner, hSelf, pThis->cLockers),
399 VERR_NOT_OWNER);
400
401 cLockers = ASMAtomicDecS32(&pThis->cLockers);
402 rtSemSpinMutexLeave(&State);
403 if (cLockers >= 0)
404 {
405 int rc = RTSemEventSignal(pThis->hEventSem);
406 AssertReleaseMsg(RT_SUCCESS(rc), ("RTSemEventSignal -> %Rrc\n", rc));
407 }
408 return VINF_SUCCESS;
409}
410RT_EXPORT_SYMBOL(RTSemSpinMutexRelease);
411
412
413RTDECL(int) RTSemSpinMutexDestroy(RTSEMSPINMUTEX hSpinMtx)
414{
415 RTSEMSPINMUTEXINTERNAL *pThis;
416 RTSEMEVENT hEventSem;
417 int rc;
418
419 if (hSpinMtx == NIL_RTSEMSPINMUTEX)
420 return VINF_SUCCESS;
421 pThis = hSpinMtx;
422 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
423
424 /* No destruction races allowed! */
425 AssertMsg( pThis->cLockers == -1
426 && pThis->hOwner == NIL_RTNATIVETHREAD,
427 ("pThis=%p cLockers=%d hOwner=%p\n", pThis, pThis->cLockers, pThis->hOwner));
428
429 /*
430 * Invalidate the structure, free the mutex and free the structure.
431 */
432 ASMAtomicWriteU32(&pThis->u32Magic, RTSEMSPINMUTEX_MAGIC_DEAD);
433 hEventSem = pThis->hEventSem;
434 pThis->hEventSem = NIL_RTSEMEVENT;
435 rc = RTSemEventDestroy(hEventSem); AssertRC(rc);
436
437 RTMemFree(pThis);
438 return rc;
439}
440RT_EXPORT_SYMBOL(RTSemSpinMutexDestroy);
441
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