Пример #1
0
/**
 * Deletes one critical section.
 *
 * @returns Return code from RTCritSectDelete.
 *
 * @param   pVM         Pointer to the VM.
 * @param   pCritSect   The critical section.
 * @param   pPrev       The previous critical section in the list.
 * @param   fFinal      Set if this is the final call and statistics shouldn't be deregistered.
 *
 * @remarks Caller must have entered the ListCritSect.
 */
static int pdmR3CritSectDeleteOne(PVM pVM, PUVM pUVM, PPDMCRITSECTINT pCritSect, PPDMCRITSECTINT pPrev, bool fFinal)
{
    /*
     * Assert free waiters and so on (c&p from RTCritSectDelete).
     */
    Assert(pCritSect->Core.u32Magic == RTCRITSECT_MAGIC);
    Assert(pCritSect->Core.cNestings == 0);
    Assert(pCritSect->Core.cLockers == -1);
    Assert(pCritSect->Core.NativeThreadOwner == NIL_RTNATIVETHREAD);
    Assert(RTCritSectIsOwner(&pUVM->pdm.s.ListCritSect));

    /*
     * Unlink it.
     */
    if (pPrev)
        pPrev->pNext = pCritSect->pNext;
    else
        pUVM->pdm.s.pCritSects = pCritSect->pNext;

    /*
     * Delete it (parts taken from RTCritSectDelete).
     * In case someone is waiting we'll signal the semaphore cLockers + 1 times.
     */
    ASMAtomicWriteU32(&pCritSect->Core.u32Magic, 0);
    SUPSEMEVENT hEvent = (SUPSEMEVENT)pCritSect->Core.EventSem;
    pCritSect->Core.EventSem = NIL_RTSEMEVENT;
    while (pCritSect->Core.cLockers-- >= 0)
        SUPSemEventSignal(pVM->pSession, hEvent);
    ASMAtomicWriteS32(&pCritSect->Core.cLockers, -1);
    int rc = SUPSemEventClose(pVM->pSession, hEvent);
    AssertRC(rc);
    RTLockValidatorRecExclDestroy(&pCritSect->Core.pValidatorRec);
    pCritSect->pNext   = NULL;
    pCritSect->pvKey   = NULL;
    pCritSect->pVMR3   = NULL;
    pCritSect->pVMR0   = NIL_RTR0PTR;
    pCritSect->pVMRC   = NIL_RTRCPTR;
    RTStrFree((char *)pCritSect->pszName);
    pCritSect->pszName = NULL;
    if (!fFinal)
    {
        STAMR3Deregister(pVM, &pCritSect->StatContentionRZLock);
        STAMR3Deregister(pVM, &pCritSect->StatContentionRZUnlock);
        STAMR3Deregister(pVM, &pCritSect->StatContentionR3);
#ifdef VBOX_WITH_STATISTICS
        STAMR3Deregister(pVM, &pCritSect->StatLocked);
#endif
    }
    return rc;
}
Пример #2
0
/**
 * Leaves a critical section entered with PDMCritSectEnter().
 *
 * @param   pCritSect           The PDM critical section to leave.
 */
VMMDECL(void) PDMCritSectLeave(PPDMCRITSECT pCritSect)
{
    AssertMsg(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC, ("%p %RX32\n", pCritSect, pCritSect->s.Core.u32Magic));
    Assert(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC);

    /* Check for NOP sections before asserting ownership. */
    if (pCritSect->s.Core.fFlags & RTCRITSECT_FLAGS_NOP)
        return;

    /*
     * Always check that the caller is the owner (screw performance).
     */
    RTNATIVETHREAD const hNativeSelf = pdmCritSectGetNativeSelf(pCritSect);
    AssertReleaseMsgReturnVoid(pCritSect->s.Core.NativeThreadOwner == hNativeSelf,
                               ("%p %s: %p != %p; cLockers=%d cNestings=%d\n", pCritSect, R3STRING(pCritSect->s.pszName),
                                pCritSect->s.Core.NativeThreadOwner, hNativeSelf,
                                pCritSect->s.Core.cLockers, pCritSect->s.Core.cNestings));
    Assert(pCritSect->s.Core.cNestings >= 1);

    /*
     * Nested leave.
     */
    if (pCritSect->s.Core.cNestings > 1)
    {
        ASMAtomicDecS32(&pCritSect->s.Core.cNestings);
        Assert(pCritSect->s.Core.cNestings >= 1);
        ASMAtomicDecS32(&pCritSect->s.Core.cLockers);
        Assert(pCritSect->s.Core.cLockers >= 0);
        return;
    }

#ifdef IN_RING0
# if 0 /** @todo Make SUPSemEventSignal interrupt safe (handle table++) and enable this for: defined(RT_OS_LINUX) || defined(RT_OS_OS2) */
    if (1) /* SUPSemEventSignal is safe */
# else
    if (ASMIntAreEnabled())
# endif
#endif
#if defined(IN_RING3) || defined(IN_RING0)
    {
        /*
         * Leave for real.
         */
        /* update members. */
# ifdef IN_RING3
        RTSEMEVENT hEventToSignal    = pCritSect->s.EventToSignal;
        pCritSect->s.EventToSignal   = NIL_RTSEMEVENT;
#  if defined(PDMCRITSECT_STRICT)
        if (pCritSect->s.Core.pValidatorRec->hThread != NIL_RTTHREAD)
            RTLockValidatorRecExclReleaseOwnerUnchecked(pCritSect->s.Core.pValidatorRec);
#  endif
        Assert(!pCritSect->s.Core.pValidatorRec || pCritSect->s.Core.pValidatorRec->hThread == NIL_RTTHREAD);
# endif
        ASMAtomicAndU32(&pCritSect->s.Core.fFlags, ~PDMCRITSECT_FLAGS_PENDING_UNLOCK);
        ASMAtomicWriteHandle(&pCritSect->s.Core.NativeThreadOwner, NIL_RTNATIVETHREAD);
        ASMAtomicDecS32(&pCritSect->s.Core.cNestings);
        Assert(pCritSect->s.Core.cNestings == 0);

        /* stop and decrement lockers. */
        STAM_PROFILE_ADV_STOP(&pCritSect->s.StatLocked, l);
        ASMCompilerBarrier();
        if (ASMAtomicDecS32(&pCritSect->s.Core.cLockers) >= 0)
        {
            /* Someone is waiting, wake up one of them. */
            SUPSEMEVENT     hEvent   = (SUPSEMEVENT)pCritSect->s.Core.EventSem;
            PSUPDRVSESSION  pSession = pCritSect->s.CTX_SUFF(pVM)->pSession;
            int rc = SUPSemEventSignal(pSession, hEvent);
            AssertRC(rc);
        }

# ifdef IN_RING3
        /* Signal exit event. */
        if (hEventToSignal != NIL_RTSEMEVENT)
        {
            LogBird(("Signalling %#x\n", hEventToSignal));
            int rc = RTSemEventSignal(hEventToSignal);
            AssertRC(rc);
        }
# endif

# if defined(DEBUG_bird) && defined(IN_RING0)
        VMMTrashVolatileXMMRegs();
# endif
    }
#endif  /* IN_RING3 || IN_RING0 */
#ifdef IN_RING0
    else
#endif
#if defined(IN_RING0) || defined(IN_RC)
    {
        /*
         * Try leave it.
         */
        if (pCritSect->s.Core.cLockers == 0)
        {
            ASMAtomicWriteS32(&pCritSect->s.Core.cNestings, 0);
            RTNATIVETHREAD hNativeThread = pCritSect->s.Core.NativeThreadOwner;
            ASMAtomicAndU32(&pCritSect->s.Core.fFlags, ~PDMCRITSECT_FLAGS_PENDING_UNLOCK);
            STAM_PROFILE_ADV_STOP(&pCritSect->s.StatLocked, l);

            ASMAtomicWriteHandle(&pCritSect->s.Core.NativeThreadOwner, NIL_RTNATIVETHREAD);
            if (ASMAtomicCmpXchgS32(&pCritSect->s.Core.cLockers, -1, 0))
                return;

            /* darn, someone raced in on us. */
            ASMAtomicWriteHandle(&pCritSect->s.Core.NativeThreadOwner, hNativeThread);
            STAM_PROFILE_ADV_START(&pCritSect->s.StatLocked, l);
            Assert(pCritSect->s.Core.cNestings == 0);
            ASMAtomicWriteS32(&pCritSect->s.Core.cNestings, 1);
        }
        ASMAtomicOrU32(&pCritSect->s.Core.fFlags, PDMCRITSECT_FLAGS_PENDING_UNLOCK);

        /*
         * Queue the request.
         */
        PVM         pVM   = pCritSect->s.CTX_SUFF(pVM);     AssertPtr(pVM);
        PVMCPU      pVCpu = VMMGetCpu(pVM);                 AssertPtr(pVCpu);
        uint32_t    i     = pVCpu->pdm.s.cQueuedCritSectLeaves++;
        LogFlow(("PDMCritSectLeave: [%d]=%p => R3\n", i, pCritSect));
        AssertFatal(i < RT_ELEMENTS(pVCpu->pdm.s.apQueuedCritSectsLeaves));
        pVCpu->pdm.s.apQueuedCritSectsLeaves[i] = MMHyperCCToR3(pVM, pCritSect);
        VMCPU_FF_SET(pVCpu, VMCPU_FF_PDM_CRITSECT);
        VMCPU_FF_SET(pVCpu, VMCPU_FF_TO_R3);
        STAM_REL_COUNTER_INC(&pVM->pdm.s.StatQueuedCritSectLeaves);
        STAM_REL_COUNTER_INC(&pCritSect->s.StatContentionRZUnlock);
    }
#endif /* IN_RING0 || IN_RC */
}
Пример #3
0
int main(int argc, char **argv)
{
    bool fSys = true;
    bool fGip = false;
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    fGip = true;
#endif

    /*
     * Init.
     */
    int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
    if (RT_FAILURE(rc))
        return RTMsgInitFailure(rc);

    if (argc == 2 && !strcmp(argv[1], "child"))
    {
        RTThreadSleep(300);
        return 0;
    }

    RTTEST hTest;
    rc = RTTestCreate("tstSupSem", &hTest);
    if (RT_FAILURE(rc))
    {
        RTPrintf("tstSupSem: fatal error: RTTestCreate failed with rc=%Rrc\n", rc);
        return 1;
    }
    g_hTest = hTest;

    PSUPDRVSESSION pSession;
    rc = SUPR3Init(&pSession);
    if (RT_FAILURE(rc))
    {
        RTTestFailed(hTest, "SUPR3Init failed with rc=%Rrc\n", rc);
        return RTTestSummaryAndDestroy(hTest);
    }
    g_pSession = pSession;
    RTTestBanner(hTest);

    /*
     * Basic API checks.
     */
    RTTestSub(hTest, "Single Release Event (SRE) API");
    SUPSEMEVENT hEvent = NIL_SUPSEMEVENT;
    RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent),          VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 0),  VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 1),  VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 2),  VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 8),  VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent,20),  VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent),           VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 0),  VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent),           VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 1),  VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent),           VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 2),  VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent),           VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 8),  VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent),           VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 20), VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent),           VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent,1000),VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent),           VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent),           VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 0),  VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 0),  VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 1),  VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 2),  VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent, 8),  VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventWaitNoResume(pSession, hEvent,20),  VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent),            VINF_OBJECT_DESTROYED);
    RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent),            VERR_INVALID_HANDLE);
    RTTESTI_CHECK_RC(SUPSemEventClose(pSession, NIL_SUPSEMEVENT),   VINF_SUCCESS);

    RTTestSub(hTest, "Multiple Release Event (MRE) API");
    SUPSEMEVENTMULTI hEventMulti = NIL_SUPSEMEVENT;
    RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEventMulti),            VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0),    VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 1),    VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 2),    VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 8),    VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,20),    VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEventMulti),             VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0),    VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0),    VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0),    VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 1),    VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 2),    VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 8),    VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,20),    VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,1000),  VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEventMulti),             VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEventMulti),             VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0),    VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiReset(pSession, hEventMulti),              VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0),    VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 1),    VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 2),    VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 8),    VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,20),    VERR_TIMEOUT);
    RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEventMulti),             VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 0),    VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 1),    VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 2),    VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 8),    VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti, 20),   VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiWaitNoResume(pSession, hEventMulti,1000),  VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEventMulti),              VINF_OBJECT_DESTROYED);
    RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEventMulti),              VERR_INVALID_HANDLE);
    RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, NIL_SUPSEMEVENTMULTI),     VINF_SUCCESS);

#if !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS)
    RTTestSub(hTest, "SRE Interruptibility");
    RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
    g_cMillies = RT_INDEFINITE_WAIT;
    RTTHREAD hThread = NIL_RTTHREAD;
    RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleSRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);
    RTTESTI_CHECK_RC(RTThreadUserWait(hThread, 60*1000), VINF_SUCCESS);
    RTThreadSleep(120);
    RTThreadPoke(hThread);
    int rcThread = VINF_SUCCESS;
    RTTESTI_CHECK_RC(RTThreadWait(hThread, 60*1000, &rcThread), VINF_SUCCESS);
    RTTESTI_CHECK_RC(rcThread, VERR_INTERRUPTED);
    RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);

    RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
    g_cMillies = 120*1000;
    hThread = NIL_RTTHREAD;
    RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleSRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);
    RTTESTI_CHECK_RC(RTThreadUserWait(hThread, 60*1000), VINF_SUCCESS);
    RTThreadSleep(120);
    RTThreadPoke(hThread);
    rcThread = VINF_SUCCESS;
    RTTESTI_CHECK_RC(RTThreadWait(hThread, 60*1000, &rcThread), VINF_SUCCESS);
    RTTESTI_CHECK_RC(rcThread, VERR_INTERRUPTED);
    RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);


    RTTestSub(hTest, "MRE Interruptibility");
    RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEventMulti), VINF_SUCCESS);
    g_cMillies = RT_INDEFINITE_WAIT;
    hThread = NIL_RTTHREAD;
    RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleMRE, (void *)hEventMulti, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntMRE"), VINF_SUCCESS);
    RTTESTI_CHECK_RC(RTThreadUserWait(hThread, 60*1000), VINF_SUCCESS);
    RTThreadSleep(120);
    RTThreadPoke(hThread);
    rcThread = VINF_SUCCESS;
    RTTESTI_CHECK_RC(RTThreadWait(hThread, 60*1000, &rcThread), VINF_SUCCESS);
    RTTESTI_CHECK_RC(rcThread, VERR_INTERRUPTED);
    RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEventMulti), VINF_OBJECT_DESTROYED);

    RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEventMulti), VINF_SUCCESS);
    g_cMillies = 120*1000;
    hThread = NIL_RTTHREAD;
    RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleMRE, (void *)hEventMulti, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntMRE"), VINF_SUCCESS);
    RTTESTI_CHECK_RC(RTThreadUserWait(hThread, 60*1000), VINF_SUCCESS);
    RTThreadSleep(120);
    RTThreadPoke(hThread);
    rcThread = VINF_SUCCESS;
    RTTESTI_CHECK_RC(RTThreadWait(hThread, 60*1000, &rcThread), VINF_SUCCESS);
    RTTESTI_CHECK_RC(rcThread, VERR_INTERRUPTED);
    RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEventMulti), VINF_OBJECT_DESTROYED);

    /*
     * Fork test.
     * Spawn a thread waiting for an event, then spawn a new child process (of
     * ourselves) and make sure that this does not alter the intended behaviour
     * of our event semaphore implementation (see @bugref{5090}).
     */
    RTTestSub(hTest, "SRE Process Spawn");
    hThread = NIL_RTTHREAD;
    g_cMillies = 120*1000;
    RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);
    RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleSRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);

    const char *apszArgs[3] = { argv[0], "child", NULL };
    RTPROCESS Process = NIL_RTPROCESS;
    RTThreadSleep(250);
    RTTESTI_CHECK_RC(RTProcCreate(apszArgs[0], apszArgs, RTENV_DEFAULT, 0, &Process), VINF_SUCCESS);

    RTThreadSleep(250);
    RTTESTI_CHECK_RC(SUPSemEventSignal(pSession, hEvent), VINF_SUCCESS);

    rcThread = VERR_GENERAL_FAILURE;
    RTTESTI_CHECK_RC(RTThreadWait(hThread, 120*1000, &rcThread), VINF_SUCCESS);
    RTTESTI_CHECK_RC(rcThread, VINF_SUCCESS);
    RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);


    RTTestSub(hTest, "MRE Process Spawn");
    hThread = NIL_RTTHREAD;
    g_cMillies = 120*1000;
    RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEvent), VINF_SUCCESS);
    RTTESTI_CHECK_RC(RTThreadCreate(&hThread, tstSupSemInterruptibleMRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);

    RTTHREAD hThread2 = NIL_RTTHREAD;
    RTTESTI_CHECK_RC(RTThreadCreate(&hThread2, tstSupSemInterruptibleMRE, (void *)hEvent, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "IntSRE"), VINF_SUCCESS);

    Process = NIL_RTPROCESS;
    RTThreadSleep(250);
    RTTESTI_CHECK_RC(RTProcCreate(apszArgs[0], apszArgs, RTENV_DEFAULT, 0, &Process), VINF_SUCCESS);

    RTThreadSleep(250);
    RTTESTI_CHECK_RC(SUPSemEventMultiSignal(pSession, hEvent), VINF_SUCCESS);

    rcThread = VERR_GENERAL_FAILURE;
    RTTESTI_CHECK_RC(RTThreadWait(hThread, 120*1000, &rcThread), VINF_SUCCESS);
    RTTESTI_CHECK_RC(rcThread, VINF_SUCCESS);

    int rcThread2 = VERR_GENERAL_FAILURE;
    RTTESTI_CHECK_RC(RTThreadWait(hThread2, 120*1000, &rcThread2), VINF_SUCCESS);
    RTTESTI_CHECK_RC(rcThread2, VINF_SUCCESS);

    RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEvent), VINF_OBJECT_DESTROYED);

#endif /* !OS2 && !WINDOWS */

    {

#define LOOP_COUNT 20
        static unsigned const s_acMsIntervals[] = { 0, 1, 2, 3, 4, 8, 10, 16, 32 };
        if (RTTestErrorCount(hTest) == 0)
        {
            RTTestSub(hTest, "SRE Timeout Accuracy (ms)");
            RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);

            uint32_t cInterrupted = 0;
            for (unsigned i = 0; i < RT_ELEMENTS(s_acMsIntervals); i++)
            {
                uint64_t cMs        = s_acMsIntervals[i];
                uint64_t cNsMinSys  = UINT64_MAX;
                uint64_t cNsMin     = UINT64_MAX;
                uint64_t cNsTotalSys= 0;
                uint64_t cNsTotal   = 0;
                unsigned cLoops     = 0;
                while (cLoops < LOOP_COUNT)
                {
                    uint64_t u64StartSys = RTTimeSystemNanoTS();
                    uint64_t u64Start    = RTTimeNanoTS();
                    int rcX = SUPSemEventWaitNoResume(pSession, hEvent, cMs);
                    uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
                    uint64_t cNsElapsed    = RTTimeNanoTS()       - u64Start;

                    if (rcX == VERR_INTERRUPTED)
                    {
                        cInterrupted++;
                        continue; /* retry */
                    }
                    if (rcX != VERR_TIMEOUT)
                        RTTestFailed(hTest, "%Rrc cLoops=%u cMs=%u", rcX, cLoops, cMs);

                    if (cNsElapsedSys < cNsMinSys)
                        cNsMinSys = cNsElapsedSys;
                    if (cNsElapsed < cNsMin)
                        cNsMin = cNsElapsed;
                    cNsTotalSys += cNsElapsedSys;
                    cNsTotal    += cNsElapsed;
                    cLoops++;
                }
                if (fSys)
                {
                    RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS,            "%u ms min (clock=sys)", cMs);
                    RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%u ms avg (clock=sys)", cMs);
                }
                if (fGip)
                {
                    RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS,               "%u ms min (clock=gip)", cMs);
                    RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS,    "%u ms avg (clock=gip)", cMs);
                }
            }

            RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
            RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
        }

        if (RTTestErrorCount(hTest) == 0)
        {
            RTTestSub(hTest, "MRE Timeout Accuracy (ms)");
            RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEvent), VINF_SUCCESS);

            uint32_t cInterrupted = 0;
            for (unsigned i = 0; i < RT_ELEMENTS(s_acMsIntervals); i++)
            {
                uint64_t cMs        = s_acMsIntervals[i];
                uint64_t cNsMinSys  = UINT64_MAX;
                uint64_t cNsMin     = UINT64_MAX;
                uint64_t cNsTotalSys= 0;
                uint64_t cNsTotal   = 0;
                unsigned cLoops     = 0;
                while (cLoops < LOOP_COUNT)
                {
                    uint64_t u64StartSys = RTTimeSystemNanoTS();
                    uint64_t u64Start    = RTTimeNanoTS();
                    int rcX = SUPSemEventMultiWaitNoResume(pSession, hEvent, cMs);
                    uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
                    uint64_t cNsElapsed    = RTTimeNanoTS()       - u64Start;

                    if (rcX == VERR_INTERRUPTED)
                    {
                        cInterrupted++;
                        continue; /* retry */
                    }
                    if (rcX != VERR_TIMEOUT)
                        RTTestFailed(hTest, "%Rrc cLoops=%u cMs=%u", rcX, cLoops, cMs);

                    if (cNsElapsedSys < cNsMinSys)
                        cNsMinSys = cNsElapsedSys;
                    if (cNsElapsed < cNsMin)
                        cNsMin = cNsElapsed;
                    cNsTotalSys += cNsElapsedSys;
                    cNsTotal    += cNsElapsed;
                    cLoops++;
                }
                if (fSys)
                {
                    RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS,            "%u ms min (clock=sys)", cMs);
                    RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%u ms avg (clock=sys)", cMs);
                }
                if (fGip)
                {
                    RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS,               "%u ms min (clock=gip)", cMs);
                    RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS,    "%u ms avg (clock=gip)", cMs);
                }
            }

            RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
            RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
        }
    }

    {
        static uint32_t const s_acNsIntervals[] =
        {
            0, 1000, 5000, 15000, 30000, 50000, 100000, 250000, 500000, 750000, 900000, 1500000, 2200000
        };

        if (RTTestErrorCount(hTest) == 0)
        {
            RTTestSub(hTest, "SUPSemEventWaitNsRelIntr Accuracy");
            RTTestValueF(hTest, SUPSemEventGetResolution(pSession), RTTESTUNIT_NS, "SRE resolution");
            RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);

            uint32_t cInterrupted = 0;
            for (unsigned i = 0; i < RT_ELEMENTS(s_acNsIntervals); i++)
            {
                uint64_t cNs        = s_acNsIntervals[i];
                uint64_t cNsMinSys  = UINT64_MAX;
                uint64_t cNsMin     = UINT64_MAX;
                uint64_t cNsTotalSys= 0;
                uint64_t cNsTotal   = 0;
                unsigned cLoops     = 0;
                while (cLoops < LOOP_COUNT)
                {
                    uint64_t u64StartSys = RTTimeSystemNanoTS();
                    uint64_t u64Start    = RTTimeNanoTS();
                    int rcX = SUPSemEventWaitNsRelIntr(pSession, hEvent, cNs);
                    uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
                    uint64_t cNsElapsed    = RTTimeNanoTS()       - u64Start;

                    if (rcX == VERR_INTERRUPTED)
                    {
                        cInterrupted++;
                        continue; /* retry */
                    }
                    if (rcX != VERR_TIMEOUT)
                        RTTestFailed(hTest, "%Rrc cLoops=%u cNs=%u", rcX, cLoops, cNs);

                    if (cNsElapsedSys < cNsMinSys)
                        cNsMinSys = cNsElapsedSys;
                    if (cNsElapsed < cNsMin)
                        cNsMin = cNsElapsed;
                    cNsTotalSys += cNsElapsedSys;
                    cNsTotal    += cNsElapsed;
                    cLoops++;
                }
                if (fSys)
                {
                    RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS,            "%'u ns min (clock=sys)", cNs);
                    RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=sys)", cNs);
                }
                if (fGip)
                {
                    RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS,               "%'u ns min (clock=gip)", cNs);
                    RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS,    "%'u ns avg (clock=gip)", cNs);
                }
            }

            RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
            RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
        }

        if (RTTestErrorCount(hTest) == 0)
        {
            RTTestSub(hTest, "SUPSemEventMultiWaitNsRelIntr Accuracy");
            RTTestValueF(hTest, SUPSemEventMultiGetResolution(pSession), RTTESTUNIT_NS, "MRE resolution");
            RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEvent), VINF_SUCCESS);

            uint32_t cInterrupted = 0;
            for (unsigned i = 0; i < RT_ELEMENTS(s_acNsIntervals); i++)
            {
                uint64_t cNs        = s_acNsIntervals[i];
                uint64_t cNsMinSys  = UINT64_MAX;
                uint64_t cNsMin     = UINT64_MAX;
                uint64_t cNsTotalSys= 0;
                uint64_t cNsTotal   = 0;
                unsigned cLoops     = 0;
                while (cLoops < LOOP_COUNT)
                {
                    uint64_t u64StartSys = RTTimeSystemNanoTS();
                    uint64_t u64Start    = RTTimeNanoTS();
                    int rcX = SUPSemEventMultiWaitNsRelIntr(pSession, hEvent, cNs);
                    uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
                    uint64_t cNsElapsed    = RTTimeNanoTS()       - u64Start;

                    if (rcX == VERR_INTERRUPTED)
                    {
                        cInterrupted++;
                        continue; /* retry */
                    }
                    if (rcX != VERR_TIMEOUT)
                        RTTestFailed(hTest, "%Rrc cLoops=%u cNs=%u", rcX, cLoops, cNs);

                    if (cNsElapsedSys < cNsMinSys)
                        cNsMinSys = cNsElapsedSys;
                    if (cNsElapsed < cNsMin)
                        cNsMin = cNsElapsed;
                    cNsTotalSys += cNsElapsedSys;
                    cNsTotal    += cNsElapsed;
                    cLoops++;
                }
                if (fSys)
                {
                    RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS,            "%'u ns min (clock=sys)", cNs);
                    RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=sys)", cNs);
                }
                if (fGip)
                {
                    RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS,               "%'u ns min (clock=gip)", cNs);
                    RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS,    "%'u ns avg (clock=gip)", cNs);
                }
            }

            RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
            RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
        }

        if (RTTestErrorCount(hTest) == 0)
        {
            RTTestSub(hTest, "SUPSemEventWaitNsAbsIntr Accuracy");
            RTTestValueF(hTest, SUPSemEventGetResolution(pSession), RTTESTUNIT_NS, "MRE resolution");
            RTTESTI_CHECK_RC(SUPSemEventCreate(pSession, &hEvent), VINF_SUCCESS);

            uint32_t cInterrupted = 0;
            for (unsigned i = 0; i < RT_ELEMENTS(s_acNsIntervals); i++)
            {
                uint64_t cNs        = s_acNsIntervals[i];
                uint64_t cNsMinSys  = UINT64_MAX;
                uint64_t cNsMin     = UINT64_MAX;
                uint64_t cNsTotalSys= 0;
                uint64_t cNsTotal   = 0;
                unsigned cLoops     = 0;
                while (cLoops < LOOP_COUNT)
                {
                    uint64_t u64StartSys   = RTTimeSystemNanoTS();
                    uint64_t u64Start      = RTTimeNanoTS();
                    uint64_t uAbsDeadline  = (fGip ? u64Start : u64StartSys) + cNs;
                    int rcX = SUPSemEventWaitNsAbsIntr(pSession, hEvent, uAbsDeadline);
                    uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
                    uint64_t cNsElapsed    = RTTimeNanoTS()       - u64Start;

                    if (rcX == VERR_INTERRUPTED)
                    {
                        cInterrupted++;
                        continue; /* retry */
                    }
                    if (rcX != VERR_TIMEOUT)
                        RTTestFailed(hTest, "%Rrc cLoops=%u cNs=%u", rcX, cLoops, cNs);

                    if (cNsElapsedSys < cNsMinSys)
                        cNsMinSys = cNsElapsedSys;
                    if (cNsElapsed < cNsMin)
                        cNsMin = cNsElapsed;
                    cNsTotalSys += cNsElapsedSys;
                    cNsTotal    += cNsElapsed;
                    cLoops++;
                }
                if (fSys)
                {
                    RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS,            "%'u ns min (clock=sys)", cNs);
                    RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=sys)", cNs);
                }
                if (fGip)
                {
                    RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS,               "%'u ns min (clock=gip)", cNs);
                    RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS,    "%'u ns avg (clock=gip)", cNs);
                }
            }

            RTTESTI_CHECK_RC(SUPSemEventClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
            RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
        }


        if (RTTestErrorCount(hTest) == 0)
        {
            RTTestSub(hTest, "SUPSemEventMultiWaitNsAbsIntr Accuracy");
            RTTestValueF(hTest, SUPSemEventMultiGetResolution(pSession), RTTESTUNIT_NS, "MRE resolution");
            RTTESTI_CHECK_RC(SUPSemEventMultiCreate(pSession, &hEvent), VINF_SUCCESS);

            uint32_t cInterrupted = 0;
            for (unsigned i = 0; i < RT_ELEMENTS(s_acNsIntervals); i++)
            {
                uint64_t cNs        = s_acNsIntervals[i];
                uint64_t cNsMinSys  = UINT64_MAX;
                uint64_t cNsMin     = UINT64_MAX;
                uint64_t cNsTotalSys= 0;
                uint64_t cNsTotal   = 0;
                unsigned cLoops     = 0;
                while (cLoops < LOOP_COUNT)
                {
                    uint64_t u64StartSys   = RTTimeSystemNanoTS();
                    uint64_t u64Start      = RTTimeNanoTS();
                    uint64_t uAbsDeadline  = (fGip ? u64Start : u64StartSys) + cNs;
                    int rcX = SUPSemEventMultiWaitNsAbsIntr(pSession, hEvent, uAbsDeadline);
                    uint64_t cNsElapsedSys = RTTimeSystemNanoTS() - u64StartSys;
                    uint64_t cNsElapsed    = RTTimeNanoTS()       - u64Start;

                    if (rcX == VERR_INTERRUPTED)
                    {
                        cInterrupted++;
                        continue; /* retry */
                    }
                    if (rcX != VERR_TIMEOUT)
                        RTTestFailed(hTest, "%Rrc cLoops=%u cNs=%u", rcX, cLoops, cNs);

                    if (cNsElapsedSys < cNsMinSys)
                        cNsMinSys = cNsElapsedSys;
                    if (cNsElapsed < cNsMin)
                        cNsMin = cNsElapsed;
                    cNsTotalSys += cNsElapsedSys;
                    cNsTotal    += cNsElapsed;
                    cLoops++;
                }
                if (fSys)
                {
                    RTTestValueF(hTest, cNsMinSys, RTTESTUNIT_NS,            "%'u ns min (clock=sys)", cNs);
                    RTTestValueF(hTest, cNsTotalSys / cLoops, RTTESTUNIT_NS, "%'u ns avg (clock=sys)", cNs);
                }
                if (fGip)
                {
                    RTTestValueF(hTest, cNsMin, RTTESTUNIT_NS,               "%'u ns min (clock=gip)", cNs);
                    RTTestValueF(hTest, cNsTotal / cLoops, RTTESTUNIT_NS,    "%'u ns avg (clock=gip)", cNs);
                }
            }

            RTTESTI_CHECK_RC(SUPSemEventMultiClose(pSession, hEvent), VINF_OBJECT_DESTROYED);
            RTTestValueF(hTest, cInterrupted, RTTESTUNIT_OCCURRENCES, "VERR_INTERRUPTED returned");
        }

    }


    /*
     * Done.
     */
    return RTTestSummaryAndDestroy(hTest);
}