timer-posix.cpp revision cd35edfa8537ae28466f498e22b87190a852a574
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * InnoTek Portable Runtime - Timer, POSIX.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Copyright (C) 2006 InnoTek Systemberatung GmbH
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * available from http://www.virtualbox.org. This file is free software;
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * you can redistribute it and/or modify it under the terms of the GNU
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * General Public License as published by the Free Software Foundation,
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * distribution. VirtualBox OSE is distributed in the hope that it will
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * be useful, but WITHOUT ANY WARRANTY of any kind.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * If you received this file as part of a commercial VirtualBox
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * distribution, then only the terms of your commercial VirtualBox
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * license agreement apply instead of the previous paragraph.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync/*******************************************************************************
6e7c344fc7cdb580356704e8201207b394d367bbvboxsync* Header Files *
f11a163be74ca22a873fc5437f7e2cbf6ab7a564vboxsync*******************************************************************************/
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync/*******************************************************************************
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync* Structures and Typedefs *
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync*******************************************************************************/
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * The internal representation of a timer handle.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsynctypedef struct RTTIMER
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * This is RTTIMER_MAGIC, but changes to something else before the timer
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * is destroyed to indicate clearly that thread should exit. */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync /** Win32 timer id. */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync /** User argument. */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync /** Callback. */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync /** The timeout values for the timer. */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync /** The error/status of the timer.
c62d2520ac91e12cf4665c936f490dd2064152d3vboxsync * Initially -1, set to 0 when the timer have been successfully started, and
51e7ffc68ae0a6122fcfdc746905b6c7dae2c610vboxsync * to errno on failure in starting the timer. */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync volatile int iError;
51e7ffc68ae0a6122fcfdc746905b6c7dae2c610vboxsync/** Timer handle magic. */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Signal handler which ignore everything it gets.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * @param iSignal The signal number.
0bebd3a2671042901f1fcceff14f8c58dd397478vboxsync //AssertBreakpoint();
0bebd3a2671042901f1fcceff14f8c58dd397478vboxsync * SIGALRM wait thread.
0bebd3a2671042901f1fcceff14f8c58dd397478vboxsyncstatic DECLCALLBACK(int) rttimerThread(RTTHREAD Thread, void *pvArg)
51e7ffc68ae0a6122fcfdc746905b6c7dae2c610vboxsync * Install signal handler.
0bebd3a2671042901f1fcceff14f8c58dd397478vboxsync AssertMsgFailed(("sigaction failed, errno=%d\n", errno));
0bebd3a2671042901f1fcceff14f8c58dd397478vboxsync * Mask most signals except those which might be used during
51e7ffc68ae0a6122fcfdc746905b6c7dae2c610vboxsync * termination is by a pthread implementation.
51e7ffc68ae0a6122fcfdc746905b6c7dae2c610vboxsync int rc = pTimer->iError = RTErrConvertFromErrno(errno);
51e7ffc68ae0a6122fcfdc746905b6c7dae2c610vboxsync AssertMsgFailed(("sigprocmask -> errno=%d\n", errno));
51e7ffc68ae0a6122fcfdc746905b6c7dae2c610vboxsync * Start the timer.
56602ba12f2388501d594c83fbbf77b4b16f8f4fvboxsync * For some SunOS (/SysV?) threading compatibility Linux will only
51e7ffc68ae0a6122fcfdc746905b6c7dae2c610vboxsync * deliver the SIGALRM to the thread calling setitimer(). Therefore
51e7ffc68ae0a6122fcfdc746905b6c7dae2c610vboxsync * we have to call it here.
51e7ffc68ae0a6122fcfdc746905b6c7dae2c610vboxsync * It turns out this might not always be the case, see SIGALRM killing
51e7ffc68ae0a6122fcfdc746905b6c7dae2c610vboxsync * processes on RH 2.4.21.
51e7ffc68ae0a6122fcfdc746905b6c7dae2c610vboxsync if (setitimer(ITIMER_REAL, &pTimer->TimerVal, NULL))
51e7ffc68ae0a6122fcfdc746905b6c7dae2c610vboxsync * Signal wait loop-forever.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync AssertMsgFailed(("sigwaitinfo -> errno=%d\n", errno));
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Disable the timer.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync AssertMsgFailed(("setitimer(ITIMER_REAL,&{0}, NULL) failed, errno=%d\n", errno));
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Create a recurring timer.
0bebd3a2671042901f1fcceff14f8c58dd397478vboxsync * @returns iprt status code.
0bebd3a2671042901f1fcceff14f8c58dd397478vboxsync * @param ppTimer Where to store the timer handle.
0bebd3a2671042901f1fcceff14f8c58dd397478vboxsync * @param uMilliesInterval Milliseconds between the timer ticks.
0bebd3a2671042901f1fcceff14f8c58dd397478vboxsync * This is rounded up to the system granularity.
0bebd3a2671042901f1fcceff14f8c58dd397478vboxsync * @param pfnCallback Callback function which shall be scheduled for execution
0bebd3a2671042901f1fcceff14f8c58dd397478vboxsync * on every timer tick.
0bebd3a2671042901f1fcceff14f8c58dd397478vboxsync * @param pvUser User argument for the callback.
0bebd3a2671042901f1fcceff14f8c58dd397478vboxsyncRTR3DECL(int) RTTimerCreate(PRTTIMER *ppTimer, unsigned uMilliesInterval, PFNRTTIMER pfnTimer, void *pvUser)
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Check if timer is busy.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync AssertMsgFailed(("getitimer() -> errno=%d\n", errno));
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync if ( TimerVal.it_value.tv_usec || TimerVal.it_value.tv_sec
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync || TimerVal.it_interval.tv_usec || TimerVal.it_interval.tv_sec
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync AssertMsgFailed(("A timer is running. System limit is one timer per process!\n"));
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Block SIGALRM from calling thread.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync static bool fDoneRTC;
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync /* check resolution. */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Try open /dev/rtc to set the irq rate to 1024 and
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * turn periodic
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync Log(("RTTimerCreate: interval={%ld,%ld} trying to adjust /dev/rtc!\n", TimerVal.it_interval.tv_sec, TimerVal.it_interval.tv_usec));
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync Log(("RTTimerCreate: couldn't configure rtc! errno=%d\n", errno));
c62d2520ac91e12cf4665c936f490dd2064152d3vboxsync /* not so sure if closing it is a good idea... */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync //close(fh);
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync Log(("RTTimerCreate: couldn't configure rtc! open failed with errno=%d\n", errno));
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync /* disable it */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Create new timer.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync pTimer->TimerVal.it_interval.tv_sec = uMilliesInterval / 1000;
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync pTimer->TimerVal.it_interval.tv_usec = (uMilliesInterval % 1000) * 1000;
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync pTimer->TimerVal.it_value = pTimer->TimerVal.it_interval;
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync rc = RTThreadCreate(&pTimer->Thread, rttimerThread, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Wait for the timer to successfully create the timer
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync /** @todo something is may cause this to take very long. We're waiting 30 seconds now and hope that'll workaround it... */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync RTThreadYield(); /* Horrible hack to make tstTimer work. Something is really fucked related to scheduling here! (2.6.12) */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync ASMAtomicXchgU32(&pTimer->u32Magic, RTTIMER_MAGIC + 1);
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync AssertMsgFailed(("Failed to create timer uMilliesInterval=%d. rc=%Vrc\n", uMilliesInterval, rc));
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Stops and destroys a running timer.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * @returns iprt status code.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * @param pTimer Timer to stop and destroy.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync /* NULL is ok. */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Validate input.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Modify the magic and kick it.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync if (ASMAtomicXchgU32(&pTimer->u32Magic, RTTIMER_MAGIC + 1) == RTTIMER_MAGIC)
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync pthread_kill((pthread_t)RTThreadGetNative(pTimer->Thread), SIGALRM);
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Wait for the thread to exit.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync rc = RTThreadWait(pTimer->Thread, 30 * 1000, NULL);
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync || rc == VERR_INVALID_HANDLE /* we don't keep handles around, you gotta wait before it really exits! */)
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync AssertMsgFailed(("Failed to destroy timer %p. rc=%Vrc\n", pTimer, rc));
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync AssertMsgFailed(("Timer %p is already being destroyed!\n", pTimer));
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync AssertMsgFailed(("Bad pTimer pointer %p!\n", pTimer));