VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 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
Line 
1/* $Id: timer-posix.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Timer, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
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 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
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
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.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Defined Constants And Macros *
40*********************************************************************************************************************************/
41/** Enables the use of POSIX RT timers. */
42#ifndef RT_OS_SOLARIS /* Solaris 10 doesn't have SIGEV_THREAD */
43# define IPRT_WITH_POSIX_TIMERS
44#endif /* !RT_OS_SOLARIS */
45
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
55
56
57/*********************************************************************************************************************************
58* Header Files *
59*********************************************************************************************************************************/
60#define LOG_GROUP RTLOGGROUP_TIMER
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>
69#include <iprt/once.h>
70#include <iprt/err.h>
71#include <iprt/initterm.h>
72#include <iprt/critsect.h>
73#include "internal/magics.h"
74
75#include <unistd.h>
76#include <sys/fcntl.h>
77#include <sys/ioctl.h>
78#ifdef RT_OS_LINUX
79# include <linux/rtc.h>
80#endif
81#include <sys/time.h>
82#include <signal.h>
83#include <errno.h>
84#include <pthread.h>
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
88
89
90/*********************************************************************************************************************************
91* Global Variables *
92*********************************************************************************************************************************/
93#ifdef IPRT_WITH_POSIX_TIMERS
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;
106/** The signal handling thread. */
107static RTTHREAD g_TimerThread;
108#endif /* IPRT_WITH_POSIX_TIMERS */
109
110
111/*********************************************************************************************************************************
112* Structures and Typedefs *
113*********************************************************************************************************************************/
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. */
122 uint32_t volatile u32Magic;
123 /** Flag indicating the timer is suspended. */
124 uint8_t volatile fSuspended;
125 /** Flag indicating that the timer has been destroyed. */
126 uint8_t volatile fDestroyed;
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
131 * running, however, it may just cause more complications than its
132 * worth - sigwait/sigwaitinfo work atomically anyway...)
133 * Also, must block the signal in the thread main procedure too. */
134 /** The timer thread. */
135 RTTHREAD Thread;
136 /** Event semaphore on which the thread is blocked. */
137 RTSEMEVENT Event;
138#endif /* !IPRT_WITH_POSIX_TIMERS */
139 /** User argument. */
140 void *pvUser;
141 /** Callback. */
142 PFNRTTIMER pfnTimer;
143 /** The timer interval. 0 if one-shot. */
144 uint64_t u64NanoInterval;
145#ifndef IPRT_WITH_POSIX_TIMERS
146 /** The first shot interval. 0 if ASAP. */
147 uint64_t volatile u64NanoFirst;
148#endif /* !IPRT_WITH_POSIX_TIMERS */
149 /** The current timer tick. */
150 uint64_t volatile iTick;
151#ifndef IPRT_WITH_POSIX_TIMERS
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. */
155 int volatile iError;
156#else /* IPRT_WITH_POSIX_TIMERS */
157 timer_t NativeTimer;
158#endif /* IPRT_WITH_POSIX_TIMERS */
159
160} RTTIMER;
161
162
163
164#ifdef IPRT_WITH_POSIX_TIMERS
165
166/**
167 * RTOnce callback that initializes the critical section.
168 *
169 * @returns RTCritSectInit return code.
170 * @param pvUser NULL, ignored.
171 *
172 */
173static DECLCALLBACK(int) rtTimerOnce(void *pvUser)
174{
175 NOREF(pvUser);
176 return RTCritSectInit(&g_TimerCritSect);
177}
178#endif
179
180
181/**
182 * Signal handler which ignore everything it gets.
183 *
184 * @param iSignal The signal number.
185 */
186static void rttimerSignalIgnore(int iSignal)
187{
188 //AssertBreakpoint();
189 NOREF(iSignal);
190}
191
192
193/**
194 * RT_TIMER_SIGNAL wait thread.
195 */
196static DECLCALLBACK(int) rttimerThread(RTTHREAD hThreadSelf, void *pvArg)
197{
198 NOREF(hThreadSelf); NOREF(pvArg);
199#ifndef IPRT_WITH_POSIX_TIMERS
200 PRTTIMER pTimer = (PRTTIMER)pvArg;
201 RTTIMER Timer = *pTimer;
202 Assert(pTimer->u32Magic == RTTIMER_MAGIC);
203#endif /* !IPRT_WITH_POSIX_TIMERS */
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;
213 if (sigaction(RT_TIMER_SIGNAL, &SigAct, NULL))
214 {
215 SigAct.sa_flags &= ~SA_RESTART;
216 if (sigaction(RT_TIMER_SIGNAL, &SigAct, NULL))
217 AssertMsgFailed(("sigaction failed, errno=%d\n", errno));
218 }
219
220 /*
221 * Mask most signals except those which might be used by the pthread implementation (linux).
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 {
236#ifdef IPRT_WITH_POSIX_TIMERS
237 int rc = RTErrConvertFromErrno(errno);
238#else
239 int rc = pTimer->iError = RTErrConvertFromErrno(errno);
240#endif
241 AssertMsgFailed(("sigprocmask -> errno=%d\n", errno));
242 return rc;
243 }
244
245 /*
246 * The work loop.
247 */
248 RTThreadUserSignal(hThreadSelf);
249
250#ifndef IPRT_WITH_POSIX_TIMERS
251 while ( !pTimer->fDestroyed
252 && pTimer->u32Magic == RTTIMER_MAGIC)
253 {
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);
263 if (pTimer->fDestroyed)
264 continue;
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
276 * deliver the RT_TIMER_SIGNAL to the thread calling setitimer(). Therefore
277 * we have to call it here.
278 *
279 * It turns out this might not always be the case, see RT_TIMER_SIGNAL killing
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);
310 RTThreadUserSignal(hThreadSelf);
311 continue; /* back to suspended mode. */
312 }
313 pTimer->iError = 0;
314 RTThreadUserSignal(hThreadSelf);
315
316 /*
317 * Timer Service Loop.
318 */
319 sigemptyset(&SigSet);
320 sigaddset(&SigSet, RT_TIMER_SIGNAL);
321 do
322 {
323 siginfo_t SigInfo;
324 RT_ZERO(SigInfo);
325#ifdef RT_OS_DARWIN
326 if (RT_LIKELY(sigwait(&SigSet, &SigInfo.si_signo) >= 0))
327 {
328#else
329 if (RT_LIKELY(sigwaitinfo(&SigSet, &SigInfo) >= 0))
330 {
331 if (RT_LIKELY(SigInfo.si_signo == RT_TIMER_SIGNAL))
332#endif
333 {
334 if (RT_UNLIKELY( pTimer->fSuspended
335 || pTimer->fDestroyed
336 || pTimer->u32Magic != RTTIMER_MAGIC))
337 break;
338
339 pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->iTick);
340
341 /* auto suspend one-shot timers. */
342 if (RT_UNLIKELY(!pTimer->u64NanoInterval))
343 {
344 ASMAtomicWriteU8(&pTimer->fSuspended, true);
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)
366 {
367 pTimer->iError = 0;
368 RTThreadUserSignal(hThreadSelf);
369 }
370 }
371
372 /*
373 * Exit.
374 */
375 pTimer->iError = 0;
376 RTThreadUserSignal(hThreadSelf);
377
378#else /* IPRT_WITH_POSIX_TIMERS */
379
380 sigemptyset(&SigSet);
381 sigaddset(&SigSet, RT_TIMER_SIGNAL);
382 while (g_cTimerInstances)
383 {
384 siginfo_t SigInfo;
385 RT_ZERO(SigInfo);
386 if (RT_LIKELY(sigwaitinfo(&SigSet, &SigInfo) >= 0))
387 {
388 LogFlow(("rttimerThread: signo=%d pTimer=%p\n", SigInfo.si_signo, SigInfo.si_value.sival_ptr));
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. */
391 {
392 PRTTIMER pTimer = (PRTTIMER)SigInfo.si_value.sival_ptr;
393 AssertPtr(pTimer);
394 if (RT_UNLIKELY( !RT_VALID_PTR(pTimer)
395 || ASMAtomicUoReadU8(&pTimer->fSuspended)
396 || ASMAtomicUoReadU8(&pTimer->fDestroyed)
397 || pTimer->u32Magic != RTTIMER_MAGIC))
398 continue;
399
400 pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->iTick);
401
402 /* auto suspend one-shot timers. */
403 if (RT_UNLIKELY(!pTimer->u64NanoInterval))
404 ASMAtomicWriteU8(&pTimer->fSuspended, true);
405 }
406 }
407 }
408#endif /* IPRT_WITH_POSIX_TIMERS */
409
410 return VINF_SUCCESS;
411}
412
413
414RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
415{
416 /*
417 * We don't support the fancy MP features.
418 */
419 if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
420 return VERR_NOT_SUPPORTED;
421
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
429#ifndef IPRT_WITH_POSIX_TIMERS
430 /*
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 }
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)
443 {
444 AssertMsgFailed(("A timer is running. System limit is one timer per process!\n"));
445 return VERR_TIMER_BUSY;
446 }
447#endif /* !IPRT_WITH_POSIX_TIMERS */
448
449 /*
450 * Block RT_TIMER_SIGNAL from calling thread.
451 */
452 sigset_t SigSet;
453 sigemptyset(&SigSet);
454 sigaddset(&SigSet, RT_TIMER_SIGNAL);
455 sigprocmask(SIG_BLOCK, &SigSet, NULL);
456
457#ifndef IPRT_WITH_POSIX_TIMERS /** @todo combine more of the setitimer/timer_create code. setitimer could also use the global thread. */
458 /** @todo Move this RTC hack else where... */
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));
476# ifdef RT_OS_LINUX
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));
490# endif
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 /*
500 * Create a new timer.
501 */
502 int rc;
503 PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
504 if (pTimer)
505 {
506 pTimer->u32Magic = RTTIMER_MAGIC;
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;
514 pTimer->u64NanoFirst = 0;
515 pTimer->iTick = 0;
516 pTimer->iError = 0;
517 rc = RTSemEventCreate(&pTimer->Event);
518 AssertRC(rc);
519 if (RT_SUCCESS(rc))
520 {
521 rc = RTThreadCreate(&pTimer->Thread, rttimerThread, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
522 AssertRC(rc);
523 if (RT_SUCCESS(rc))
524 {
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);
531 if (RT_SUCCESS(rc))
532 {
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 }
542 }
543
544 /* bail out */
545 ASMAtomicXchgU8(&pTimer->fDestroyed, true);
546 ASMAtomicXchgU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
547 RTThreadWait(pTimer->Thread, 45*1000, NULL);
548 }
549 RTSemEventDestroy(pTimer->Event);
550 pTimer->Event = NIL_RTSEMEVENT;
551 }
552 RTMemFree(pTimer);
553 }
554 else
555 rc = VERR_NO_MEMORY;
556
557#else /* IPRT_WITH_POSIX_TIMERS */
558
559 /*
560 * Do the global init first.
561 */
562 int rc = RTOnce(&g_TimerOnce, rtTimerOnce, NULL);
563 if (RT_FAILURE(rc))
564 return rc;
565
566 /*
567 * Create a new timer structure.
568 */
569 LogFlow(("RTTimerCreateEx: u64NanoInterval=%llu fFlags=%lu\n", u64NanoInterval, fFlags));
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
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)
591 {
592 /*
593 * Increment the timer count, do this behind the critsect to avoid races.
594 */
595 RTCritSectEnter(&g_TimerCritSect);
596
597 if (ASMAtomicIncU32(&g_cTimerInstances) != 1)
598 {
599 Assert(g_cTimerInstances > 1);
600 RTCritSectLeave(&g_TimerCritSect);
601
602 LogFlow(("RTTimerCreateEx: rc=%Rrc pTimer=%p (thread already running)\n", rc, pTimer));
603 *ppTimer = pTimer;
604 return VINF_SUCCESS;
605 }
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);
633 }
634 else
635 {
636 rc = RTErrConvertFromErrno(err);
637 Log(("RTTimerCreateEx: err=%d (%Rrc)\n", err, rc));
638 }
639
640 RTMemFree(pTimer);
641 }
642 else
643 rc = VERR_NO_MEMORY;
644
645#endif /* IPRT_WITH_POSIX_TIMERS */
646 return rc;
647}
648
649
650RTR3DECL(int) RTTimerDestroy(PRTTIMER pTimer)
651{
652 LogFlow(("RTTimerDestroy: pTimer=%p\n", pTimer));
653
654 /*
655 * Validate input.
656 */
657 /* NULL is ok. */
658 if (!pTimer)
659 return VINF_SUCCESS;
660 int rc = VINF_SUCCESS;
661 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
662 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
663#ifdef IPRT_WITH_POSIX_TIMERS
664 AssertReturn(g_TimerThread != RTThreadSelf(), VERR_INTERNAL_ERROR);
665#else
666 AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
667#endif
668
669 /*
670 * Mark the semaphore as destroyed.
671 */
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 */
679 if (!pTimer->fSuspended)
680 {
681 struct itimerspec TimerSpec;
682 TimerSpec.it_value.tv_sec = 0;
683 TimerSpec.it_value.tv_nsec = 0;
684 TimerSpec.it_interval.tv_sec = 0;
685 TimerSpec.it_interval.tv_nsec = 0;
686 int err = timer_settime(pTimer->NativeTimer, 0, &TimerSpec, NULL); NOREF(err);
687 AssertMsg(!err, ("%d / %d\n", err, errno));
688 }
689#endif
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;
702 }
703 RTCritSectLeave(&g_TimerCritSect);
704#else /* IPRT_WITH_POSIX_TIMERS */
705 RTTHREAD Thread = pTimer->Thread;
706 rc = RTSemEventSignal(pTimer->Event);
707 AssertRC(rc);
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);
713
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
728 RTSemEventDestroy(pTimer->Event);
729 pTimer->Event = NIL_RTSEMEVENT;
730#endif /* !IPRT_WITH_POSIX_TIMERS */
731 if (RT_SUCCESS(rc))
732 RTMemFree(pTimer);
733 return rc;
734}
735
736
737RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
738{
739 /*
740 * Validate input.
741 */
742 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
743 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
744#ifndef IPRT_WITH_POSIX_TIMERS
745 AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
746#endif
747
748 /*
749 * Already running?
750 */
751 if (!ASMAtomicXchgU8(&pTimer->fSuspended, false))
752 return VERR_TIMER_ACTIVE;
753 LogFlow(("RTTimerStart: pTimer=%p u64First=%llu u64NanoInterval=%llu\n", pTimer, u64First, pTimer->u64NanoInterval));
754
755#ifndef IPRT_WITH_POSIX_TIMERS
756 /*
757 * Tell the thread to start servicing the timer.
758 * Wait for it to ACK the request to avoid reset races.
759 */
760 RTThreadUserReset(pTimer->Thread);
761 ASMAtomicUoWriteU64(&pTimer->u64NanoFirst, u64First);
762 ASMAtomicUoWriteU64(&pTimer->iTick, 0);
763 ASMAtomicWriteU8(&pTimer->fSuspended, false);
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);
773
774#else /* IPRT_WITH_POSIX_TIMERS */
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);
784 int rc = err == 0 ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
785#endif /* IPRT_WITH_POSIX_TIMERS */
786
787 if (RT_FAILURE(rc))
788 ASMAtomicXchgU8(&pTimer->fSuspended, false);
789 return rc;
790}
791
792
793RTDECL(int) RTTimerStop(PRTTIMER pTimer)
794{
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 */
804 if (ASMAtomicXchgU8(&pTimer->fSuspended, true))
805 return VERR_TIMER_SUSPENDED;
806 LogFlow(("RTTimerStop: pTimer=%p\n", pTimer));
807
808#ifndef IPRT_WITH_POSIX_TIMERS
809 /*
810 * Tell the thread to stop servicing the timer.
811 */
812 RTThreadUserReset(pTimer->Thread);
813 ASMAtomicXchgU8(&pTimer->fSuspended, true);
814 int rc = VINF_SUCCESS;
815 if (RTThreadSelf() != pTimer->Thread)
816 {
817 pthread_kill((pthread_t)RTThreadGetNative(pTimer->Thread), RT_TIMER_SIGNAL);
818 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
819 AssertRC(rc);
820 RTThreadUserReset(pTimer->Thread);
821 }
822
823#else /* IPRT_WITH_POSIX_TIMERS */
824 /*
825 * Stop the timer.
826 */
827 struct itimerspec TimerSpec;
828 TimerSpec.it_value.tv_sec = 0;
829 TimerSpec.it_value.tv_nsec = 0;
830 TimerSpec.it_interval.tv_sec = 0;
831 TimerSpec.it_interval.tv_nsec = 0;
832 int err = timer_settime(pTimer->NativeTimer, 0, &TimerSpec, NULL);
833 int rc = err == 0 ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
834#endif /* IPRT_WITH_POSIX_TIMERS */
835
836 return rc;
837}
838
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);
844 NOREF(u64NanoInterval);
845 return VERR_NOT_SUPPORTED;
846}
847
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use