VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/timer-posix.cpp

Last change on this file was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 27.8 KB
RevLine 
[1]1/* $Id: timer-posix.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
[8245]3 * IPRT - Timer, POSIX.
[1]4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[1]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
[5999]11 *
[96407]12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
[5999]25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
[96407]27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
[5999]29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
[96407]33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
[1]35 */
36
[57358]37
38/*********************************************************************************************************************************
39* Defined Constants And Macros *
40*********************************************************************************************************************************/
[10614]41/** Enables the use of POSIX RT timers. */
[10854]42#ifndef RT_OS_SOLARIS /* Solaris 10 doesn't have SIGEV_THREAD */
[10941]43# define IPRT_WITH_POSIX_TIMERS
[10854]44#endif /* !RT_OS_SOLARIS */
[1]45
[10941]46/** @def RT_TIMER_SIGNAL
47 * The signal number that the timers use.
48 * We currently use SIGALRM for both setitimer and posix real time timers
49 * out of simplicity, but we might want change this later for the posix ones. */
50#ifdef IPRT_WITH_POSIX_TIMERS
51# define RT_TIMER_SIGNAL SIGALRM
52#else
53# define RT_TIMER_SIGNAL SIGALRM
54#endif
[10614]55
[10941]56
[57358]57/*********************************************************************************************************************************
58* Header Files *
59*********************************************************************************************************************************/
[10941]60#define LOG_GROUP RTLOGGROUP_TIMER
[1]61#include <iprt/timer.h>
62#include <iprt/alloc.h>
63#include <iprt/assert.h>
64#include <iprt/thread.h>
65#include <iprt/log.h>
66#include <iprt/asm.h>
67#include <iprt/semaphore.h>
68#include <iprt/string.h>
[10941]69#include <iprt/once.h>
[1]70#include <iprt/err.h>
[44456]71#include <iprt/initterm.h>
[10941]72#include <iprt/critsect.h>
[1807]73#include "internal/magics.h"
[1]74
75#include <unistd.h>
76#include <sys/fcntl.h>
77#include <sys/ioctl.h>
[3672]78#ifdef RT_OS_LINUX
[537]79# include <linux/rtc.h>
[1]80#endif
81#include <sys/time.h>
82#include <signal.h>
83#include <errno.h>
[10941]84#include <pthread.h>
[95190]85#if defined(RT_OS_DARWIN)
86# define sigprocmask pthread_sigmask /* On xnu sigprocmask works on the process, not the calling thread as elsewhere. */
87#endif
[1]88
[10941]89
[57358]90/*********************************************************************************************************************************
91* Global Variables *
92*********************************************************************************************************************************/
[10920]93#ifdef IPRT_WITH_POSIX_TIMERS
[10941]94/** Init the critsect on first call. */
95static RTONCE g_TimerOnce = RTONCE_INITIALIZER;
96/** Global critsect that serializes timer creation and destruction.
97 * This is lazily created on the first RTTimerCreateEx call and will not be
98 * freed up (I'm afraid). */
99static RTCRITSECT g_TimerCritSect;
100/**
101 * Global counter of RTTimer instances. The signal thread is
102 * started when it changes from 0 to 1. The signal thread
103 * terminates when it becomes 0 again.
104 */
105static uint32_t volatile g_cTimerInstances;
[10920]106/** The signal handling thread. */
[10941]107static RTTHREAD g_TimerThread;
[10920]108#endif /* IPRT_WITH_POSIX_TIMERS */
109
[10941]110
[57358]111/*********************************************************************************************************************************
112* Structures and Typedefs *
113*********************************************************************************************************************************/
[1]114/**
115 * The internal representation of a timer handle.
116 */
117typedef struct RTTIMER
118{
119 /** Magic.
120 * This is RTTIMER_MAGIC, but changes to something else before the timer
121 * is destroyed to indicate clearly that thread should exit. */
[1807]122 uint32_t volatile u32Magic;
[14298]123 /** Flag indicating the timer is suspended. */
[1807]124 uint8_t volatile fSuspended;
125 /** Flag indicating that the timer has been destroyed. */
126 uint8_t volatile fDestroyed;
[9932]127#ifndef IPRT_WITH_POSIX_TIMERS /** @todo We have to take the signals on a dedicated timer thread as
128 * we (might) have code assuming that signals doesn't screw around
129 * on existing threads. (It would be sufficient to have one thread
130 * per signal of course since the signal will be masked while it's
[33540]131 * running, however, it may just cause more complications than its
[9932]132 * worth - sigwait/sigwaitinfo work atomically anyway...)
133 * Also, must block the signal in the thread main procedure too. */
[1807]134 /** The timer thread. */
[1]135 RTTHREAD Thread;
[1807]136 /** Event semaphore on which the thread is blocked. */
137 RTSEMEVENT Event;
[10920]138#endif /* !IPRT_WITH_POSIX_TIMERS */
[1]139 /** User argument. */
140 void *pvUser;
141 /** Callback. */
142 PFNRTTIMER pfnTimer;
[1807]143 /** The timer interval. 0 if one-shot. */
144 uint64_t u64NanoInterval;
[9839]145#ifndef IPRT_WITH_POSIX_TIMERS
[1807]146 /** The first shot interval. 0 if ASAP. */
147 uint64_t volatile u64NanoFirst;
[9839]148#endif /* !IPRT_WITH_POSIX_TIMERS */
[9444]149 /** The current timer tick. */
150 uint64_t volatile iTick;
[9839]151#ifndef IPRT_WITH_POSIX_TIMERS
[1]152 /** The error/status of the timer.
153 * Initially -1, set to 0 when the timer have been successfully started, and
154 * to errno on failure in starting the timer. */
[1807]155 int volatile iError;
[10920]156#else /* IPRT_WITH_POSIX_TIMERS */
[10941]157 timer_t NativeTimer;
[10920]158#endif /* IPRT_WITH_POSIX_TIMERS */
[1]159
160} RTTIMER;
161
[10941]162
163
164#ifdef IPRT_WITH_POSIX_TIMERS
165
[1]166/**
[33540]167 * RTOnce callback that initializes the critical section.
[10941]168 *
169 * @returns RTCritSectInit return code.
[43879]170 * @param pvUser NULL, ignored.
[10941]171 *
172 */
[43879]173static DECLCALLBACK(int) rtTimerOnce(void *pvUser)
[10941]174{
[43879]175 NOREF(pvUser);
[10941]176 return RTCritSectInit(&g_TimerCritSect);
177}
178#endif
179
180
181/**
[1]182 * Signal handler which ignore everything it gets.
183 *
184 * @param iSignal The signal number.
185 */
186static void rttimerSignalIgnore(int iSignal)
187{
188 //AssertBreakpoint();
[39091]189 NOREF(iSignal);
[1]190}
191
192
193/**
[10941]194 * RT_TIMER_SIGNAL wait thread.
[1]195 */
[39091]196static DECLCALLBACK(int) rttimerThread(RTTHREAD hThreadSelf, void *pvArg)
[1]197{
[39091]198 NOREF(hThreadSelf); NOREF(pvArg);
[10920]199#ifndef IPRT_WITH_POSIX_TIMERS
[39091]200 PRTTIMER pTimer = (PRTTIMER)pvArg;
[1]201 RTTIMER Timer = *pTimer;
202 Assert(pTimer->u32Magic == RTTIMER_MAGIC);
[10920]203#endif /* !IPRT_WITH_POSIX_TIMERS */
[1]204
205 /*
206 * Install signal handler.
207 */
208 struct sigaction SigAct;
209 memset(&SigAct, 0, sizeof(SigAct));
210 SigAct.sa_flags = SA_RESTART;
211 sigemptyset(&SigAct.sa_mask);
212 SigAct.sa_handler = rttimerSignalIgnore;
[10941]213 if (sigaction(RT_TIMER_SIGNAL, &SigAct, NULL))
[1]214 {
215 SigAct.sa_flags &= ~SA_RESTART;
[10941]216 if (sigaction(RT_TIMER_SIGNAL, &SigAct, NULL))
[1]217 AssertMsgFailed(("sigaction failed, errno=%d\n", errno));
218 }
219
220 /*
[1807]221 * Mask most signals except those which might be used by the pthread implementation (linux).
[1]222 */
223 sigset_t SigSet;
224 sigfillset(&SigSet);
225 sigdelset(&SigSet, SIGTERM);
226 sigdelset(&SigSet, SIGHUP);
227 sigdelset(&SigSet, SIGINT);
228 sigdelset(&SigSet, SIGABRT);
229 sigdelset(&SigSet, SIGKILL);
230#ifdef SIGRTMIN
231 for (int iSig = SIGRTMIN; iSig < SIGRTMAX; iSig++)
232 sigdelset(&SigSet, iSig);
233#endif
234 if (sigprocmask(SIG_SETMASK, &SigSet, NULL))
235 {
[10941]236#ifdef IPRT_WITH_POSIX_TIMERS
237 int rc = RTErrConvertFromErrno(errno);
238#else
[1]239 int rc = pTimer->iError = RTErrConvertFromErrno(errno);
[10941]240#endif
[1]241 AssertMsgFailed(("sigprocmask -> errno=%d\n", errno));
242 return rc;
243 }
244
245 /*
[1807]246 * The work loop.
[1]247 */
[39091]248 RTThreadUserSignal(hThreadSelf);
[10920]249
250#ifndef IPRT_WITH_POSIX_TIMERS
[1807]251 while ( !pTimer->fDestroyed
252 && pTimer->u32Magic == RTTIMER_MAGIC)
[1]253 {
[1807]254 /*
255 * Wait for a start or destroy event.
256 */
257 if (pTimer->fSuspended)
258 {
259 int rc = RTSemEventWait(pTimer->Event, RT_INDEFINITE_WAIT);
260 if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED)
261 {
262 AssertRC(rc);
[10941]263 if (pTimer->fDestroyed)
264 continue;
[1807]265 RTThreadSleep(1000); /* Don't cause trouble! */
266 }
267 if ( pTimer->fSuspended
268 || pTimer->fDestroyed)
269 continue;
270 }
271
272 /*
273 * Start the timer.
274 *
275 * For some SunOS (/SysV?) threading compatibility Linux will only
[10941]276 * deliver the RT_TIMER_SIGNAL to the thread calling setitimer(). Therefore
[1807]277 * we have to call it here.
278 *
[10941]279 * It turns out this might not always be the case, see RT_TIMER_SIGNAL killing
[1807]280 * processes on RH 2.4.21.
281 */
282 struct itimerval TimerVal;
283 if (pTimer->u64NanoFirst)
284 {
285 uint64_t u64 = RT_MAX(1000, pTimer->u64NanoFirst);
286 TimerVal.it_value.tv_sec = u64 / 1000000000;
287 TimerVal.it_value.tv_usec = (u64 % 1000000000) / 1000;
288 }
289 else
290 {
291 TimerVal.it_value.tv_sec = 0;
292 TimerVal.it_value.tv_usec = 10;
293 }
294 if (pTimer->u64NanoInterval)
295 {
296 uint64_t u64 = RT_MAX(1000, pTimer->u64NanoInterval);
297 TimerVal.it_interval.tv_sec = u64 / 1000000000;
298 TimerVal.it_interval.tv_usec = (u64 % 1000000000) / 1000;
299 }
300 else
301 {
302 TimerVal.it_interval.tv_sec = 0;
303 TimerVal.it_interval.tv_usec = 0;
304 }
305
306 if (setitimer(ITIMER_REAL, &TimerVal, NULL))
307 {
308 ASMAtomicXchgU8(&pTimer->fSuspended, true);
309 pTimer->iError = RTErrConvertFromErrno(errno);
[39093]310 RTThreadUserSignal(hThreadSelf);
[1807]311 continue; /* back to suspended mode. */
312 }
313 pTimer->iError = 0;
[39093]314 RTThreadUserSignal(hThreadSelf);
[1]315
[1807]316 /*
317 * Timer Service Loop.
318 */
319 sigemptyset(&SigSet);
[10941]320 sigaddset(&SigSet, RT_TIMER_SIGNAL);
[1807]321 do
322 {
[26257]323 siginfo_t SigInfo;
324 RT_ZERO(SigInfo);
[3672]325#ifdef RT_OS_DARWIN
[1807]326 if (RT_LIKELY(sigwait(&SigSet, &SigInfo.si_signo) >= 0))
327 {
[1]328#else
[1807]329 if (RT_LIKELY(sigwaitinfo(&SigSet, &SigInfo) >= 0))
330 {
[10941]331 if (RT_LIKELY(SigInfo.si_signo == RT_TIMER_SIGNAL))
[1807]332#endif
333 {
334 if (RT_UNLIKELY( pTimer->fSuspended
335 || pTimer->fDestroyed
336 || pTimer->u32Magic != RTTIMER_MAGIC))
337 break;
338
[9444]339 pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->iTick);
[1807]340
341 /* auto suspend one-shot timers. */
342 if (RT_UNLIKELY(!pTimer->u64NanoInterval))
343 {
[10941]344 ASMAtomicWriteU8(&pTimer->fSuspended, true);
[1807]345 break;
346 }
347 }
348 }
349 else if (errno != EINTR)
350 AssertMsgFailed(("sigwaitinfo -> errno=%d\n", errno));
351 } while (RT_LIKELY( !pTimer->fSuspended
352 && !pTimer->fDestroyed
353 && pTimer->u32Magic == RTTIMER_MAGIC));
354
355 /*
356 * Disable the timer.
357 */
358 struct itimerval TimerVal2 = {{0,0}, {0,0}};
359 if (setitimer(ITIMER_REAL, &TimerVal2, NULL))
360 AssertMsgFailed(("setitimer(ITIMER_REAL,&{0}, NULL) failed, errno=%d\n", errno));
361
362 /*
363 * ACK any pending suspend request.
364 */
365 if (!pTimer->fDestroyed)
[1]366 {
[1807]367 pTimer->iError = 0;
[39093]368 RTThreadUserSignal(hThreadSelf);
[1]369 }
370 }
371
372 /*
373 * Exit.
374 */
[1807]375 pTimer->iError = 0;
[39093]376 RTThreadUserSignal(hThreadSelf);
[1807]377
[10920]378#else /* IPRT_WITH_POSIX_TIMERS */
[10941]379
[10920]380 sigemptyset(&SigSet);
381 sigaddset(&SigSet, RT_TIMER_SIGNAL);
382 while (g_cTimerInstances)
383 {
[26257]384 siginfo_t SigInfo;
385 RT_ZERO(SigInfo);
[10920]386 if (RT_LIKELY(sigwaitinfo(&SigSet, &SigInfo) >= 0))
387 {
[13213]388 LogFlow(("rttimerThread: signo=%d pTimer=%p\n", SigInfo.si_signo, SigInfo.si_value.sival_ptr));
[10941]389 if (RT_LIKELY( SigInfo.si_signo == RT_TIMER_SIGNAL
390 && SigInfo.si_code == SI_TIMER)) /* The SI_TIMER check is *essential* because of the pthread_kill. */
[10920]391 {
[13213]392 PRTTIMER pTimer = (PRTTIMER)SigInfo.si_value.sival_ptr;
[10941]393 AssertPtr(pTimer);
[90803]394 if (RT_UNLIKELY( !RT_VALID_PTR(pTimer)
[10941]395 || ASMAtomicUoReadU8(&pTimer->fSuspended)
396 || ASMAtomicUoReadU8(&pTimer->fDestroyed)
[10920]397 || pTimer->u32Magic != RTTIMER_MAGIC))
398 continue;
[10941]399
[10920]400 pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->iTick);
[10941]401
[10920]402 /* auto suspend one-shot timers. */
403 if (RT_UNLIKELY(!pTimer->u64NanoInterval))
[13213]404 ASMAtomicWriteU8(&pTimer->fSuspended, true);
[10920]405 }
406 }
407 }
[10941]408#endif /* IPRT_WITH_POSIX_TIMERS */
[10920]409
410 return VINF_SUCCESS;
411}
412
413
[32572]414RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
[1]415{
416 /*
[9416]417 * We don't support the fancy MP features.
418 */
419 if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
420 return VERR_NOT_SUPPORTED;
421
[44456]422 /*
423 * We need the signal masks to be set correctly, which they won't be in
424 * unobtrusive mode.
425 */
426 if (RTR3InitIsUnobtrusive())
427 return VERR_NOT_SUPPORTED;
428
[10920]429#ifndef IPRT_WITH_POSIX_TIMERS
[9416]430 /*
[1]431 * Check if timer is busy.
432 */
433 struct itimerval TimerVal;
434 if (getitimer(ITIMER_REAL, &TimerVal))
435 {
436 AssertMsgFailed(("getitimer() -> errno=%d\n", errno));
437 return VERR_NOT_IMPLEMENTED;
438 }
[10941]439 if ( TimerVal.it_value.tv_usec
440 || TimerVal.it_value.tv_sec
441 || TimerVal.it_interval.tv_usec
442 || TimerVal.it_interval.tv_sec)
[1]443 {
444 AssertMsgFailed(("A timer is running. System limit is one timer per process!\n"));
445 return VERR_TIMER_BUSY;
446 }
[10920]447#endif /* !IPRT_WITH_POSIX_TIMERS */
[1]448
449 /*
[10941]450 * Block RT_TIMER_SIGNAL from calling thread.
[1]451 */
[537]452 sigset_t SigSet;
453 sigemptyset(&SigSet);
[10941]454 sigaddset(&SigSet, RT_TIMER_SIGNAL);
[537]455 sigprocmask(SIG_BLOCK, &SigSet, NULL);
[1807]456
[10941]457#ifndef IPRT_WITH_POSIX_TIMERS /** @todo combine more of the setitimer/timer_create code. setitimer could also use the global thread. */
[1807]458 /** @todo Move this RTC hack else where... */
[1]459 static bool fDoneRTC;
460 if (!fDoneRTC)
461 {
462 fDoneRTC = true;
463 /* check resolution. */
464 TimerVal.it_interval.tv_sec = 0;
465 TimerVal.it_interval.tv_usec = 1000;
466 TimerVal.it_value = TimerVal.it_interval;
467 if ( setitimer(ITIMER_REAL, &TimerVal, NULL)
468 || getitimer(ITIMER_REAL, &TimerVal)
469 || TimerVal.it_interval.tv_usec > 1000)
470 {
471 /*
472 * Try open /dev/rtc to set the irq rate to 1024 and
473 * turn periodic
474 */
475 Log(("RTTimerCreate: interval={%ld,%ld} trying to adjust /dev/rtc!\n", TimerVal.it_interval.tv_sec, TimerVal.it_interval.tv_usec));
[10941]476# ifdef RT_OS_LINUX
[1]477 int fh = open("/dev/rtc", O_RDONLY);
478 if (fh >= 0)
479 {
480 if ( ioctl(fh, RTC_IRQP_SET, 1024) < 0
481 || ioctl(fh, RTC_PIE_ON, 0) < 0)
482 Log(("RTTimerCreate: couldn't configure rtc! errno=%d\n", errno));
483 ioctl(fh, F_SETFL, O_ASYNC);
484 ioctl(fh, F_SETOWN, getpid());
485 /* not so sure if closing it is a good idea... */
486 //close(fh);
487 }
488 else
489 Log(("RTTimerCreate: couldn't configure rtc! open failed with errno=%d\n", errno));
[10941]490# endif
[1]491 }
492 /* disable it */
493 TimerVal.it_interval.tv_sec = 0;
494 TimerVal.it_interval.tv_usec = 0;
495 TimerVal.it_value = TimerVal.it_interval;
496 setitimer(ITIMER_REAL, &TimerVal, NULL);
497 }
498
499 /*
[1807]500 * Create a new timer.
[1]501 */
502 int rc;
503 PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
504 if (pTimer)
505 {
506 pTimer->u32Magic = RTTIMER_MAGIC;
[1807]507 pTimer->fSuspended = true;
508 pTimer->fDestroyed = false;
509 pTimer->Thread = NIL_RTTHREAD;
510 pTimer->Event = NIL_RTSEMEVENT;
511 pTimer->pfnTimer = pfnTimer;
512 pTimer->pvUser = pvUser;
513 pTimer->u64NanoInterval = u64NanoInterval;
[5449]514 pTimer->u64NanoFirst = 0;
[9444]515 pTimer->iTick = 0;
[1]516 pTimer->iError = 0;
[1807]517 rc = RTSemEventCreate(&pTimer->Event);
518 AssertRC(rc);
[1]519 if (RT_SUCCESS(rc))
520 {
[1807]521 rc = RTThreadCreate(&pTimer->Thread, rttimerThread, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
522 AssertRC(rc);
[1]523 if (RT_SUCCESS(rc))
524 {
[1807]525 /*
526 * Wait for the timer thread to initialize it self.
527 * This might take a little while...
528 */
529 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
530 AssertRC(rc);
[1]531 if (RT_SUCCESS(rc))
532 {
[1807]533 rc = RTThreadUserReset(pTimer->Thread); AssertRC(rc);
534 rc = pTimer->iError;
535 AssertRC(rc);
536 if (RT_SUCCESS(rc))
537 {
538 RTThreadYield(); /* <-- Horrible hack to make tstTimer work. (linux 2.6.12) */
539 *ppTimer = pTimer;
540 return VINF_SUCCESS;
541 }
[1]542 }
[1807]543
544 /* bail out */
545 ASMAtomicXchgU8(&pTimer->fDestroyed, true);
[10941]546 ASMAtomicXchgU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
[1807]547 RTThreadWait(pTimer->Thread, 45*1000, NULL);
[1]548 }
[1807]549 RTSemEventDestroy(pTimer->Event);
550 pTimer->Event = NIL_RTSEMEVENT;
[1]551 }
552 RTMemFree(pTimer);
553 }
554 else
555 rc = VERR_NO_MEMORY;
[10941]556
[10920]557#else /* IPRT_WITH_POSIX_TIMERS */
[10941]558
[9839]559 /*
[10941]560 * Do the global init first.
[9839]561 */
[43879]562 int rc = RTOnce(&g_TimerOnce, rtTimerOnce, NULL);
[10941]563 if (RT_FAILURE(rc))
564 return rc;
565
566 /*
567 * Create a new timer structure.
568 */
[10920]569 LogFlow(("RTTimerCreateEx: u64NanoInterval=%llu fFlags=%lu\n", u64NanoInterval, fFlags));
[9839]570 PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
571 if (pTimer)
572 {
573 /* Initialize timer structure. */
574 pTimer->u32Magic = RTTIMER_MAGIC;
575 pTimer->fSuspended = true;
576 pTimer->fDestroyed = false;
577 pTimer->pfnTimer = pfnTimer;
578 pTimer->pvUser = pvUser;
579 pTimer->u64NanoInterval = u64NanoInterval;
580 pTimer->iTick = 0;
581
[10941]582 /*
583 * Create a timer that deliver RT_TIMER_SIGNAL upon timer expiration.
584 */
585 struct sigevent SigEvt;
586 SigEvt.sigev_notify = SIGEV_SIGNAL;
587 SigEvt.sigev_signo = RT_TIMER_SIGNAL;
588 SigEvt.sigev_value.sival_ptr = pTimer; /* sigev_value gets copied to siginfo. */
589 int err = timer_create(CLOCK_REALTIME, &SigEvt, &pTimer->NativeTimer);
590 if (!err)
[9839]591 {
[10941]592 /*
593 * Increment the timer count, do this behind the critsect to avoid races.
594 */
595 RTCritSectEnter(&g_TimerCritSect);
[10920]596
[10941]597 if (ASMAtomicIncU32(&g_cTimerInstances) != 1)
[10920]598 {
[10941]599 Assert(g_cTimerInstances > 1);
600 RTCritSectLeave(&g_TimerCritSect);
601
602 LogFlow(("RTTimerCreateEx: rc=%Rrc pTimer=%p (thread already running)\n", rc, pTimer));
[10920]603 *ppTimer = pTimer;
604 return VINF_SUCCESS;
605 }
[10941]606
607 /*
608 * Create the signal handling thread. It will wait for the signal
609 * and execute the timer functions.
610 */
611 rc = RTThreadCreate(&g_TimerThread, rttimerThread, NULL, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
612 if (RT_SUCCESS(rc))
613 {
614 rc = RTThreadUserWait(g_TimerThread, 45*1000); /* this better not fail... */
615 if (RT_SUCCESS(rc))
616 {
617 RTCritSectLeave(&g_TimerCritSect);
618
619 LogFlow(("RTTimerCreateEx: rc=%Rrc pTimer=%p (thread already running)\n", rc, pTimer));
620 *ppTimer = pTimer;
621 return VINF_SUCCESS;
622 }
623 /* darn, what do we do here? */
624 }
625
626 /* bail out */
627 ASMAtomicDecU32(&g_cTimerInstances);
628 Assert(!g_cTimerInstances);
629
630 RTCritSectLeave(&g_TimerCritSect);
631
632 timer_delete(pTimer->NativeTimer);
[9839]633 }
[10941]634 else
635 {
636 rc = RTErrConvertFromErrno(err);
637 Log(("RTTimerCreateEx: err=%d (%Rrc)\n", err, rc));
638 }
639
[9839]640 RTMemFree(pTimer);
641 }
642 else
643 rc = VERR_NO_MEMORY;
644
[10920]645#endif /* IPRT_WITH_POSIX_TIMERS */
[1]646 return rc;
647}
648
649
[10941]650RTR3DECL(int) RTTimerDestroy(PRTTIMER pTimer)
[1]651{
652 LogFlow(("RTTimerDestroy: pTimer=%p\n", pTimer));
653
[1807]654 /*
655 * Validate input.
656 */
[197]657 /* NULL is ok. */
658 if (!pTimer)
659 return VINF_SUCCESS;
[1807]660 int rc = VINF_SUCCESS;
661 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
662 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
[10941]663#ifdef IPRT_WITH_POSIX_TIMERS
664 AssertReturn(g_TimerThread != RTThreadSelf(), VERR_INTERNAL_ERROR);
665#else
[1807]666 AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
[10941]667#endif
[197]668
[1]669 /*
[10941]670 * Mark the semaphore as destroyed.
[1]671 */
[10941]672 ASMAtomicWriteU8(&pTimer->fDestroyed, true);
673 ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
674
675#ifdef IPRT_WITH_POSIX_TIMERS
676 /*
677 * Suspend the timer if it's running.
678 */
[89678]679 if (!pTimer->fSuspended)
[1]680 {
[10941]681 struct itimerspec TimerSpec;
682 TimerSpec.it_value.tv_sec = 0;
683 TimerSpec.it_value.tv_nsec = 0;
[89679]684 TimerSpec.it_interval.tv_sec = 0;
685 TimerSpec.it_interval.tv_nsec = 0;
[11133]686 int err = timer_settime(pTimer->NativeTimer, 0, &TimerSpec, NULL); NOREF(err);
[52759]687 AssertMsg(!err, ("%d / %d\n", err, errno));
[10941]688 }
[1]689#endif
[10941]690
691 /*
692 * Poke the thread and wait for it to finish.
693 * This is only done for the last timer when using posix timers.
694 */
695#ifdef IPRT_WITH_POSIX_TIMERS
696 RTTHREAD Thread = NIL_RTTHREAD;
697 RTCritSectEnter(&g_TimerCritSect);
698 if (ASMAtomicDecU32(&g_cTimerInstances) == 0)
699 {
700 Thread = g_TimerThread;
701 g_TimerThread = NIL_RTTHREAD;
[1807]702 }
[10941]703 RTCritSectLeave(&g_TimerCritSect);
704#else /* IPRT_WITH_POSIX_TIMERS */
705 RTTHREAD Thread = pTimer->Thread;
706 rc = RTSemEventSignal(pTimer->Event);
[1807]707 AssertRC(rc);
[10941]708#endif /* IPRT_WITH_POSIX_TIMERS */
709 if (Thread != NIL_RTTHREAD)
710 {
711 /* Signal it so it gets out of the sigwait if it's stuck there... */
712 pthread_kill((pthread_t)RTThreadGetNative(Thread), RT_TIMER_SIGNAL);
[1]713
[10941]714 /*
715 * Wait for the thread to complete.
716 */
717 rc = RTThreadWait(Thread, 30 * 1000, NULL);
718 AssertRC(rc);
719 }
720
721
722 /*
723 * Free up the resources associated with the timer.
724 */
725#ifdef IPRT_WITH_POSIX_TIMERS
726 timer_delete(pTimer->NativeTimer);
727#else
[1807]728 RTSemEventDestroy(pTimer->Event);
729 pTimer->Event = NIL_RTSEMEVENT;
[10941]730#endif /* !IPRT_WITH_POSIX_TIMERS */
[1807]731 if (RT_SUCCESS(rc))
732 RTMemFree(pTimer);
[1]733 return rc;
734}
[1470]735
736
737RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
738{
[1807]739 /*
740 * Validate input.
741 */
742 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
743 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
[9839]744#ifndef IPRT_WITH_POSIX_TIMERS
[1807]745 AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
[10941]746#endif
[1807]747
748 /*
749 * Already running?
750 */
[10941]751 if (!ASMAtomicXchgU8(&pTimer->fSuspended, false))
[1807]752 return VERR_TIMER_ACTIVE;
[10941]753 LogFlow(("RTTimerStart: pTimer=%p u64First=%llu u64NanoInterval=%llu\n", pTimer, u64First, pTimer->u64NanoInterval));
[1807]754
[10941]755#ifndef IPRT_WITH_POSIX_TIMERS
[1807]756 /*
757 * Tell the thread to start servicing the timer.
[10941]758 * Wait for it to ACK the request to avoid reset races.
[1807]759 */
760 RTThreadUserReset(pTimer->Thread);
[9444]761 ASMAtomicUoWriteU64(&pTimer->u64NanoFirst, u64First);
762 ASMAtomicUoWriteU64(&pTimer->iTick, 0);
763 ASMAtomicWriteU8(&pTimer->fSuspended, false);
[1807]764 int rc = RTSemEventSignal(pTimer->Event);
765 if (RT_SUCCESS(rc))
766 {
767 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
768 AssertRC(rc);
769 RTThreadUserReset(pTimer->Thread);
770 }
771 else
772 AssertRC(rc);
[10941]773
[10920]774#else /* IPRT_WITH_POSIX_TIMERS */
[10941]775 /*
776 * Start the timer.
777 */
778 struct itimerspec TimerSpec;
779 TimerSpec.it_value.tv_sec = u64First / 1000000000; /* nanosec => sec */
780 TimerSpec.it_value.tv_nsec = u64First ? u64First % 1000000000 : 10; /* 0 means disable, replace it with 10. */
781 TimerSpec.it_interval.tv_sec = pTimer->u64NanoInterval / 1000000000;
782 TimerSpec.it_interval.tv_nsec = pTimer->u64NanoInterval % 1000000000;
783 int err = timer_settime(pTimer->NativeTimer, 0, &TimerSpec, NULL);
[52759]784 int rc = err == 0 ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
[10920]785#endif /* IPRT_WITH_POSIX_TIMERS */
[9839]786
[10941]787 if (RT_FAILURE(rc))
788 ASMAtomicXchgU8(&pTimer->fSuspended, false);
[1807]789 return rc;
[1470]790}
791
792
793RTDECL(int) RTTimerStop(PRTTIMER pTimer)
794{
[1807]795 /*
796 * Validate input.
797 */
798 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
799 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
800
801 /*
802 * Already running?
803 */
[10941]804 if (ASMAtomicXchgU8(&pTimer->fSuspended, true))
[1807]805 return VERR_TIMER_SUSPENDED;
[10941]806 LogFlow(("RTTimerStop: pTimer=%p\n", pTimer));
[1807]807
[10941]808#ifndef IPRT_WITH_POSIX_TIMERS
[1807]809 /*
810 * Tell the thread to stop servicing the timer.
811 */
812 RTThreadUserReset(pTimer->Thread);
813 ASMAtomicXchgU8(&pTimer->fSuspended, true);
[1810]814 int rc = VINF_SUCCESS;
[1809]815 if (RTThreadSelf() != pTimer->Thread)
[1807]816 {
[10941]817 pthread_kill((pthread_t)RTThreadGetNative(pTimer->Thread), RT_TIMER_SIGNAL);
[1810]818 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
[1807]819 AssertRC(rc);
820 RTThreadUserReset(pTimer->Thread);
821 }
[10941]822
[10920]823#else /* IPRT_WITH_POSIX_TIMERS */
[10941]824 /*
825 * Stop the timer.
826 */
827 struct itimerspec TimerSpec;
828 TimerSpec.it_value.tv_sec = 0;
829 TimerSpec.it_value.tv_nsec = 0;
[89678]830 TimerSpec.it_interval.tv_sec = 0;
831 TimerSpec.it_interval.tv_nsec = 0;
[10941]832 int err = timer_settime(pTimer->NativeTimer, 0, &TimerSpec, NULL);
[52759]833 int rc = err == 0 ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
[10920]834#endif /* IPRT_WITH_POSIX_TIMERS */
[9839]835
[1807]836 return rc;
[1470]837}
[1807]838
[32572]839
840RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
841{
842 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
843 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
[39091]844 NOREF(u64NanoInterval);
[32572]845 return VERR_NOT_SUPPORTED;
846}
847
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use