VirtualBox

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

Last change on this file since 94293 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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