diff options
Diffstat (limited to 'src/VBox/Runtime/r0drv/solaris/semevent-r0drv-solaris.c')
-rw-r--r-- | src/VBox/Runtime/r0drv/solaris/semevent-r0drv-solaris.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r0drv/solaris/semevent-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/semevent-r0drv-solaris.c new file mode 100644 index 00000000..1fcda034 --- /dev/null +++ b/src/VBox/Runtime/r0drv/solaris/semevent-r0drv-solaris.c @@ -0,0 +1,347 @@ +/* $Id: semevent-r0drv-solaris.c $ */ +/** @file + * IPRT - Single Release Event Semaphores, Ring-0 Driver, Solaris. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define RTSEMEVENT_WITHOUT_REMAPPING +#include "the-solaris-kernel.h" +#include "internal/iprt.h" +#include <iprt/semaphore.h> + +#include <iprt/assert.h> +#include <iprt/asm.h> +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include <iprt/asm-amd64-x86.h> +#endif +#include <iprt/err.h> +#include <iprt/list.h> +#include <iprt/lockvalidator.h> +#include <iprt/mem.h> +#include <iprt/mp.h> +#include <iprt/thread.h> +#include "internal/magics.h" +#include "semeventwait-r0drv-solaris.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Waiter entry. Lives on the stack. + * + * @remarks Unfortunately, we cannot easily use cv_signal because we cannot + * distinguish between it and the spurious wakeups we get after fork. + * So, we keep an unprioritized FIFO with the sleeping threads. + */ +typedef struct RTSEMEVENTSOLENTRY +{ + /** The list node. */ + RTLISTNODE Node; + /** The thread. */ + kthread_t *pThread; + /** Set to @c true when waking up the thread by signal or destroy. */ + uint32_t volatile fWokenUp; +} RTSEMEVENTSOLENTRY; +/** Pointer to waiter entry. */ +typedef RTSEMEVENTSOLENTRY *PRTSEMEVENTSOLENTRY; + + +/** + * Solaris event semaphore. + */ +typedef struct RTSEMEVENTINTERNAL +{ + /** Magic value (RTSEMEVENT_MAGIC). */ + uint32_t volatile u32Magic; + /** The number of threads referencing this object. */ + uint32_t volatile cRefs; + /** Set if the object is signalled when there are no waiters. */ + bool fSignaled; + /** List of waiting and woken up threads. */ + RTLISTANCHOR WaitList; + /** The Solaris mutex protecting this structure and pairing up the with the cv. */ + kmutex_t Mtx; + /** The Solaris condition variable. */ + kcondvar_t Cnd; +} RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL; + + + +RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem) +{ + return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL); +} + + +RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...) +{ + AssertCompile(sizeof(RTSEMEVENTINTERNAL) > sizeof(void *)); + AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER); + Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL)); + AssertPtrReturn(phEventSem, VERR_INVALID_POINTER); + RT_ASSERT_PREEMPTIBLE(); + + PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)RTMemAlloc(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + + pThis->u32Magic = RTSEMEVENT_MAGIC; + pThis->cRefs = 1; + pThis->fSignaled = false; + RTListInit(&pThis->WaitList); + mutex_init(&pThis->Mtx, "IPRT Event Semaphore", MUTEX_DRIVER, (void *)ipltospl(DISP_LEVEL)); + cv_init(&pThis->Cnd, "IPRT CV", CV_DRIVER, NULL); + + *phEventSem = pThis; + return VINF_SUCCESS; +} + + +/** + * Retain a reference to the semaphore. + * + * @param pThis The semaphore. + */ +DECLINLINE(void) rtR0SemEventSolRetain(PRTSEMEVENTINTERNAL pThis) +{ + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + Assert(cRefs && cRefs < 100000); + NOREF(cRefs); +} + + +/** + * The destruct. + * + * @param pThis The semaphore. + */ +static void rtR0SemEventSolDtor(PRTSEMEVENTINTERNAL pThis) +{ + Assert(pThis->u32Magic != RTSEMEVENT_MAGIC); + cv_destroy(&pThis->Cnd); + mutex_destroy(&pThis->Mtx); + RTMemFree(pThis); +} + + +/** + * Release a reference, destroy the thing if necessary. + * + * @param pThis The semaphore. + */ +DECLINLINE(void) rtR0SemEventSolRelease(PRTSEMEVENTINTERNAL pThis) +{ + if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0)) + rtR0SemEventSolDtor(pThis); +} + + +RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem) +{ + /* + * Validate input. + */ + PRTSEMEVENTINTERNAL pThis = hEventSem; + if (pThis == NIL_RTSEMEVENT) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE); + Assert(pThis->cRefs > 0); + RT_ASSERT_INTS_ON(); + + mutex_enter(&pThis->Mtx); + + /* + * Invalidate the semaphore. + */ + ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC); + ASMAtomicWriteBool(&pThis->fSignaled, false); + + /* + * Abort and wake up all threads. + */ + PRTSEMEVENTSOLENTRY pWaiter; + RTListForEach(&pThis->WaitList, pWaiter, RTSEMEVENTSOLENTRY, Node) + { + pWaiter->fWokenUp = true; + } + cv_broadcast(&pThis->Cnd); + + /* + * Release the reference from RTSemEventCreateEx. + */ + mutex_exit(&pThis->Mtx); + rtR0SemEventSolRelease(pThis); + + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem) +{ + PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem; + RT_ASSERT_PREEMPT_CPUID_VAR(); + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE); + RT_ASSERT_INTS_ON(); + + rtR0SemEventSolRetain(pThis); + rtR0SemSolWaitEnterMutexWithUnpinningHack(&pThis->Mtx); + + /* + * Wake up one thread. + */ + ASMAtomicWriteBool(&pThis->fSignaled, true); + + PRTSEMEVENTSOLENTRY pWaiter; + RTListForEach(&pThis->WaitList, pWaiter, RTSEMEVENTSOLENTRY, Node) + { + if (!pWaiter->fWokenUp) + { + pWaiter->fWokenUp = true; + setrun(pWaiter->pThread); + ASMAtomicWriteBool(&pThis->fSignaled, false); + break; + } + } + + mutex_exit(&pThis->Mtx); + rtR0SemEventSolRelease(pThis); + +#ifdef DEBUG_ramshankar + /** See @bugref{6318} comment#11 */ + return VINF_SUCCESS; +#endif + RT_ASSERT_PREEMPT_CPUID(); + return VINF_SUCCESS; +} + + +/** + * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug. + * + * @returns VBox status code. + * @param pThis The event semaphore. + * @param fFlags See RTSemEventWaitEx. + * @param uTimeout See RTSemEventWaitEx. + * @param pSrcPos The source code position of the wait. + */ +static int rtR0SemEventSolWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout, + PCRTLOCKVALSRCPOS pSrcPos) +{ + /* + * Validate the input. + */ + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER); + AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER); + + rtR0SemEventSolRetain(pThis); + mutex_enter(&pThis->Mtx); + + /* + * In the signaled state? + */ + int rc; + if (ASMAtomicCmpXchgBool(&pThis->fSignaled, false, true)) + rc = VINF_SUCCESS; + else + { + /* + * We have to wait. + */ + RTR0SEMSOLWAIT Wait; + rc = rtR0SemSolWaitInit(&Wait, fFlags, uTimeout); + if (RT_SUCCESS(rc)) + { + RTSEMEVENTSOLENTRY Waiter; /* ASSUMES we won't get swapped out while waiting (TS_DONT_SWAP). */ + Waiter.pThread = curthread; + Waiter.fWokenUp = false; + RTListAppend(&pThis->WaitList, &Waiter.Node); + + for (;;) + { + /* The destruction test. */ + if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC)) + rc = VERR_SEM_DESTROYED; + else + { + /* Check the exit conditions. */ + if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC)) + rc = VERR_SEM_DESTROYED; + else if (Waiter.fWokenUp) + rc = VINF_SUCCESS; + else if (rtR0SemSolWaitHasTimedOut(&Wait)) + rc = VERR_TIMEOUT; + else if (rtR0SemSolWaitWasInterrupted(&Wait)) + rc = VERR_INTERRUPTED; + else + { + /* Do the wait and then recheck the conditions. */ + rtR0SemSolWaitDoIt(&Wait, &pThis->Cnd, &pThis->Mtx, &Waiter.fWokenUp, false); + continue; + } + } + break; + } + + rtR0SemSolWaitDelete(&Wait); + RTListNodeRemove(&Waiter.Node); + } + } + + mutex_exit(&pThis->Mtx); + rtR0SemEventSolRelease(pThis); + return rc; +} + + +RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout) +{ +#ifndef RTSEMEVENT_STRICT + return rtR0SemEventSolWait(hEventSem, fFlags, uTimeout, NULL); +#else + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API(); + return rtR0SemEventSolWait(hEventSem, fFlags, uTimeout, &SrcPos); +#endif +} + + +RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout, + RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API(); + return rtR0SemEventSolWait(hEventSem, fFlags, uTimeout, &SrcPos); +} + + +RTDECL(uint32_t) RTSemEventGetResolution(void) +{ + return rtR0SemSolWaitGetResolution(); +} + |