VirtualBox

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

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

semspinmutex-r0drv-generic.c: windows build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 13.2 KB
Line 
1/* $Id: semspinmutex-r0drv-generic.c 21543 2009-07-13 15:18:20Z 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 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
256
257 /*
258 * Check context, disable preemption and save flags if necessary.
259 */
260 rc = rtSemSpinMutexEnter(&State, pThis);
261 if (RT_FAILURE(rc))
262 return rc;
263
264 /*
265 * Try take the ownership.
266 */
267 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
268 if (!fRc)
269 {
270 /* Busy, too bad. Check for attempts at nested access. */
271 int rc = VERR_SEM_BUSY;
272 if (RT_UNLIKELY(pThis->hOwner == hSelf))
273 {
274 AssertMsgFailed(("%p attempt at nested access\n"));
275 rc = VERR_SEM_NESTED;
276 }
277
278 rtSemSpinMutexLeave(&State);
279 return rc;
280 }
281
282 /*
283 * We're the semaphore owner.
284 */
285 ASMAtomicIncS32(&pThis->cLockers);
286 pThis->SavedState = State;
287 return VINF_SUCCESS;
288}
289RT_EXPORT_SYMBOL(RTSemSpinMutexTryRequest);
290
291
292RTDECL(int) RTSemSpinMutexRequest(RTSEMSPINMUTEX hSpinMtx)
293{
294 RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
295 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
296 RTSEMSPINMUTEXSTATE State;
297 bool fRc;
298 int rc;
299
300 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
301
302 /*
303 * Check context, disable preemption and save flags if necessary.
304 */
305 rc = rtSemSpinMutexEnter(&State, pThis);
306 if (RT_SUCCESS(rc))
307 return rc;
308
309 /*
310 * Try take the ownership.
311 */
312 ASMAtomicIncS32(&pThis->cLockers);
313 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
314 if (!fRc)
315 {
316 uint32_t cSpins;
317
318 /*
319 * It's busy. Check if it's an attempt at nested access.
320 */
321 if (RT_UNLIKELY(pThis->hOwner == hSelf))
322 {
323 AssertMsgFailed(("%p attempt at nested access\n"));
324 rtSemSpinMutexLeave(&State);
325 return VERR_SEM_NESTED;
326 }
327
328 /*
329 * Ok, we have to wait.
330 */
331 for (cSpins = 0;; cSpins++)
332 {
333 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
334 if (fRc)
335 break;
336
337 if (RT_UNLIKELY(pThis->u32Magic != RTSEMSPINMUTEX_MAGIC))
338 {
339 rtSemSpinMutexLeave(&State);
340 return VERR_SEM_DESTROYED;
341 }
342
343 if ( State.fSpin
344 || (cSpins & 15) != 15 /* spin a bit everytime we wake up. */)
345 ASMNopPause();
346 else
347 {
348 rtSemSpinMutexLeave(&State);
349
350 rc = RTSemEventWait(pThis->hEventSem, RT_INDEFINITE_WAIT);
351 ASMCompilerBarrier();
352 if (RT_SUCCESS(rc))
353 AssertReturn(pThis->u32Magic == RTSEMSPINMUTEX_MAGIC, VERR_SEM_DESTROYED);
354 else if (rc == VERR_INTERRUPTED)
355 AssertRC(rc); /* shouldn't happen */
356 else
357 {
358 AssertRC(rc);
359 return rc;
360 }
361
362 rc = rtSemSpinMutexEnter(&State, pThis);
363 AssertRCReturn(rc, rc);
364 }
365 }
366 }
367
368 /*
369 * We're the semaphore owner.
370 */
371 pThis->SavedState = State;
372 return VINF_SUCCESS;
373}
374RT_EXPORT_SYMBOL(RTSemSpinMutexRequest);
375
376
377RTDECL(int) RTSemSpinMutexRelease(RTSEMSPINMUTEX hSpinMtx)
378{
379 RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
380 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
381 uint32_t cLockers;
382 RTSEMSPINMUTEXSTATE State;
383 bool fRc;
384
385 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
386
387 /*
388 * Get the saved state and try release the semaphore.
389 */
390 State = pThis->SavedState;
391 ASMCompilerBarrier();
392 ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
393 AssertReturn(fRc, VERR_NOT_OWNER);
394
395 cLockers = ASMAtomicDecS32(&pThis->cLockers);
396 if (cLockers > 0)
397 {
398 int rc = RTSemEventSignal(pThis->hEventSem);
399 AssertReleaseMsg(RT_SUCCESS(rc), ("RTSemEventSignal -> %Rrc\n", rc));
400 }
401 return VINF_SUCCESS;
402}
403RT_EXPORT_SYMBOL(RTSemSpinMutexRelease);
404
405
406RTDECL(int) RTSemSpinMutexDestroy(RTSEMSPINMUTEX hSpinMtx)
407{
408 RTSEMSPINMUTEXINTERNAL *pThis;
409 RTSEMEVENT hEventSem;
410 int rc;
411
412 if (hSpinMtx == NIL_RTSEMSPINMUTEX)
413 return VINF_SUCCESS;
414 pThis = hSpinMtx;
415 RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
416
417 /* No destruction races allowed! */
418 AssertMsg( pThis->cLockers == -1
419 && pThis->hOwner == NIL_RTNATIVETHREAD,
420 ("pThis=%p cLockers=%d hOwner=%p\n", pThis, pThis->cLockers, pThis->hOwner));
421
422 /*
423 * Invalidate the structure, free the mutex and free the structure.
424 */
425 ASMAtomicWriteU32(&pThis->u32Magic, RTSEMSPINMUTEX_MAGIC_DEAD);
426 hEventSem = pThis->hEventSem;
427 pThis->hEventSem = NIL_RTSEMEVENT;
428 rc = RTSemEventDestroy(hEventSem); AssertRC(rc);
429
430 RTMemFree(pThis);
431 return rc;
432}
433RT_EXPORT_SYMBOL(RTSemSpinMutexDestroy);
434
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