semevent-r0drv-solaris.c revision dc95659cf2f6c5830e66313b6046e4317881035b
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * IPRT - Semaphores, Ring-0 Driver, Solaris.
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * Copyright (C) 2006-2007 Oracle Corporation
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * This file is part of VirtualBox Open Source Edition (OSE), as
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * available from http://www.virtualbox.org. This file is free software;
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * you can redistribute it and/or modify it under the terms of the GNU
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * General Public License (GPL) as published by the Free Software
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * Foundation, in version 2 as it comes in the "COPYING" file of the
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * The contents of this file may alternatively be used under the terms
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * of the Common Development and Distribution License Version 1.0
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * VirtualBox OSE distribution, in which case the provisions of the
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * CDDL are applicable instead of those of the GPL.
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * You may elect to license modified versions of this file under the
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * terms and conditions of either the GPL or the CDDL or both.
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer/*******************************************************************************
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer* Header Files *
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer*******************************************************************************/
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer/*******************************************************************************
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer* Structures and Typedefs *
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer*******************************************************************************/
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * Solaris event semaphore.
744e50acbaaf22481b25b921b2bc199c8dd4a35cStephen Brooks /** Magic value (RTSEMEVENT_MAGIC). */
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer /** The number of threads referencing this object. */
7656b92221e26376547b0dc52b67e7a414ea9775Stephen Brooks /** Set if the object is signalled when there are no waiters. */
7656b92221e26376547b0dc52b67e7a414ea9775Stephen Brooks /** Object generation.
7656b92221e26376547b0dc52b67e7a414ea9775Stephen Brooks * This is incremented every time the object is signalled and used to
7a0878494fd11b7a861d0b3e2034534c10b5fb46Stephen Brooks * check for spurious wake-ups. */
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer /** The number of waiting threads. */
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer /** The number of signalled threads. */
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer /** The Solaris mutex protecting this structure and pairing up the with the cv. */
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer /** The Solaris condition variable. */
e4f5a11d4a234623168c1558fcdf4341e11769e1James KremerRTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
7656b92221e26376547b0dc52b67e7a414ea9775Stephen BrooksRTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
7656b92221e26376547b0dc52b67e7a414ea9775Stephen Brooks AssertCompile(sizeof(RTSEMEVENTINTERNAL) > sizeof(void *));
7656b92221e26376547b0dc52b67e7a414ea9775Stephen Brooks AssertReturn(!(fFlags & ~RTSEMEVENT_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
7656b92221e26376547b0dc52b67e7a414ea9775Stephen Brooks AssertPtrReturn(phEventSem, VERR_INVALID_POINTER);
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)RTMemAlloc(sizeof(*pThis));
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer mutex_init(&pThis->Mtx, "IPRT Event Semaphore", MUTEX_DRIVER, (void *)ipltospl(DISP_LEVEL));
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer cv_init(&pThis->Cnd, "IPRT CV", CV_DRIVER, NULL);
7656b92221e26376547b0dc52b67e7a414ea9775Stephen BrooksRTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer pThis->u32Magic = RTSEMEVENT_MAGIC_DEAD; /* make the handle invalid */
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * Signal all threads to destroy.
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * We're the last thread referencing this object, destroy it.
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * There are other threads still referencing this object, last one cleans up.
e4f5a11d4a234623168c1558fcdf4341e11769e1James KremerRTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem;
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * If we're in interrupt context we need to unpin the underlying current
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * thread as this could lead to a deadlock (see #4259 for the full explanation)
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * Note! This assumes nobody is using the RTThreadPreemptDisable in an
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * interrupt context and expects it to work right. The swtch will
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * result in a voluntary preemption. To fix this, we would have to
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * do our own counting in RTThreadPreemptDisable/Restore like we do
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * on systems which doesn't do preemption (OS/2, linux, ...) and
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * check whether preemption was disabled via RTThreadPreemptDisable
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * or not and only call swtch if RTThreadPreemptDisable wasn't called.
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer if (curthread->t_intr && getpil() < DISP_LEVEL)
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * We decrement waiters here so that we don't keep signalling threads that
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * have already been signalled but not yet scheduled. So cWaiters might be
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * 0 even when there are threads actually waiting.
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremerstatic int rtSemEventWaitWorker(PRTSEMEVENTINTERNAL pThis, RTMSINTERVAL cMillies, bool fInterruptible)
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * Translate milliseconds into ticks and go to sleep.
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer clock_t cTicks = drv_usectohz((clock_t)(cMillies * 1000L));
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer rc = cv_timedwait_sig(&pThis->Cnd, &pThis->Mtx, cTimeout);
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer rc = cv_timedwait(&pThis->Cnd, &pThis->Mtx, cTimeout);
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremerstatic int rtSemEventWait(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies, bool fInterruptible)
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem;
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * The last signal occurred without any waiters and now we're the first thread
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer * waiting for the event signal. So no real need to wait for one.
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer /* This loop is only for continuing after a spurious wake-up. */
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer uint32_t const uSignalGenBeforeWait = pThis->uSignalGen;
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer rc = rtSemEventWaitWorker(pThis, cMillies, fInterruptible);
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer /* We've been signaled by cv_signal(), consume the wake up. */
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer /* Spurious wakeup due to some signal, go back to waiting. */
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer /* We're being destroyed. */
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer /* Timeout reached. */
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer /* Returned due to pending signal */
e4f5a11d4a234623168c1558fcdf4341e11769e1James KremerRTDECL(int) RTSemEventWait(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer return rtSemEventWait(hEventSem, cMillies, false /* not interruptible */);
e4f5a11d4a234623168c1558fcdf4341e11769e1James KremerRTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
e4f5a11d4a234623168c1558fcdf4341e11769e1James Kremer return rtSemEventWait(hEventSem, cMillies, true /* interruptible */);