/** * The timer callback for an omni-timer. * * This is responsible for queueing the DPCs for the other CPUs and * perform the callback on the CPU on which it is called. * * @param pDpc The DPC object. * @param pvUser Pointer to the sub-timer. * @param SystemArgument1 Some system stuff. * @param SystemArgument2 Some system stuff. */ static void _stdcall rtTimerNtOmniMasterCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { PRTTIMERNTSUBTIMER pSubTimer = (PRTTIMERNTSUBTIMER)pvUser; PRTTIMER pTimer = pSubTimer->pParent; int iCpuSelf = RTMpCpuIdToSetIndex(RTMpCpuId()); AssertPtr(pTimer); #ifdef RT_STRICT if (KeGetCurrentIrql() < DISPATCH_LEVEL) RTAssertMsg2Weak("rtTimerNtOmniMasterCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL); if (pSubTimer - &pTimer->aSubTimers[0] != iCpuSelf) RTAssertMsg2Weak("rtTimerNtOmniMasterCallback: iCpuSelf=%d pSubTimer=%p / %d\n", iCpuSelf, pSubTimer, pSubTimer - &pTimer->aSubTimers[0]); #endif /* * Check that we haven't been suspended before scheduling the other DPCs * and doing the callout. */ if ( !ASMAtomicUoReadBool(&pTimer->fSuspended) && pTimer->u32Magic == RTTIMER_MAGIC) { RTCPUSET OnlineSet; RTMpGetOnlineSet(&OnlineSet); for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++) if ( RTCpuSetIsMemberByIndex(&OnlineSet, iCpu) && iCpuSelf != iCpu) KeInsertQueueDpc(&pTimer->aSubTimers[iCpu].NtDpc, 0, 0); pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick); } NOREF(pDpc); NOREF(SystemArgument1); NOREF(SystemArgument2); }
/** * The slave DPC callback for an omni timer. * * @param pDpc The DPC object. * @param pvUser Pointer to the sub-timer. * @param SystemArgument1 Some system stuff. * @param SystemArgument2 Some system stuff. */ static void _stdcall rtTimerNtOmniSlaveCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { PRTTIMERNTSUBTIMER pSubTimer = (PRTTIMERNTSUBTIMER)pvUser; PRTTIMER pTimer = pSubTimer->pParent; AssertPtr(pTimer); #ifdef RT_STRICT if (KeGetCurrentIrql() < DISPATCH_LEVEL) RTAssertMsg2Weak("rtTimerNtOmniSlaveCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL); int iCpuSelf = RTMpCpuIdToSetIndex(RTMpCpuId()); if (pSubTimer - &pTimer->aSubTimers[0] != iCpuSelf) RTAssertMsg2Weak("rtTimerNtOmniSlaveCallback: iCpuSelf=%d pSubTimer=%p / %d\n", iCpuSelf, pSubTimer, pSubTimer - &pTimer->aSubTimers[0]); #endif /* * Check that we haven't been suspended before doing the callout. */ if ( !ASMAtomicUoReadBool(&pTimer->fSuspended) && pTimer->u32Magic == RTTIMER_MAGIC) { if (!pTimer->u64NanoInterval) ASMAtomicWriteBool(&pTimer->fSuspended, true); pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick); } NOREF(pDpc); NOREF(SystemArgument1); NOREF(SystemArgument2); }
/** * Timer callback function for the non-omni timers. * * @returns HRTIMER_NORESTART or HRTIMER_RESTART depending on whether it's a one-shot or interval timer. * @param pDpc Pointer to the DPC. * @param pvUser Pointer to our internal timer structure. * @param SystemArgument1 Some system argument. * @param SystemArgument2 Some system argument. */ static void _stdcall rtTimerNtSimpleCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { PRTTIMER pTimer = (PRTTIMER)pvUser; AssertPtr(pTimer); #ifdef RT_STRICT if (KeGetCurrentIrql() < DISPATCH_LEVEL) RTAssertMsg2Weak("rtTimerNtSimpleCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL); #endif /* * Check that we haven't been suspended before doing the callout. */ if ( !ASMAtomicUoReadBool(&pTimer->fSuspended) && pTimer->u32Magic == RTTIMER_MAGIC) { ASMAtomicWriteHandle(&pTimer->aSubTimers[0].hActiveThread, RTThreadNativeSelf()); if (!pTimer->u64NanoInterval) ASMAtomicWriteBool(&pTimer->fSuspended, true); uint64_t iTick = ++pTimer->aSubTimers[0].iTick; if (pTimer->u64NanoInterval) rtTimerNtRearmInternval(pTimer, iTick, &pTimer->aSubTimers[0].NtDpc); pTimer->pfnTimer(pTimer, pTimer->pvUser, iTick); ASMAtomicWriteHandle(&pTimer->aSubTimers[0].hActiveThread, NIL_RTNATIVETHREAD); } NOREF(pDpc); NOREF(SystemArgument1); NOREF(SystemArgument2); }
/** * Terminates the thread database. */ DECLHIDDEN(void) rtThreadTerm(void) { #ifdef IN_RING3 /* we don't cleanup here yet */ #elif defined(IN_RING0) /* just destroy the spinlock and assume the thread is fine... */ RTSpinlockDestroy(g_ThreadSpinlock); g_ThreadSpinlock = NIL_RTSPINLOCK; if (g_ThreadTree != NULL) RTAssertMsg2Weak("WARNING: g_ThreadTree=%p\n", g_ThreadTree); #endif }
/** * Timer callback function for the non-omni timers. * * @returns HRTIMER_NORESTART or HRTIMER_RESTART depending on whether it's a one-shot or interval timer. * @param pDpc Pointer to the DPC. * @param pvUser Pointer to our internal timer structure. * @param SystemArgument1 Some system argument. * @param SystemArgument2 Some system argument. */ static void _stdcall rtTimerNtSimpleCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { PRTTIMER pTimer = (PRTTIMER)pvUser; AssertPtr(pTimer); #ifdef RT_STRICT if (KeGetCurrentIrql() < DISPATCH_LEVEL) RTAssertMsg2Weak("rtTimerNtSimpleCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL); #endif /* * Check that we haven't been suspended before doing the callout. */ if ( !ASMAtomicUoReadBool(&pTimer->fSuspended) && pTimer->u32Magic == RTTIMER_MAGIC) pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->aSubTimers[0].iTick); NOREF(pDpc); NOREF(SystemArgument1); NOREF(SystemArgument2); }
RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags, PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser, const char *pszPassword, PRTPROCESS phProcess) { int rc; /* * Input validation */ AssertPtrReturn(pszExec, VERR_INVALID_POINTER); AssertReturn(*pszExec, VERR_INVALID_PARAMETER); AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER); AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER); AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER); AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER); AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER); AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER); AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER); #if defined(RT_OS_OS2) if (fFlags & RTPROC_FLAGS_DETACHED) return VERR_PROC_DETACH_NOT_SUPPORTED; #endif /* * Get the file descriptors for the handles we've been passed. */ PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr }; int aStdFds[3] = { -1, -1, -1 }; for (int i = 0; i < 3; i++) { if (paHandles[i]) { AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER); switch (paHandles[i]->enmType) { case RTHANDLETYPE_FILE: aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE ? (int)RTFileToNative(paHandles[i]->u.hFile) : -2 /* close it */; break; case RTHANDLETYPE_PIPE: aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE ? (int)RTPipeToNative(paHandles[i]->u.hPipe) : -2 /* close it */; break; case RTHANDLETYPE_SOCKET: aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET ? (int)RTSocketToNative(paHandles[i]->u.hSocket) : -2 /* close it */; break; default: AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER); } /** @todo check the close-on-execness of these handles? */ } } for (int i = 0; i < 3; i++) if (aStdFds[i] == i) aStdFds[i] = -1; for (int i = 0; i < 3; i++) AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i, ("%i := %i not possible because we're lazy\n", i, aStdFds[i]), VERR_NOT_SUPPORTED); /* * Resolve the user id if specified. */ uid_t uid = ~(uid_t)0; gid_t gid = ~(gid_t)0; if (pszAsUser) { rc = rtCheckCredentials(pszAsUser, pszPassword, &gid, &uid); if (RT_FAILURE(rc)) return rc; } /* * Create the child environment if either RTPROC_FLAGS_PROFILE or * RTPROC_FLAGS_ENV_CHANGE_RECORD are in effect. */ RTENV hEnvToUse = hEnv; if ( (fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_PROFILE)) && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT) ) { if (fFlags & RTPROC_FLAGS_PROFILE) rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser); else rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT); if (RT_SUCCESS(rc)) { if ((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) && hEnv != RTENV_DEFAULT) rc = RTEnvApplyChanges(hEnvToUse, hEnv); if (RT_FAILURE(rc)) RTEnvDestroy(hEnvToUse); } if (RT_FAILURE(rc)) return rc; } /* * Check for execute access to the file. */ char szRealExec[RTPATH_MAX]; if (access(pszExec, X_OK)) { rc = errno; if ( !(fFlags & RTPROC_FLAGS_SEARCH_PATH) || rc != ENOENT || RTPathHavePath(pszExec) ) rc = RTErrConvertFromErrno(rc); else { /* search */ char *pszPath = RTEnvDupEx(hEnvToUse, "PATH"); rc = RTPathTraverseList(pszPath, ':', rtPathFindExec, (void *)pszExec, &szRealExec[0]); RTStrFree(pszPath); if (RT_SUCCESS(rc)) pszExec = szRealExec; else rc = rc == VERR_END_OF_STRING ? VERR_FILE_NOT_FOUND : rc; } if (RT_FAILURE(rc)) return rtProcPosixCreateReturn(rc, hEnvToUse, hEnv); } pid_t pid = -1; const char * const *papszEnv = RTEnvGetExecEnvP(hEnvToUse); AssertPtrReturn(papszEnv, rtProcPosixCreateReturn(VERR_INVALID_HANDLE, hEnvToUse, hEnv)); /* * Take care of detaching the process. * * HACK ALERT! Put the process into a new process group with pgid = pid * to make sure it differs from that of the parent process to ensure that * the IPRT waitpid call doesn't race anyone (read XPCOM) doing group wide * waits. setsid() includes the setpgid() functionality. * 2010-10-11 XPCOM no longer waits for anything, but it cannot hurt. */ #ifndef RT_OS_OS2 if (fFlags & RTPROC_FLAGS_DETACHED) { # ifdef RT_OS_SOLARIS int templateFd = -1; if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) { templateFd = rtSolarisContractPreFork(); if (templateFd == -1) return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv); } # endif /* RT_OS_SOLARIS */ pid = fork(); if (!pid) { # ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkChild(templateFd); # endif setsid(); /* see comment above */ pid = -1; /* Child falls through to the actual spawn code below. */ } else { # ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkParent(templateFd, pid); # endif if (pid > 0) { /* Must wait for the temporary process to avoid a zombie. */ int status = 0; pid_t pidChild = 0; /* Restart if we get interrupted. */ do { pidChild = waitpid(pid, &status, 0); } while ( pidChild == -1 && errno == EINTR); /* Assume that something wasn't found. No detailed info. */ if (status) return rtProcPosixCreateReturn(VERR_PROCESS_NOT_FOUND, hEnvToUse, hEnv); if (phProcess) *phProcess = 0; return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv); } return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv); } } #endif /* * Spawn the child. * * Any spawn code MUST not execute any atexit functions if it is for a * detached process. It would lead to running the atexit functions which * make only sense for the parent. libORBit e.g. gets confused by multiple * execution. Remember, there was only a fork() so far, and until exec() * is successfully run there is nothing which would prevent doing anything * silly with the (duplicated) file descriptors. */ #ifdef HAVE_POSIX_SPAWN /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize. */ if ( uid == ~(uid_t)0 && gid == ~(gid_t)0) { /* Spawn attributes. */ posix_spawnattr_t Attr; rc = posix_spawnattr_init(&Attr); if (!rc) { /* Indicate that process group and signal mask are to be changed, and that the child should use default signal actions. */ rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF); Assert(rc == 0); /* The child starts in its own process group. */ if (!rc) { rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */); Assert(rc == 0); } /* Unmask all signals. */ if (!rc) { sigset_t SigMask; sigemptyset(&SigMask); rc = posix_spawnattr_setsigmask(&Attr, &SigMask); Assert(rc == 0); } /* File changes. */ posix_spawn_file_actions_t FileActions; posix_spawn_file_actions_t *pFileActions = NULL; if ((aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1) && !rc) { rc = posix_spawn_file_actions_init(&FileActions); if (!rc) { pFileActions = &FileActions; for (int i = 0; i < 3; i++) { int fd = aStdFds[i]; if (fd == -2) rc = posix_spawn_file_actions_addclose(&FileActions, i); else if (fd >= 0 && fd != i) { rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i); if (!rc) { for (int j = i + 1; j < 3; j++) if (aStdFds[j] == fd) { fd = -1; break; } if (fd >= 0) rc = posix_spawn_file_actions_addclose(&FileActions, fd); } } if (rc) break; } } } if (!rc) rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs, (char * const *)papszEnv); /* cleanup */ int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2); if (pFileActions) { rc2 = posix_spawn_file_actions_destroy(pFileActions); Assert(rc2 == 0); } /* return on success.*/ if (!rc) { /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(0); if (phProcess) *phProcess = pid; return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv); } } /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(124); } else #endif { #ifdef RT_OS_SOLARIS int templateFd = -1; if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) { templateFd = rtSolarisContractPreFork(); if (templateFd == -1) return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv); } #endif /* RT_OS_SOLARIS */ pid = fork(); if (!pid) { #ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkChild(templateFd); #endif /* RT_OS_SOLARIS */ if (!(fFlags & RTPROC_FLAGS_DETACHED)) setpgid(0, 0); /* see comment above */ /* * Change group and user if requested. */ #if 1 /** @todo This needs more work, see suplib/hardening. */ if (pszAsUser) { int ret = initgroups(pszAsUser, gid); if (ret) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } if (gid != ~(gid_t)0) { if (setgid(gid)) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } if (uid != ~(uid_t)0) { if (setuid(uid)) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } #endif /* * Some final profile environment tweaks, if running as user. */ if ( (fFlags & RTPROC_FLAGS_PROFILE) && pszAsUser && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT) ) { rc = rtProcPosixAdjustProfileEnvFromChild(hEnvToUse, fFlags, hEnv); papszEnv = RTEnvGetExecEnvP(hEnvToUse); if (RT_FAILURE(rc) || !papszEnv) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } /* * Unset the signal mask. */ sigset_t SigMask; sigemptyset(&SigMask); rc = sigprocmask(SIG_SETMASK, &SigMask, NULL); Assert(rc == 0); /* * Apply changes to the standard file descriptor and stuff. */ for (int i = 0; i < 3; i++) { int fd = aStdFds[i]; if (fd == -2) close(i); else if (fd >= 0) { int rc2 = dup2(fd, i); if (rc2 != i) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(125); else exit(125); } for (int j = i + 1; j < 3; j++) if (aStdFds[j] == fd) { fd = -1; break; } if (fd >= 0) close(fd); } } /* * Finally, execute the requested program. */ rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv); if (errno == ENOEXEC) { /* This can happen when trying to start a shell script without the magic #!/bin/sh */ RTAssertMsg2Weak("Cannot execute this binary format!\n"); } else RTAssertMsg2Weak("execve returns %d errno=%d\n", rc, errno); RTAssertReleasePanic(); if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(127); else exit(127); } #ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkParent(templateFd, pid); #endif /* RT_OS_SOLARIS */ if (pid > 0) { /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(0); if (phProcess) *phProcess = pid; return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv); } /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(124); return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv); } return rtProcPosixCreateReturn(VERR_NOT_IMPLEMENTED, hEnvToUse, hEnv); }
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; }
/** * The timer callback for an omni-timer. * * This is responsible for queueing the DPCs for the other CPUs and * perform the callback on the CPU on which it is called. * * @param pDpc The DPC object. * @param pvUser Pointer to the sub-timer. * @param SystemArgument1 Some system stuff. * @param SystemArgument2 Some system stuff. */ static void _stdcall rtTimerNtOmniMasterCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { PRTTIMERNTSUBTIMER pSubTimer = (PRTTIMERNTSUBTIMER)pvUser; PRTTIMER pTimer = pSubTimer->pParent; int iCpuSelf = RTMpCpuIdToSetIndex(RTMpCpuId()); AssertPtr(pTimer); #ifdef RT_STRICT if (KeGetCurrentIrql() < DISPATCH_LEVEL) RTAssertMsg2Weak("rtTimerNtOmniMasterCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL); if (pSubTimer - &pTimer->aSubTimers[0] != iCpuSelf) RTAssertMsg2Weak("rtTimerNtOmniMasterCallback: iCpuSelf=%d pSubTimer=%p / %d\n", iCpuSelf, pSubTimer, pSubTimer - &pTimer->aSubTimers[0]); #endif /* * Check that we haven't been suspended before scheduling the other DPCs * and doing the callout. */ if ( !ASMAtomicUoReadBool(&pTimer->fSuspended) && pTimer->u32Magic == RTTIMER_MAGIC) { RTCPUSET OnlineSet; RTMpGetOnlineSet(&OnlineSet); ASMAtomicWriteHandle(&pSubTimer->hActiveThread, RTThreadNativeSelf()); if (pTimer->u64NanoInterval) { /* * Recurring timer. */ for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++) if ( RTCpuSetIsMemberByIndex(&OnlineSet, iCpu) && iCpuSelf != iCpu) KeInsertQueueDpc(&pTimer->aSubTimers[iCpu].NtDpc, 0, 0); uint64_t iTick = ++pSubTimer->iTick; rtTimerNtRearmInternval(pTimer, iTick, &pTimer->aSubTimers[RTMpCpuIdToSetIndex(pTimer->idCpu)].NtDpc); pTimer->pfnTimer(pTimer, pTimer->pvUser, iTick); } else { /* * Single shot timers gets complicated wrt to fSuspended maintance. */ uint32_t cCpus = 0; for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++) if (RTCpuSetIsMemberByIndex(&OnlineSet, iCpu)) cCpus++; ASMAtomicAddS32(&pTimer->cOmniSuspendCountDown, cCpus); for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++) if ( RTCpuSetIsMemberByIndex(&OnlineSet, iCpu) && iCpuSelf != iCpu) if (!KeInsertQueueDpc(&pTimer->aSubTimers[iCpu].NtDpc, 0, 0)) ASMAtomicDecS32(&pTimer->cOmniSuspendCountDown); /* already queued and counted. */ if (ASMAtomicDecS32(&pTimer->cOmniSuspendCountDown) <= 0) ASMAtomicWriteBool(&pTimer->fSuspended, true); pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick); } ASMAtomicWriteHandle(&pSubTimer->hActiveThread, NIL_RTNATIVETHREAD); } NOREF(pDpc); NOREF(SystemArgument1); NOREF(SystemArgument2); }