RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem) { /* * Validate input. */ struct RTSEMEVENTINTERNAL *pThis = hEventSem; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE); #ifdef RTSEMEVENT_STRICT if (pThis->fEverHadSignallers) { int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD); if (RT_FAILURE(rc9)) return rc9; } #endif ASMAtomicWriteU32(&pThis->fSignalled, 1); if (ASMAtomicReadS32(&pThis->cWaiters) < 1) return VINF_SUCCESS; /* somebody is waiting, try wake up one of them. */ long cWoken = sys_futex(&pThis->fSignalled, FUTEX_WAKE, 1, NULL, NULL, 0); if (RT_LIKELY(cWoken >= 0)) return VINF_SUCCESS; if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC)) return VERR_SEM_DESTROYED; return VERR_INVALID_PARAMETER; }
/** * The common thread main function. * This is called by rtThreadNativeMain(). * * @returns The status code of the thread. * pThread is dereference by the thread before returning! * @param pThread The thread structure. * @param NativeThread The native thread id. * @param pszThreadName The name of the thread (purely a dummy for backtrace). */ DECLCALLBACK(DECLHIDDEN(int)) rtThreadMain(PRTTHREADINT pThread, RTNATIVETHREAD NativeThread, const char *pszThreadName) { int rc; NOREF(pszThreadName); rtThreadInsert(pThread, NativeThread); Log(("rtThreadMain: Starting: pThread=%p NativeThread=%RTnthrd Name=%s pfnThread=%p pvUser=%p\n", pThread, NativeThread, pThread->szName, pThread->pfnThread, pThread->pvUser)); /* * Change the priority. */ rc = rtThreadNativeSetPriority(pThread, pThread->enmType); #ifdef IN_RING3 AssertMsgRC(rc, ("Failed to set priority of thread %p (%RTnthrd / %s) to enmType=%d enmPriority=%d rc=%Rrc\n", pThread, NativeThread, pThread->szName, pThread->enmType, g_enmProcessPriority, rc)); #else AssertMsgRC(rc, ("Failed to set priority of thread %p (%RTnthrd / %s) to enmType=%d rc=%Rrc\n", pThread, NativeThread, pThread->szName, pThread->enmType, rc)); #endif /* * Call thread function and terminate when it returns. */ rtThreadSetState(pThread, RTTHREADSTATE_RUNNING); rc = pThread->pfnThread(pThread, pThread->pvUser); /* * Paranoia checks for leftover resources. */ #ifdef RTSEMRW_STRICT int32_t cWrite = ASMAtomicReadS32(&pThread->cWriteLocks); Assert(!cWrite); int32_t cRead = ASMAtomicReadS32(&pThread->cReadLocks); Assert(!cRead); #endif Log(("rtThreadMain: Terminating: rc=%d pThread=%p NativeThread=%RTnthrd Name=%s pfnThread=%p pvUser=%p\n", rc, pThread, NativeThread, pThread->szName, pThread->pfnThread, pThread->pvUser)); rtThreadTerminate(pThread, rc); return rc; }
void ListenerRecord::shutdown() { if (mQEvent != NIL_RTSEMEVENT) { /* Grab the event semaphore. Must do this while owning the CS or we'll be racing user wanting to use the handle. */ ::RTCritSectEnter(&mcsQLock); RTSEMEVENT hEvt = mQEvent; mQEvent = NIL_RTSEMEVENT; ::RTCritSectLeave(&mcsQLock); /* * Signal waiters and wait for them and any other signallers to stop using the sempahore. * * Note! RTSemEventDestroy does not necessarily guarantee that waiting threads are * out of RTSemEventWait or even woken up when it returns. Darwin is (or was?) * an example of this, the result was undesirable freezes on shutdown. */ int32_t cBusy = ASMAtomicReadS32(&mQEventBusyCnt); if (cBusy > 0) { Log(("Wait for %d waiters+signalers to release.\n", cBusy)); while (cBusy-- > 0) ::RTSemEventSignal(hEvt); for (uint32_t cLoops = 0;; cLoops++) { RTThreadSleep(RT_MIN(8, cLoops)); if (ASMAtomicReadS32(&mQEventBusyCnt) <= 0) break; ::RTSemEventSignal(hEvt); /* (Technically unnecessary, but just in case.) */ } Log(("All waiters+signalers just released the lock.\n")); } ::RTSemEventDestroy(hEvt); } }
/** * Yield the critical section if someone is waiting on it. * * When yielding, we'll leave the critical section and try to make sure the * other waiting threads get a chance of entering before we reclaim it. * * @retval true if yielded. * @retval false if not yielded. * @param pCritSect The critical section. */ VMMR3DECL(bool) PDMR3CritSectYield(PPDMCRITSECT pCritSect) { AssertPtrReturn(pCritSect, false); AssertReturn(pCritSect->s.Core.u32Magic == RTCRITSECT_MAGIC, false); Assert(pCritSect->s.Core.NativeThreadOwner == RTThreadNativeSelf()); Assert(!(pCritSect->s.Core.fFlags & RTCRITSECT_FLAGS_NOP)); /* No recursion allowed here. */ int32_t const cNestings = pCritSect->s.Core.cNestings; AssertReturn(cNestings == 1, false); int32_t const cLockers = ASMAtomicReadS32(&pCritSect->s.Core.cLockers); if (cLockers < cNestings) return false; #ifdef PDMCRITSECT_STRICT RTLOCKVALSRCPOS const SrcPos = pCritSect->s.Core.pValidatorRec->SrcPos; #endif PDMCritSectLeave(pCritSect); /* * If we're lucky, then one of the waiters has entered the lock already. * We spin a little bit in hope for this to happen so we can avoid the * yield detour. */ if (ASMAtomicUoReadS32(&pCritSect->s.Core.cNestings) == 0) { int cLoops = 20; while ( cLoops > 0 && ASMAtomicUoReadS32(&pCritSect->s.Core.cNestings) == 0 && ASMAtomicUoReadS32(&pCritSect->s.Core.cLockers) >= 0) { ASMNopPause(); cLoops--; } if (cLoops == 0) RTThreadYield(); } #ifdef PDMCRITSECT_STRICT int rc = PDMCritSectEnterDebug(pCritSect, VERR_IGNORED, SrcPos.uId, SrcPos.pszFile, SrcPos.uLine, SrcPos.pszFunction); #else int rc = PDMCritSectEnter(pCritSect, VERR_IGNORED); #endif AssertLogRelRC(rc); return true; }
RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies, PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs) { int rc = VINF_SUCCESS; int cRequestsCompleted = 0; PRTFILEAIOCTXINTERNAL pCtxInt = (PRTFILEAIOCTXINTERNAL)hAioCtx; struct timespec Timeout; struct timespec *pTimeout = NULL; uint64_t StartNanoTS = 0; LogFlowFunc(("hAioCtx=%#p cMinReqs=%zu cMillies=%u pahReqs=%#p cReqs=%zu pcbReqs=%#p\n", hAioCtx, cMinReqs, cMillies, pahReqs, cReqs, pcReqs)); /* Check parameters. */ AssertPtrReturn(pCtxInt, VERR_INVALID_HANDLE); AssertPtrReturn(pcReqs, VERR_INVALID_POINTER); AssertPtrReturn(pahReqs, VERR_INVALID_POINTER); AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER); AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE); rtFileAioCtxDump(pCtxInt); int32_t cRequestsWaiting = ASMAtomicReadS32(&pCtxInt->cRequests); if ( RT_UNLIKELY(cRequestsWaiting <= 0) && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS)) return VERR_FILE_AIO_NO_REQUEST; if (RT_UNLIKELY(cMinReqs > (uint32_t)cRequestsWaiting)) return VERR_INVALID_PARAMETER; if (cMillies != RT_INDEFINITE_WAIT) { Timeout.tv_sec = cMillies / 1000; Timeout.tv_nsec = (cMillies % 1000) * 1000000; pTimeout = &Timeout; StartNanoTS = RTTimeNanoTS(); } /* Wait for at least one. */ if (!cMinReqs) cMinReqs = 1; /* For the wakeup call. */ Assert(pCtxInt->hThreadWait == NIL_RTTHREAD); ASMAtomicWriteHandle(&pCtxInt->hThreadWait, RTThreadSelf()); /* Update the waiting list once before we enter the loop. */ rc = rtFileAioCtxProcessEvents(pCtxInt); while ( cMinReqs && RT_SUCCESS_NP(rc)) { #ifdef RT_STRICT if (RT_UNLIKELY(!pCtxInt->iFirstFree)) { for (unsigned i = 0; i < pCtxInt->cReqsWaitMax; i++) RTAssertMsg2Weak("wait[%d] = %#p\n", i, pCtxInt->apReqs[i]); AssertMsgFailed(("No request to wait for. pReqsWaitHead=%#p pReqsWaitTail=%#p\n", pCtxInt->pReqsWaitHead, pCtxInt->pReqsWaitTail)); } #endif LogFlow(("Waiting for %d requests to complete\n", pCtxInt->iFirstFree)); rtFileAioCtxDump(pCtxInt); ASMAtomicXchgBool(&pCtxInt->fWaiting, true); int rcPosix = aio_suspend((const struct aiocb * const *)pCtxInt->apReqs, pCtxInt->iFirstFree, pTimeout); ASMAtomicXchgBool(&pCtxInt->fWaiting, false); if (rcPosix < 0) { LogFlow(("aio_suspend failed %d nent=%u\n", errno, pCtxInt->iFirstFree)); /* Check that this is an external wakeup event. */ if (errno == EINTR) rc = rtFileAioCtxProcessEvents(pCtxInt); else rc = RTErrConvertFromErrno(errno); } else { /* Requests finished. */ unsigned iReqCurr = 0; unsigned cDone = 0; /* Remove completed requests from the waiting list. */ while ( (iReqCurr < pCtxInt->iFirstFree) && (cDone < cReqs)) { PRTFILEAIOREQINTERNAL pReq = pCtxInt->apReqs[iReqCurr]; int rcReq = aio_error(&pReq->AioCB); if (rcReq != EINPROGRESS) { /* Completed store the return code. */ if (rcReq == 0) { pReq->Rc = VINF_SUCCESS; /* Call aio_return() to free resources. */ pReq->cbTransfered = aio_return(&pReq->AioCB); } else { #if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) pReq->Rc = RTErrConvertFromErrno(errno); #else pReq->Rc = RTErrConvertFromErrno(rcReq); #endif } /* Mark the request as finished. */ RTFILEAIOREQ_SET_STATE(pReq, COMPLETED); cDone++; /* If there are other entries waiting put the head into the now free entry. */ if (pCtxInt->pReqsWaitHead) { PRTFILEAIOREQINTERNAL pReqInsert = pCtxInt->pReqsWaitHead; pCtxInt->pReqsWaitHead = pReqInsert->pNext; if (!pCtxInt->pReqsWaitHead) { /* List is empty now. Clear tail too. */ pCtxInt->pReqsWaitTail = NULL; } pReqInsert->iWaitingList = pReq->iWaitingList; pCtxInt->apReqs[pReqInsert->iWaitingList] = pReqInsert; iReqCurr++; } else { /* * Move the last entry into the current position to avoid holes * but only if it is not the last element already. */ if (pReq->iWaitingList < pCtxInt->iFirstFree - 1) { pCtxInt->apReqs[pReq->iWaitingList] = pCtxInt->apReqs[--pCtxInt->iFirstFree]; pCtxInt->apReqs[pReq->iWaitingList]->iWaitingList = pReq->iWaitingList; } else pCtxInt->iFirstFree--; pCtxInt->apReqs[pCtxInt->iFirstFree] = NULL; } /* Put the request into the completed list. */ pahReqs[cRequestsCompleted++] = pReq; pReq->iWaitingList = RTFILEAIOCTX_WAIT_ENTRY_INVALID; } else iReqCurr++; } AssertMsg((cDone <= cReqs), ("Overflow cReqs=%u cMinReqs=%u cDone=%u\n", cReqs, cDone)); cReqs -= cDone; cMinReqs = RT_MAX(cMinReqs, cDone) - cDone; ASMAtomicSubS32(&pCtxInt->cRequests, cDone); AssertMsg(pCtxInt->cRequests >= 0, ("Finished more requests than currently active\n")); if (!cMinReqs) break; if (cMillies != RT_INDEFINITE_WAIT) { uint64_t TimeDiff; /* Recalculate the timeout. */ TimeDiff = RTTimeSystemNanoTS() - StartNanoTS; Timeout.tv_sec = Timeout.tv_sec - (TimeDiff / 1000000); Timeout.tv_nsec = Timeout.tv_nsec - (TimeDiff % 1000000); } /* Check for new elements. */ rc = rtFileAioCtxProcessEvents(pCtxInt); } } *pcReqs = cRequestsCompleted; Assert(pCtxInt->hThreadWait == RTThreadSelf()); ASMAtomicWriteHandle(&pCtxInt->hThreadWait, NIL_RTTHREAD); rtFileAioCtxDump(pCtxInt); return rc; }
RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies, PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs) { int rc = VINF_SUCCESS; int cRequestsCompleted = 0; /* * Validate the parameters, making sure to always set pcReqs. */ AssertPtrReturn(pcReqs, VERR_INVALID_POINTER); *pcReqs = 0; /* always set */ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx; RTFILEAIOCTX_VALID_RETURN(pCtxInt); AssertPtrReturn(pahReqs, VERR_INVALID_POINTER); AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER); AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE); if (RT_UNLIKELY(ASMAtomicReadS32(&pCtxInt->cRequests) == 0)) return VERR_FILE_AIO_NO_REQUEST; /* * Convert the timeout if specified. */ struct timespec *pTimeout = NULL; struct timespec Timeout = {0,0}; uint64_t StartNanoTS = 0; if (cMillies != RT_INDEFINITE_WAIT) { Timeout.tv_sec = cMillies / 1000; Timeout.tv_nsec = cMillies % 1000 * 1000000; pTimeout = &Timeout; StartNanoTS = RTTimeNanoTS(); } /* Wait for at least one. */ if (!cMinReqs) cMinReqs = 1; /* For the wakeup call. */ Assert(pCtxInt->hThreadWait == NIL_RTTHREAD); ASMAtomicWriteHandle(&pCtxInt->hThreadWait, RTThreadSelf()); while ( cMinReqs && RT_SUCCESS_NP(rc)) { struct kevent aKEvents[AIO_MAXIMUM_REQUESTS_PER_CONTEXT]; int cRequestsToWait = cMinReqs < AIO_MAXIMUM_REQUESTS_PER_CONTEXT ? cReqs : AIO_MAXIMUM_REQUESTS_PER_CONTEXT; int rcBSD; uint64_t StartTime; ASMAtomicXchgBool(&pCtxInt->fWaiting, true); rcBSD = kevent(pCtxInt->iKQueue, NULL, 0, aKEvents, cRequestsToWait, pTimeout); ASMAtomicXchgBool(&pCtxInt->fWaiting, false); if (RT_UNLIKELY(rcBSD < 0)) { rc = RTErrConvertFromErrno(errno); break; } uint32_t const cDone = rcBSD; /* Process received events. */ for (uint32_t i = 0; i < cDone; i++) { PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)aKEvents[i].udata; AssertPtr(pReqInt); Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC); /* * Retrieve the status code here already because the * user may omit the RTFileAioReqGetRC() call and * we will leak kernel resources then. * This will result in errors during submission * of other requests as soon as the max_aio_queue_per_proc * limit is reached. */ int cbTransfered = aio_return(&pReqInt->AioCB); if (cbTransfered < 0) { pReqInt->Rc = RTErrConvertFromErrno(cbTransfered); pReqInt->cbTransfered = 0; } else { pReqInt->Rc = VINF_SUCCESS; pReqInt->cbTransfered = cbTransfered; } RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED); pahReqs[cRequestsCompleted++] = (RTFILEAIOREQ)pReqInt; } /* * Done Yet? If not advance and try again. */ if (cDone >= cMinReqs) break; cMinReqs -= cDone; cReqs -= cDone; if (cMillies != RT_INDEFINITE_WAIT) { /* The API doesn't return ETIMEDOUT, so we have to fix that ourselves. */ uint64_t NanoTS = RTTimeNanoTS(); uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000; if (cMilliesElapsed >= cMillies) { rc = VERR_TIMEOUT; break; } /* The syscall supposedly updates it, but we're paranoid. :-) */ Timeout.tv_sec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) / 1000; Timeout.tv_nsec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) % 1000 * 1000000; } } /* * Update the context state and set the return value. */ *pcReqs = cRequestsCompleted; ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted); Assert(pCtxInt->hThreadWait == RTThreadSelf()); ASMAtomicWriteHandle(&pCtxInt->hThreadWait, NIL_RTTHREAD); /* * Clear the wakeup flag and set rc. */ if ( pCtxInt->fWokenUp && RT_SUCCESS(rc)) { ASMAtomicXchgBool(&pCtxInt->fWokenUp, false); rc = VERR_INTERRUPTED; } return rc; }
RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies, PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs) { int rc = VINF_SUCCESS; int cRequestsCompleted = 0; /* * Validate the parameters, making sure to always set pcReqs. */ AssertPtrReturn(pcReqs, VERR_INVALID_POINTER); *pcReqs = 0; /* always set */ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx; RTFILEAIOCTX_VALID_RETURN(pCtxInt); AssertPtrReturn(pahReqs, VERR_INVALID_POINTER); AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER); AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE); if ( RT_UNLIKELY(ASMAtomicReadS32(&pCtxInt->cRequests) == 0) && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS)) return VERR_FILE_AIO_NO_REQUEST; /* * Convert the timeout if specified. */ struct timespec *pTimeout = NULL; struct timespec Timeout = {0,0}; uint64_t StartNanoTS = 0; if (cMillies != RT_INDEFINITE_WAIT) { Timeout.tv_sec = cMillies / 1000; Timeout.tv_nsec = cMillies % 1000 * 1000000; pTimeout = &Timeout; StartNanoTS = RTTimeNanoTS(); } /* Wait for at least one. */ if (!cMinReqs) cMinReqs = 1; while ( cMinReqs && RT_SUCCESS_NP(rc)) { port_event_t aPortEvents[AIO_MAXIMUM_REQUESTS_PER_CONTEXT]; uint_t cRequests = cMinReqs; int cRequestsToWait = RT_MIN(cReqs, AIO_MAXIMUM_REQUESTS_PER_CONTEXT); int rcSol; uint64_t StartTime; rcSol = port_getn(pCtxInt->iPort, &aPortEvents[0], cRequestsToWait, &cRequests, pTimeout); if (RT_UNLIKELY(rcSol < 0)) rc = RTErrConvertFromErrno(errno); /* Process received events. */ for (uint_t i = 0; i < cRequests; i++) { if (aPortEvents[i].portev_source == PORT_SOURCE_ALERT) { Assert(aPortEvents[i].portev_events == AIO_CONTEXT_WAKEUP_EVENT); rc = VERR_INTERRUPTED; /* We've got interrupted. */ /* Reset the port. */ port_alert(pCtxInt->iPort, PORT_ALERT_SET, 0, NULL); } else { PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)aPortEvents[i].portev_user; AssertPtr(pReqInt); Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC); /* A request has finished. */ pahReqs[cRequestsCompleted++] = pReqInt; /* Mark the request as finished. */ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED); } } /* * Done Yet? If not advance and try again. */ if (cRequests >= cMinReqs) break; cMinReqs -= cRequests; cReqs -= cRequests; if (cMillies != RT_INDEFINITE_WAIT) { uint64_t NanoTS = RTTimeNanoTS(); uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000; /* The syscall supposedly updates it, but we're paranoid. :-) */ if (cMilliesElapsed < cMillies) { Timeout.tv_sec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) / 1000; Timeout.tv_nsec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) % 1000 * 1000000; } else { Timeout.tv_sec = 0; Timeout.tv_nsec = 0; } } } /* * Update the context state and set the return value. */ *pcReqs = cRequestsCompleted; ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted); return rc; }