/** * The guest library uses lazy initialization for VMMDev port and memory, * because these values are provided by the VBoxGuest driver and it might * be loaded later than other drivers. * * The VbglEnter checks the current library status, tries to retrieve these * values and fails if they are unavailable. * */ static void vbglQueryDriverInfo (void) { int rc = VINF_SUCCESS; rc = RTSemMutexRequest(g_vbgldata.mutexDriverInit, RT_INDEFINITE_WAIT); if (RT_FAILURE(rc)) return; if (g_vbgldata.status == VbglStatusReady) { RTSemMutexRelease(g_vbgldata.mutexDriverInit); return; } rc = vbglDriverOpen(&g_vbgldata.driver); if (RT_SUCCESS(rc)) { /* * Try query the port info. */ VBoxGuestPortInfo port; rc = vbglDriverIOCtl (&g_vbgldata.driver, VBOXGUEST_IOCTL_GETVMMDEVPORT, &port, sizeof (port)); if (RT_SUCCESS (rc)) { dprintf (("port = 0x%04X, mem = %p\n", port.portAddress, port.pVMMDevMemory)); g_vbgldata.portVMMDev = (RTIOPORT)port.portAddress; g_vbgldata.pVMMDevMemory = port.pVMMDevMemory; g_vbgldata.status = VbglStatusReady; vbglR0QueryHostVersion(); } } RTSemMutexRelease(g_vbgldata.mutexDriverInit); dprintf (("vbglQueryDriverInfo rc = %d\n", rc)); }
int ThreadTest1(RTTHREAD ThreadSelf, void *pvUser) { uint64_t *pu64 = (uint64_t *)pvUser; for (;;) { int rc = RTSemMutexRequestNoResume(g_hMutex, RT_INDEFINITE_WAIT); if (RT_FAILURE(rc)) { PrintError("%x: RTSemMutexRequestNoResume failed with %Rrc\n", rc); break; } if (ASMAtomicIncU32(&g_cbConcurrent) != 1) { PrintError("g_cbConcurrent=%d after request!\n", g_cbConcurrent); break; } /* * Check for fairness: The values of the threads should not differ too much */ (*pu64)++; /* * Check for correctness: Give other threads a chance. If the implementation is * correct, no other thread will be able to enter this lock now. */ if (g_fYield) RTThreadYield(); if (ASMAtomicDecU32(&g_cbConcurrent) != 0) { PrintError("g_cbConcurrent=%d before release!\n", g_cbConcurrent); break; } rc = RTSemMutexRelease(g_hMutex); if (RT_FAILURE(rc)) { PrintError("%x: RTSemMutexRelease failed with %Rrc\n", rc); break; } if (g_fTerminate) break; } if (!g_fQuiet) RTPrintf("tstSemMutex: Thread %08x exited with %lld\n", ThreadSelf, *pu64); return VINF_SUCCESS; }
/** * Service request callback function. * * @returns VBox status code. * @param pSession The caller's session. * @param u64Arg 64-bit integer argument. * @param pReqHdr The request header. Input / Output. Optional. */ DECLEXPORT(int) TSTRTR0SemMutexSrvReqHandler(PSUPDRVSESSION pSession, uint32_t uOperation, uint64_t u64Arg, PSUPR0SERVICEREQHDR pReqHdr) { NOREF(pSession); if (!VALID_PTR(pReqHdr)) return VERR_INVALID_PARAMETER; char *pszErr = (char *)(pReqHdr + 1); size_t cchErr = pReqHdr->cbReq - sizeof(*pReqHdr); if (cchErr < 32 || cchErr >= 0x10000) return VERR_INVALID_PARAMETER; *pszErr = '\0'; #define SET_ERROR(szFmt) do { if (!*pszErr) RTStrPrintf(pszErr, cchErr, "!" szFmt); } while (0) #define SET_ERROR1(szFmt, a1) do { if (!*pszErr) RTStrPrintf(pszErr, cchErr, "!" szFmt, a1); } while (0) #define SET_ERROR2(szFmt, a1, a2) do { if (!*pszErr) RTStrPrintf(pszErr, cchErr, "!" szFmt, a1, a2); } while (0) #define SET_ERROR3(szFmt, a1, a2, a3) do { if (!*pszErr) RTStrPrintf(pszErr, cchErr, "!" szFmt, a1, a2, a3); } while (0) #define CHECK_RC_BREAK(rc, rcExpect, szOp) \ if ((rc) != (rcExpect)) \ { \ RTStrPrintf(pszErr, cchErr, "!%s -> %Rrc, expected %Rrc. line %u", szOp, rc, rcExpect, __LINE__); \ SUPR0Printf("%s -> %d, expected %d. line %u", szOp, rc, rcExpect, __LINE__); \ break; \ } /* * Set up test timeout (when applicable). */ if (u64Arg > 120) { SET_ERROR1("Timeout is too large (max 120): %lld", u64Arg); return VINF_SUCCESS; } uint64_t const StartTS = RTTimeSystemMilliTS(); uint32_t const cMsMax = (uint32_t)u64Arg * 1000; /* * The big switch. */ RTSEMMUTEX hMtx; int rc; switch (uOperation) { case TSTRTR0SEMMUTEX_SANITY_OK: break; case TSTRTR0SEMMUTEX_SANITY_FAILURE: SET_ERROR1("42failure42%1024s", ""); break; case TSTRTR0SEMMUTEX_BASIC: rc = RTSemMutexCreate(&hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexCreate"); do { /* * The interruptible version first. */ /* simple request and release, polling. */ rc = RTSemMutexRequestNoResume(hMtx, 0); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequestNoResume"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease"); /* simple request and release, wait for ever. */ rc = RTSemMutexRequestNoResume(hMtx, RT_INDEFINITE_WAIT); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequestNoResume(indef_wait)"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease"); /* simple request and release, wait a tiny while. */ rc = RTSemMutexRequestNoResume(hMtx, 133); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequestNoResume(133)"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease"); /* nested request and release. */ rc = RTSemMutexRequestNoResume(hMtx, RT_INDEFINITE_WAIT); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequestNoResume#1"); rc = RTSemMutexRequestNoResume(hMtx, RT_INDEFINITE_WAIT); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequestNoResume#2"); rc = RTSemMutexRequestNoResume(hMtx, RT_INDEFINITE_WAIT); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequestNoResume#3"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease#3"); rc = RTSemMutexRequestNoResume(hMtx, RT_INDEFINITE_WAIT); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequestNoResume#3b"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease#3b"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease#2"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease#1"); /* * The uninterruptible variant. */ /* simple request and release, polling. */ rc = RTSemMutexRequest(hMtx, 0); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequest"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease"); /* simple request and release, wait for ever. */ rc = RTSemMutexRequest(hMtx, RT_INDEFINITE_WAIT); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequest(indef_wait)"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease"); /* simple request and release, wait a tiny while. */ rc = RTSemMutexRequest(hMtx, 133); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequest(133)"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease"); /* nested request and release. */ rc = RTSemMutexRequest(hMtx, RT_INDEFINITE_WAIT); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequest#1"); rc = RTSemMutexRequest(hMtx, RT_INDEFINITE_WAIT); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequest#2"); rc = RTSemMutexRequest(hMtx, RT_INDEFINITE_WAIT); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequest#3"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease#3"); rc = RTSemMutexRequest(hMtx, RT_INDEFINITE_WAIT); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequest#3b"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease#3b"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease#2"); rc = RTSemMutexRelease(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease#1"); } while (false); rc = RTSemMutexDestroy(hMtx); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexDestroy"); break; case TSTRTR0SEMMUTEX_TEST2_SETUP: case TSTRTR0SEMMUTEX_TEST3_SETUP: case TSTRTR0SEMMUTEX_TEST4_SETUP: rc = RTSemMutexCreate(&g_hMtxTest2); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexCreate"); break; case TSTRTR0SEMMUTEX_TEST2_DO: for (unsigned i = 0; i < 200; i++) { if (i & 1) { rc = RTSemMutexRequestNoResume(g_hMtxTest2, RT_INDEFINITE_WAIT); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequestNoResume(,indef_wait)"); } else { rc = RTSemMutexRequestNoResume(g_hMtxTest2, 30000); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequestNoResume(,30000)"); } RTThreadSleep(1); rc = RTSemMutexRelease(g_hMtxTest2); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease"); if ((i % 16) == 15 && RTTimeSystemMilliTS() - StartTS >= cMsMax) break; } break; case TSTRTR0SEMMUTEX_TEST3_DO: for (unsigned i = 0; i < 1000000; i++) { if (i & 1) { rc = RTSemMutexRequestNoResume(g_hMtxTest2, RT_INDEFINITE_WAIT); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequestNoResume(,indef_wait)"); } else { rc = RTSemMutexRequestNoResume(g_hMtxTest2, 30000); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequestNoResume(,30000)"); } rc = RTSemMutexRelease(g_hMtxTest2); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease"); if ((i % 256) == 255 && RTTimeSystemMilliTS() - StartTS >= cMsMax) break; } break; case TSTRTR0SEMMUTEX_TEST4_DO: for (unsigned i = 0; i < 1024; i++) { rc = RTSemMutexRequestNoResume(g_hMtxTest2, (i % 32)); if (rc != VERR_TIMEOUT) { CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRequestNoResume"); RTThreadSleep(1000); rc = RTSemMutexRelease(g_hMtxTest2); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexRelease"); } if (RTTimeSystemMilliTS() - StartTS >= cMsMax) break; } break; case TSTRTR0SEMMUTEX_TEST2_CLEANUP: case TSTRTR0SEMMUTEX_TEST3_CLEANUP: case TSTRTR0SEMMUTEX_TEST4_CLEANUP: rc = RTSemMutexDestroy(g_hMtxTest2); CHECK_RC_BREAK(rc, VINF_SUCCESS, "RTSemMutexCreate"); g_hMtxTest2 = NIL_RTSEMMUTEX; break; default: SET_ERROR1("Unknown test #%d", uOperation); break; } /* The error indicator is the '!' in the message buffer. */ return VINF_SUCCESS; }
static int Test1(unsigned cThreads, unsigned cSeconds, bool fYield, bool fQuiet) { int rc; unsigned i; uint64_t g_au64[32]; RTTHREAD aThreads[RT_ELEMENTS(g_au64)]; AssertRelease(cThreads <= RT_ELEMENTS(g_au64)); /* * Init globals. */ g_fYield = fYield; g_fQuiet = fQuiet; g_fTerminate = false; rc = RTSemMutexCreate(&g_hMutex); if (RT_FAILURE(rc)) return PrintError("RTSemMutexCreate failed (rc=%Rrc)\n", rc); /* * Create the threads and let them block on the mutex. */ rc = RTSemMutexRequest(g_hMutex, RT_INDEFINITE_WAIT); if (RT_FAILURE(rc)) return PrintError("RTSemMutexRequest failed (rc=%Rrc)\n", rc); for (i = 0; i < cThreads; i++) { g_au64[i] = 0; rc = RTThreadCreate(&aThreads[i], ThreadTest1, &g_au64[i], 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "test"); if (RT_FAILURE(rc)) return PrintError("RTThreadCreate failed for thread %u (rc=%Rrc)\n", i, rc); } if (!fQuiet) RTPrintf("tstSemMutex: %zu Threads created. Racing them for %u seconds (%s) ...\n", cThreads, cSeconds, g_fYield ? "yielding" : "no yielding"); uint64_t u64StartTS = RTTimeNanoTS(); rc = RTSemMutexRelease(g_hMutex); if (RT_FAILURE(rc)) PrintError("RTSemMutexRelease failed (rc=%Rrc)\n", rc); RTThreadSleep(cSeconds * 1000); ASMAtomicXchgBool(&g_fTerminate, true); uint64_t ElapsedNS = RTTimeNanoTS() - u64StartTS; for (i = 0; i < cThreads; i++) { rc = RTThreadWait(aThreads[i], 5000, NULL); if (RT_FAILURE(rc)) PrintError("RTThreadWait failed for thread %u (rc=%Rrc)\n", i, rc); } rc = RTSemMutexDestroy(g_hMutex); if (RT_FAILURE(rc)) PrintError("RTSemMutexDestroy failed - %Rrc\n", rc); g_hMutex = NIL_RTSEMMUTEX; if (g_cErrors) RTThreadSleep(100); /* * Collect and display the results. */ uint64_t Total = g_au64[0]; for (i = 1; i < cThreads; i++) Total += g_au64[i]; uint64_t Normal = Total / cThreads; uint64_t MaxDeviation = 0; for (i = 0; i < cThreads; i++) { uint64_t Delta = RT_ABS((int64_t)(g_au64[i] - Normal)); if (Delta > Normal / 2) RTPrintf("tstSemMutex: Warning! Thread %d deviates by more than 50%% - %llu (it) vs. %llu (avg)\n", i, g_au64[i], Normal); if (Delta > MaxDeviation) MaxDeviation = Delta; } RTPrintf("tstSemMutex: Threads: %u Total: %llu Per Sec: %llu Avg: %llu ns Max dev: %llu%%\n", cThreads, Total, Total / cSeconds, ElapsedNS / Total, MaxDeviation * 100 / Normal ); return 0; }