RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval) { /* * Validate. */ RTTIMER_ASSERT_VALID_RET(pTimer); AssertReturn(u64NanoInterval > 0, VERR_INVALID_PARAMETER); AssertReturn(u64NanoInterval < UINT64_MAX / 8, VERR_INVALID_PARAMETER); AssertReturn(pTimer->cNsInterval, VERR_INVALID_STATE); if (pTimer->fSuspended || pTimer->fSuspendedFromTimer) pTimer->cNsInterval = u64NanoInterval; else { ASMAtomicWriteU64(&pTimer->cNsInterval, u64NanoInterval); ASMAtomicWriteBool(&pTimer->fIntervalChanged, true); if ( !pTimer->fAllCpus && !pTimer->u.Single.nsNextTick && pTimer->hCyclicId != CYCLIC_NONE && rtTimerSolIsCallingFromTimerProc(pTimer)) pTimer->u.Single.nsNextTick = RTTimeSystemNanoTS(); } return VINF_SUCCESS; }
RTDECL(int) RTTimerStop(PRTTIMER pTimer) { RTTIMER_ASSERT_VALID_RET(pTimer); RT_ASSERT_INTS_ON(); if (pTimer->fSuspended) return VERR_TIMER_SUSPENDED; pTimer->fSuspended = true; if (pTimer->pSingleTimer) { mutex_enter(&cpu_lock); cyclic_remove(pTimer->hCyclicId); mutex_exit(&cpu_lock); RTMemFree(pTimer->pSingleTimer); } else if (pTimer->pOmniTimer) { mutex_enter(&cpu_lock); cyclic_remove(pTimer->hCyclicId); mutex_exit(&cpu_lock); RTMemFree(pTimer->pOmniTimer->au64Ticks); RTMemFree(pTimer->pOmniTimer); } return VINF_SUCCESS; }
RTDECL(int) RTTimerDestroy(PRTTIMER pTimer) { if (pTimer == NULL) return VINF_SUCCESS; RTTIMER_ASSERT_VALID_RET(pTimer); RT_ASSERT_INTS_ON(); /* * It is not possible to destroy a timer from it's callback function. * Cyclic makes that impossible (or at least extremely risky). */ AssertReturn(!rtTimerSolIsCallingFromTimerProc(pTimer), VERR_INVALID_CONTEXT); /* * Invalidate the handle, make sure it's stopped and free the associated resources. */ ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC); if ( !pTimer->fSuspended || pTimer->hCyclicId != CYCLIC_NONE) /* 2nd check shouldn't happen */ rtTimerSolStopIt(pTimer); rtTimerSolRelease(pTimer); return VINF_SUCCESS; }
RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval) { RTTIMER_ASSERT_VALID_RET(pTimer); /** @todo implement me! */ return VERR_NOT_SUPPORTED; }
RTDECL(int) RTTimerDestroy(PRTTIMER pTimer) { if (pTimer == NULL) return VINF_SUCCESS; RTTIMER_ASSERT_VALID_RET(pTimer); RT_ASSERT_INTS_ON(); /* * Free the associated resources. */ RTTimerStop(pTimer); ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC); RTMemFree(pTimer); return VINF_SUCCESS; }
RTDECL(int) RTTimerStop(PRTTIMER pTimer) { RTTIMER_ASSERT_VALID_RET(pTimer); RT_ASSERT_INTS_ON(); if (pTimer->fSuspended) return VERR_TIMER_SUSPENDED; /* Trying the cpu_lock stuff and calling cyclic_remove may deadlock the system, so just mark the timer as suspened and deal with it in the callback wrapper function above. */ if (rtTimerSolIsCallingFromTimerProc(pTimer)) pTimer->fSuspendedFromTimer = true; else rtTimerSolStopIt(pTimer); return VINF_SUCCESS; }
RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First) { RTTIMER_ASSERT_VALID_RET(pTimer); RT_ASSERT_INTS_ON(); if (!pTimer->fSuspended) return VERR_TIMER_ACTIVE; /* One-shot timers are not supported by the cyclic system. */ if (pTimer->interval == 0) return VERR_NOT_SUPPORTED; pTimer->fSuspended = false; if (pTimer->fAllCpu) { PRTR0OMNITIMERSOL pOmniTimer = RTMemAllocZ(sizeof(RTR0OMNITIMERSOL)); if (RT_UNLIKELY(!pOmniTimer)) return VERR_NO_MEMORY; pOmniTimer->au64Ticks = RTMemAllocZ(RTMpGetCount() * sizeof(uint64_t)); if (RT_UNLIKELY(!pOmniTimer->au64Ticks)) { RTMemFree(pOmniTimer); return VERR_NO_MEMORY; } /* * Setup omni (all CPU) timer. The Omni-CPU online event will fire * and from there we setup periodic timers per CPU. */ pTimer->pOmniTimer = pOmniTimer; pOmniTimer->u64When = pTimer->interval + RTTimeNanoTS(); cyc_omni_handler_t hOmni; hOmni.cyo_online = rtTimerSolOmniCpuOnline; hOmni.cyo_offline = NULL; hOmni.cyo_arg = pTimer; mutex_enter(&cpu_lock); pTimer->hCyclicId = cyclic_add_omni(&hOmni); mutex_exit(&cpu_lock); } else { int iCpu = SOL_TIMER_ANY_CPU; if (pTimer->fSpecificCpu) { iCpu = pTimer->iCpu; if (!RTMpIsCpuOnline(iCpu)) /* ASSUMES: index == cpuid */ return VERR_CPU_OFFLINE; } PRTR0SINGLETIMERSOL pSingleTimer = RTMemAllocZ(sizeof(RTR0SINGLETIMERSOL)); if (RT_UNLIKELY(!pSingleTimer)) return VERR_NO_MEMORY; pTimer->pSingleTimer = pSingleTimer; pSingleTimer->hHandler.cyh_func = rtTimerSolCallbackWrapper; pSingleTimer->hHandler.cyh_arg = pTimer; pSingleTimer->hHandler.cyh_level = CY_LOCK_LEVEL; mutex_enter(&cpu_lock); if (iCpu != SOL_TIMER_ANY_CPU && !cpu_is_online(cpu[iCpu])) { mutex_exit(&cpu_lock); RTMemFree(pSingleTimer); pTimer->pSingleTimer = NULL; return VERR_CPU_OFFLINE; } pSingleTimer->hFireTime.cyt_when = u64First + RTTimeNanoTS(); if (pTimer->interval == 0) { /** @todo use gethrtime_max instead of LLONG_MAX? */ AssertCompileSize(pSingleTimer->hFireTime.cyt_interval, sizeof(long long)); pSingleTimer->hFireTime.cyt_interval = LLONG_MAX - pSingleTimer->hFireTime.cyt_when; } else pSingleTimer->hFireTime.cyt_interval = pTimer->interval; pTimer->hCyclicId = cyclic_add(&pSingleTimer->hHandler, &pSingleTimer->hFireTime); if (iCpu != SOL_TIMER_ANY_CPU) cyclic_bind(pTimer->hCyclicId, cpu[iCpu], NULL /* cpupart */); mutex_exit(&cpu_lock); } return VINF_SUCCESS; }
RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First) { RTTIMER_ASSERT_VALID_RET(pTimer); RT_ASSERT_INTS_ON(); /* * It's not possible to restart a one-shot time from it's callback function, * at least not at the moment. */ AssertReturn(!rtTimerSolIsCallingFromTimerProc(pTimer), VERR_INVALID_CONTEXT); mutex_enter(&cpu_lock); /* * Make sure it's not active already. If it was suspended from a timer * callback function, we need to do some cleanup work here before we can * restart the timer. */ if (!pTimer->fSuspended) { if (!pTimer->fSuspendedFromTimer) { mutex_exit(&cpu_lock); return VERR_TIMER_ACTIVE; } cyclic_remove(pTimer->hCyclicId); pTimer->hCyclicId = CYCLIC_NONE; } pTimer->fSuspended = false; pTimer->fSuspendedFromTimer = false; pTimer->fIntervalChanged = false; if (pTimer->fAllCpus) { /* * Setup omni (all CPU) timer. The Omni-CPU online event will fire * and from there we setup periodic timers per CPU. */ pTimer->u.Omni.u64When = RTTimeSystemNanoTS() + (u64First ? u64First : pTimer->cNsInterval); cyc_omni_handler_t HandlerOmni; HandlerOmni.cyo_online = rtTimerSolOmniCpuOnline; HandlerOmni.cyo_offline = NULL; HandlerOmni.cyo_arg = pTimer; pTimer->hCyclicId = cyclic_add_omni(&HandlerOmni); } else { cyc_handler_t Handler; cyc_time_t FireTime; /* * Setup a single CPU timer. If a specific CPU was requested, it * must be online or the timer cannot start. */ if ( pTimer->fSpecificCpu && !RTMpIsCpuOnline(pTimer->iCpu)) /* ASSUMES: index == cpuid */ { pTimer->fSuspended = true; mutex_exit(&cpu_lock); return VERR_CPU_OFFLINE; } Handler.cyh_func = (cyc_func_t)rtTimerSolSingleCallbackWrapper; Handler.cyh_arg = pTimer; Handler.cyh_level = CY_LOCK_LEVEL; /* * Use a large interval (1 hour) so that we don't get a timer-callback between * cyclic_add() and cyclic_bind(). Program the correct interval once cyclic_bind() is done. * See @bugref{7691#c20}. */ if (!pTimer->fSpecificCpu) FireTime.cyt_when = RTTimeSystemNanoTS() + u64First; else FireTime.cyt_when = RTTimeSystemNanoTS() + u64First + RT_NS_1HOUR; FireTime.cyt_interval = pTimer->cNsInterval != 0 ? pTimer->cNsInterval : CY_INFINITY /* Special value, see cyclic_fire(). */; pTimer->u.Single.u64Tick = 0; pTimer->u.Single.nsNextTick = 0; pTimer->hCyclicId = cyclic_add(&Handler, &FireTime); if (pTimer->fSpecificCpu) { cyclic_bind(pTimer->hCyclicId, cpu[pTimer->iCpu], NULL /* cpupart */); cyclic_reprogram(pTimer->hCyclicId, RTTimeSystemNanoTS() + u64First); } } mutex_exit(&cpu_lock); return VINF_SUCCESS; }