timer-posix.cpp revision 67d93f1b0caf207d0e7f1ddf263f8eca84408544
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * IPRT - Timer, POSIX.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
82bcaaf8077ba892f39afb721dca149353c63d2cvboxsync * available from http://www.virtualbox.org. This file is free software;
82bcaaf8077ba892f39afb721dca149353c63d2cvboxsync * you can redistribute it and/or modify it under the terms of the GNU
82bcaaf8077ba892f39afb721dca149353c63d2cvboxsync * General Public License (GPL) as published by the Free Software
82bcaaf8077ba892f39afb721dca149353c63d2cvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
82bcaaf8077ba892f39afb721dca149353c63d2cvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
82bcaaf8077ba892f39afb721dca149353c63d2cvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * The contents of this file may alternatively be used under the terms
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * of the Common Development and Distribution License Version 1.0
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * VirtualBox OSE distribution, in which case the provisions of the
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * CDDL are applicable instead of those of the GPL.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * You may elect to license modified versions of this file under the
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * terms and conditions of either the GPL or the CDDL or both.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * additional information or have any questions.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/*******************************************************************************
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync* Defined Constants And Macros *
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync*******************************************************************************/
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/** Enables the use of POSIX RT timers. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#ifndef RT_OS_SOLARIS /* Solaris 10 doesn't have SIGEV_THREAD */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#endif /* !RT_OS_SOLARIS */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/** @def RT_TIMER_SIGNAL
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * The signal number that the timers use.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * We currently use SIGALRM for both setitimer and posix real time timers
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * out of simplicity, but we might want change this later for the posix ones. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/*******************************************************************************
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync* Header Files *
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync*******************************************************************************/
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/*******************************************************************************
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync* Global Variables *
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync*******************************************************************************/
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/** Init the critsect on first call. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/** Global critsect that serializes timer creation and destruction.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * This is lazily created on the first RTTimerCreateEx call and will not be
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * freed up (I'm afraid). */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Global counter of RTTimer instances. The signal thread is
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * started when it changes from 0 to 1. The signal thread
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * terminates when it becomes 0 again.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/** The signal handling thread. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#endif /* IPRT_WITH_POSIX_TIMERS */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync/*******************************************************************************
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync* Structures and Typedefs *
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync*******************************************************************************/
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * The internal representation of a timer handle.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsynctypedef struct RTTIMER
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * This is RTTIMER_MAGIC, but changes to something else before the timer
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * is destroyed to indicate clearly that thread should exit. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** Flag indicating the the timer is suspended. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** Flag indicating that the timer has been destroyed. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#ifndef IPRT_WITH_POSIX_TIMERS /** @todo We have to take the signals on a dedicated timer thread as
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * we (might) have code assuming that signals doesn't screw around
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * on existing threads. (It would be sufficient to have one thread
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * per signal of course since the signal will be masked while it's
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * running, however, it may just cause more compilcations than its
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * worth - sigwait/sigwaitinfo work atomically anyway...)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Also, must block the signal in the thread main procedure too. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** The timer thread. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** Event semaphore on which the thread is blocked. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#endif /* !IPRT_WITH_POSIX_TIMERS */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** User argument. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** Callback. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** The timer interval. 0 if one-shot. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** The first shot interval. 0 if ASAP. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#endif /* !IPRT_WITH_POSIX_TIMERS */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** The current timer tick. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /** The error/status of the timer.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Initially -1, set to 0 when the timer have been successfully started, and
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * to errno on failure in starting the timer. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync int volatile iError;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#else /* IPRT_WITH_POSIX_TIMERS */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync#endif /* IPRT_WITH_POSIX_TIMERS */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * RTOnce callback that initalizes the critical section.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * @returns RTCritSectInit return code.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * @param pvUser1 NULL, ignopred.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * @param pvUser2 NULL, ignopred.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic DECLCALLBACK(int) rtTimerOnce(void *pvUser1, void *pvUser2)
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Signal handler which ignore everything it gets.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * @param iSignal The signal number.
8ffcab9595cc0d56977968cd496363502fd814aevboxsync //AssertBreakpoint();
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * RT_TIMER_SIGNAL wait thread.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsyncstatic DECLCALLBACK(int) rttimerThread(RTTHREAD Thread, void *pvArg)
8ffcab9595cc0d56977968cd496363502fd814aevboxsync#endif /* !IPRT_WITH_POSIX_TIMERS */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Install signal handler.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync AssertMsgFailed(("sigaction failed, errno=%d\n", errno));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Mask most signals except those which might be used by the pthread implementation (linux).
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync int rc = pTimer->iError = RTErrConvertFromErrno(errno);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync AssertMsgFailed(("sigprocmask -> errno=%d\n", errno));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * The work loop.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Wait for a start or destroy event.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync int rc = RTSemEventWait(pTimer->Event, RT_INDEFINITE_WAIT);
150e55a1de2d8702b09de9dd08e488cc9da197d9vboxsync * Start the timer.
9a12ad9a1028187595f21d9264898220c1ea565fvboxsync * For some SunOS (/SysV?) threading compatibility Linux will only
9a12ad9a1028187595f21d9264898220c1ea565fvboxsync * deliver the RT_TIMER_SIGNAL to the thread calling setitimer(). Therefore
9a12ad9a1028187595f21d9264898220c1ea565fvboxsync * we have to call it here.
b4feef6ee36ff3c271b06e7e52e22580cc66174bvboxsync * It turns out this might not always be the case, see RT_TIMER_SIGNAL killing
9a12ad9a1028187595f21d9264898220c1ea565fvboxsync * processes on RH 2.4.21.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync TimerVal.it_value.tv_usec = (u64 % 1000000000) / 1000;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync uint64_t u64 = RT_MAX(1000, pTimer->u64NanoInterval);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync TimerVal.it_interval.tv_usec = (u64 % 1000000000) / 1000;
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync continue; /* back to suspended mode. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Timer Service Loop.
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if (RT_LIKELY(sigwait(&SigSet, &SigInfo.si_signo) >= 0))
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if (RT_LIKELY(sigwaitinfo(&SigSet, &SigInfo) >= 0))
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync if (RT_LIKELY(SigInfo.si_signo == RT_TIMER_SIGNAL))
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->iTick);
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync /* auto suspend one-shot timers. */
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync AssertMsgFailed(("sigwaitinfo -> errno=%d\n", errno));
d1a00c93378091ef28db9d959b2d692cc8143a07vboxsync * Disable the timer.
while (g_cTimerInstances)
&& SigInfo.si_code == SI_TIMER)) /* The SI_TIMER check is *essential* because of the pthread_kill. */
return VINF_SUCCESS;
RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, unsigned fFlags, PFNRTTIMER pfnTimer, void *pvUser)
return VERR_NOT_SUPPORTED;
#ifndef IPRT_WITH_POSIX_TIMERS
return VERR_NOT_IMPLEMENTED;
return VERR_TIMER_BUSY;
#ifndef IPRT_WITH_POSIX_TIMERS /** @todo combine more of the setitimer/timer_create code. setitimer could also use the global thread. */
static bool fDoneRTC;
if (!fDoneRTC)
fDoneRTC = true;
Log(("RTTimerCreate: interval={%ld,%ld} trying to adjust /dev/rtc!\n", TimerVal.it_interval.tv_sec, TimerVal.it_interval.tv_usec));
# ifdef RT_OS_LINUX
if (fh >= 0)
int rc;
if (pTimer)
rc = RTThreadCreate(&pTimer->Thread, rttimerThread, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
return VINF_SUCCESS;
return rc;
if (pTimer)
if (!err)
return VINF_SUCCESS;
rc = RTThreadCreate(&g_TimerThread, rttimerThread, NULL, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
return VINF_SUCCESS;
return rc;
if (!pTimer)
return VINF_SUCCESS;
#ifdef IPRT_WITH_POSIX_TIMERS
#ifdef IPRT_WITH_POSIX_TIMERS
#ifdef IPRT_WITH_POSIX_TIMERS
#ifdef IPRT_WITH_POSIX_TIMERS
return rc;
#ifndef IPRT_WITH_POSIX_TIMERS
return VERR_TIMER_ACTIVE;
LogFlow(("RTTimerStart: pTimer=%p u64First=%llu u64NanoInterval=%llu\n", pTimer, u64First, pTimer->u64NanoInterval));
#ifndef IPRT_WITH_POSIX_TIMERS
TimerSpec.it_value.tv_nsec = u64First ? u64First % 1000000000 : 10; /* 0 means disable, replace it with 10. */
return rc;
return VERR_TIMER_SUSPENDED;
#ifndef IPRT_WITH_POSIX_TIMERS
return rc;