VirtualBox

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

Last change on this file since 10920 was 10920, checked in by vboxsync, 17 years ago

POSIX timers with SIGEV_SIGNAL and a dedicated thread

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 23.4 KB
Line 
1/* $Id: timer-posix.cpp 10920 2008-07-28 18:44:26Z vboxsync $ */
2/** @file
3 * IPRT - Timer, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 *
26 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31/*******************************************************************************
32* Defined Constants And Macros *
33*******************************************************************************/
34/** Enables the use of POSIX RT timers. */
35#ifndef RT_OS_SOLARIS /* Solaris 10 doesn't have SIGEV_THREAD */
36#define IPRT_WITH_POSIX_TIMERS
37#endif /* !RT_OS_SOLARIS */
38
39#define LOG_GROUP RTLOGGROUP_TIMER
40
41/*******************************************************************************
42* Header Files *
43*******************************************************************************/
44#include <iprt/timer.h>
45#include <iprt/alloc.h>
46#include <iprt/assert.h>
47#include <iprt/thread.h>
48#include <iprt/log.h>
49#include <iprt/asm.h>
50#include <iprt/semaphore.h>
51#include <iprt/string.h>
52#include <iprt/err.h>
53#include "internal/magics.h"
54
55#include <unistd.h>
56#include <sys/fcntl.h>
57#include <sys/ioctl.h>
58#ifdef RT_OS_LINUX
59# include <linux/rtc.h>
60#endif
61#include <sys/time.h>
62#include <signal.h>
63#include <errno.h>
64#ifndef RT_OS_OS2
65# include <pthread.h>
66#endif
67
68#ifdef IPRT_WITH_POSIX_TIMERS
69#define RT_TIMER_SIGNAL SIGALRM
70
71/**
72 * Global counter of RTTimer instances. The signal thread is
73 * started when it changes from 0 to 1. The signal thread
74 * terminates when it becomes 0 again.
75 */
76uint32_t g_cTimerInstances;
77/** The signal handling thread. */
78RTTHREAD g_Thread;
79/**
80 * Event semaphore on which the calling thread is blocked until
81 * the signal thread has been initialized.
82 */
83RTSEMEVENT g_Event;
84
85#endif /* IPRT_WITH_POSIX_TIMERS */
86
87/*******************************************************************************
88* Structures and Typedefs *
89*******************************************************************************/
90/**
91 * The internal representation of a timer handle.
92 */
93typedef struct RTTIMER
94{
95 /** Magic.
96 * This is RTTIMER_MAGIC, but changes to something else before the timer
97 * is destroyed to indicate clearly that thread should exit. */
98 uint32_t volatile u32Magic;
99 /** Flag indicating the the timer is suspended. */
100 uint8_t volatile fSuspended;
101 /** Flag indicating that the timer has been destroyed. */
102 uint8_t volatile fDestroyed;
103#ifndef IPRT_WITH_POSIX_TIMERS /** @todo We have to take the signals on a dedicated timer thread as
104 * we (might) have code assuming that signals doesn't screw around
105 * on existing threads. (It would be sufficient to have one thread
106 * per signal of course since the signal will be masked while it's
107 * running, however, it may just cause more compilcations than its
108 * worth - sigwait/sigwaitinfo work atomically anyway...)
109 * Also, must block the signal in the thread main procedure too. */
110 /** The timer thread. */
111 RTTHREAD Thread;
112 /** Event semaphore on which the thread is blocked. */
113 RTSEMEVENT Event;
114#endif /* !IPRT_WITH_POSIX_TIMERS */
115 /** User argument. */
116 void *pvUser;
117 /** Callback. */
118 PFNRTTIMER pfnTimer;
119 /** The timer interval. 0 if one-shot. */
120 uint64_t u64NanoInterval;
121#ifndef IPRT_WITH_POSIX_TIMERS
122 /** The first shot interval. 0 if ASAP. */
123 uint64_t volatile u64NanoFirst;
124#endif /* !IPRT_WITH_POSIX_TIMERS */
125 /** The current timer tick. */
126 uint64_t volatile iTick;
127#ifndef IPRT_WITH_POSIX_TIMERS
128 /** The error/status of the timer.
129 * Initially -1, set to 0 when the timer have been successfully started, and
130 * to errno on failure in starting the timer. */
131 int volatile iError;
132#else /* IPRT_WITH_POSIX_TIMERS */
133 timer_t timer;
134#endif /* IPRT_WITH_POSIX_TIMERS */
135
136} RTTIMER;
137
138/**
139 * Signal handler which ignore everything it gets.
140 *
141 * @param iSignal The signal number.
142 */
143static void rttimerSignalIgnore(int iSignal)
144{
145 //AssertBreakpoint();
146}
147
148
149/**
150 * SIGALRM wait thread.
151 */
152static DECLCALLBACK(int) rttimerThread(RTTHREAD Thread, void *pvArg)
153{
154#ifndef IPRT_WITH_POSIX_TIMERS
155 PRTTIMER pTimer = (PRTTIMER)(void *)pvArg;
156 RTTIMER Timer = *pTimer;
157 Assert(pTimer->u32Magic == RTTIMER_MAGIC);
158#endif /* !IPRT_WITH_POSIX_TIMERS */
159
160 /*
161 * Install signal handler.
162 */
163 struct sigaction SigAct;
164 memset(&SigAct, 0, sizeof(SigAct));
165 SigAct.sa_flags = SA_RESTART;
166 sigemptyset(&SigAct.sa_mask);
167 SigAct.sa_handler = rttimerSignalIgnore;
168 if (sigaction(SIGALRM, &SigAct, NULL))
169 {
170 SigAct.sa_flags &= ~SA_RESTART;
171 if (sigaction(SIGALRM, &SigAct, NULL))
172 AssertMsgFailed(("sigaction failed, errno=%d\n", errno));
173 }
174
175 /*
176 * Mask most signals except those which might be used by the pthread implementation (linux).
177 */
178 sigset_t SigSet;
179 sigfillset(&SigSet);
180 sigdelset(&SigSet, SIGTERM);
181 sigdelset(&SigSet, SIGHUP);
182 sigdelset(&SigSet, SIGINT);
183 sigdelset(&SigSet, SIGABRT);
184 sigdelset(&SigSet, SIGKILL);
185#ifdef SIGRTMIN
186 for (int iSig = SIGRTMIN; iSig < SIGRTMAX; iSig++)
187 sigdelset(&SigSet, iSig);
188#endif
189 if (sigprocmask(SIG_SETMASK, &SigSet, NULL))
190 {
191#ifndef IPRT_WITH_POSIX_TIMERS
192 int rc = pTimer->iError = RTErrConvertFromErrno(errno);
193#else /* IPRT_WITH_POSIX_TIMERS */
194 int rc = RTErrConvertFromErrno(errno);
195#endif /* IPRT_WITH_POSIX_TIMERS */
196 AssertMsgFailed(("sigprocmask -> errno=%d\n", errno));
197 return rc;
198 }
199
200 /*
201 * The work loop.
202 */
203 RTThreadUserSignal(Thread);
204
205#ifndef IPRT_WITH_POSIX_TIMERS
206 while ( !pTimer->fDestroyed
207 && pTimer->u32Magic == RTTIMER_MAGIC)
208 {
209 /*
210 * Wait for a start or destroy event.
211 */
212 if (pTimer->fSuspended)
213 {
214 int rc = RTSemEventWait(pTimer->Event, RT_INDEFINITE_WAIT);
215 if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED)
216 {
217 AssertRC(rc);
218 RTThreadSleep(1000); /* Don't cause trouble! */
219 }
220 if ( pTimer->fSuspended
221 || pTimer->fDestroyed)
222 continue;
223 }
224
225 /*
226 * Start the timer.
227 *
228 * For some SunOS (/SysV?) threading compatibility Linux will only
229 * deliver the SIGALRM to the thread calling setitimer(). Therefore
230 * we have to call it here.
231 *
232 * It turns out this might not always be the case, see SIGALRM killing
233 * processes on RH 2.4.21.
234 */
235 struct itimerval TimerVal;
236 if (pTimer->u64NanoFirst)
237 {
238 uint64_t u64 = RT_MAX(1000, pTimer->u64NanoFirst);
239 TimerVal.it_value.tv_sec = u64 / 1000000000;
240 TimerVal.it_value.tv_usec = (u64 % 1000000000) / 1000;
241 }
242 else
243 {
244 TimerVal.it_value.tv_sec = 0;
245 TimerVal.it_value.tv_usec = 10;
246 }
247 if (pTimer->u64NanoInterval)
248 {
249 uint64_t u64 = RT_MAX(1000, pTimer->u64NanoInterval);
250 TimerVal.it_interval.tv_sec = u64 / 1000000000;
251 TimerVal.it_interval.tv_usec = (u64 % 1000000000) / 1000;
252 }
253 else
254 {
255 TimerVal.it_interval.tv_sec = 0;
256 TimerVal.it_interval.tv_usec = 0;
257 }
258
259 if (setitimer(ITIMER_REAL, &TimerVal, NULL))
260 {
261 ASMAtomicXchgU8(&pTimer->fSuspended, true);
262 pTimer->iError = RTErrConvertFromErrno(errno);
263 RTThreadUserSignal(Thread);
264 continue; /* back to suspended mode. */
265 }
266 pTimer->iError = 0;
267 RTThreadUserSignal(Thread);
268
269 /*
270 * Timer Service Loop.
271 */
272 sigemptyset(&SigSet);
273 sigaddset(&SigSet, SIGALRM);
274 do
275 {
276 siginfo_t SigInfo = {0};
277#ifdef RT_OS_DARWIN
278 if (RT_LIKELY(sigwait(&SigSet, &SigInfo.si_signo) >= 0))
279 {
280#else
281 if (RT_LIKELY(sigwaitinfo(&SigSet, &SigInfo) >= 0))
282 {
283 if (RT_LIKELY(SigInfo.si_signo == SIGALRM))
284#endif
285 {
286 if (RT_UNLIKELY( pTimer->fSuspended
287 || pTimer->fDestroyed
288 || pTimer->u32Magic != RTTIMER_MAGIC))
289 break;
290
291 pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->iTick);
292
293 /* auto suspend one-shot timers. */
294 if (RT_UNLIKELY(!pTimer->u64NanoInterval))
295 {
296 ASMAtomicXchgU8(&pTimer->fSuspended, true);
297 break;
298 }
299 }
300 }
301 else if (errno != EINTR)
302 AssertMsgFailed(("sigwaitinfo -> errno=%d\n", errno));
303 } while (RT_LIKELY( !pTimer->fSuspended
304 && !pTimer->fDestroyed
305 && pTimer->u32Magic == RTTIMER_MAGIC));
306
307 /*
308 * Disable the timer.
309 */
310 struct itimerval TimerVal2 = {{0,0}, {0,0}};
311 if (setitimer(ITIMER_REAL, &TimerVal2, NULL))
312 AssertMsgFailed(("setitimer(ITIMER_REAL,&{0}, NULL) failed, errno=%d\n", errno));
313
314 /*
315 * ACK any pending suspend request.
316 */
317 if (!pTimer->fDestroyed)
318 {
319 pTimer->iError = 0;
320 RTThreadUserSignal(Thread);
321 }
322 }
323
324 /*
325 * Exit.
326 */
327 pTimer->iError = 0;
328 RTThreadUserSignal(Thread);
329
330 return VINF_SUCCESS;
331}
332#else /* IPRT_WITH_POSIX_TIMERS */
333 sigemptyset(&SigSet);
334 sigaddset(&SigSet, RT_TIMER_SIGNAL);
335 while (g_cTimerInstances)
336 {
337 siginfo_t SigInfo = {0};
338 if (RT_LIKELY(sigwaitinfo(&SigSet, &SigInfo) >= 0))
339 {
340 LogFlow(("rttimerThread: signo=%d pTimer=%p\n", SigInfo.si_signo, SigInfo._sifields._timer.si_sigval.sival_ptr));
341 if (RT_LIKELY(SigInfo.si_signo == RT_TIMER_SIGNAL))
342 {
343 PRTTIMER pTimer = (PRTTIMER)SigInfo._sifields._timer.si_sigval.sival_ptr;
344 if (RT_UNLIKELY( pTimer == NULL
345 || pTimer->fSuspended
346 || pTimer->fDestroyed
347 || pTimer->u32Magic != RTTIMER_MAGIC))
348 continue;
349 pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->iTick);
350 /* auto suspend one-shot timers. */
351 if (RT_UNLIKELY(!pTimer->u64NanoInterval))
352 {
353 ASMAtomicXchgU8(&pTimer->fSuspended, true); /* @todo Can't we do a simple assigment here? */
354 //break;
355 }
356 }
357 }
358 }
359
360 return VINF_SUCCESS;
361}
362
363/**
364 * Create the SIGALRM handling thread and wait for it to get
365 * ready.
366 */
367static int rttimerCreateSignalThread()
368{
369 int rc = RTThreadCreate(&g_Thread, rttimerThread, NULL, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
370 AssertRC(rc);
371 if (RT_SUCCESS(rc))
372 {
373 /* Let's wait for the thread to get ready to handle signals. */
374 rc = RTThreadUserWait(g_Thread, 5000); /* @todo 5 sec is enough? or is it too much? */
375 }
376 LogFlow(("rttimerCreateSignalThread: rc=%Vrc\n", rc));
377
378 return rc;
379
380}
381#endif /* IPRT_WITH_POSIX_TIMERS */
382
383
384RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, unsigned fFlags, PFNRTTIMER pfnTimer, void *pvUser)
385{
386 /*
387 * We don't support the fancy MP features.
388 */
389 if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
390 return VERR_NOT_SUPPORTED;
391
392#ifndef IPRT_WITH_POSIX_TIMERS
393 /*
394 * Check if timer is busy.
395 */
396 struct itimerval TimerVal;
397 if (getitimer(ITIMER_REAL, &TimerVal))
398 {
399 AssertMsgFailed(("getitimer() -> errno=%d\n", errno));
400 return VERR_NOT_IMPLEMENTED;
401 }
402 if ( TimerVal.it_value.tv_usec || TimerVal.it_value.tv_sec
403 || TimerVal.it_interval.tv_usec || TimerVal.it_interval.tv_sec
404 )
405 {
406 AssertMsgFailed(("A timer is running. System limit is one timer per process!\n"));
407 return VERR_TIMER_BUSY;
408 }
409#endif /* !IPRT_WITH_POSIX_TIMERS */
410
411 /*
412 * Block SIGALRM from calling thread.
413 */
414 sigset_t SigSet;
415 sigemptyset(&SigSet);
416 sigaddset(&SigSet, SIGALRM);
417 sigprocmask(SIG_BLOCK, &SigSet, NULL);
418
419#ifndef IPRT_WITH_POSIX_TIMERS
420 /** @todo Move this RTC hack else where... */
421 static bool fDoneRTC;
422 if (!fDoneRTC)
423 {
424 fDoneRTC = true;
425 /* check resolution. */
426 TimerVal.it_interval.tv_sec = 0;
427 TimerVal.it_interval.tv_usec = 1000;
428 TimerVal.it_value = TimerVal.it_interval;
429 if ( setitimer(ITIMER_REAL, &TimerVal, NULL)
430 || getitimer(ITIMER_REAL, &TimerVal)
431 || TimerVal.it_interval.tv_usec > 1000)
432 {
433 /*
434 * Try open /dev/rtc to set the irq rate to 1024 and
435 * turn periodic
436 */
437 Log(("RTTimerCreate: interval={%ld,%ld} trying to adjust /dev/rtc!\n", TimerVal.it_interval.tv_sec, TimerVal.it_interval.tv_usec));
438#ifdef RT_OS_LINUX
439 int fh = open("/dev/rtc", O_RDONLY);
440 if (fh >= 0)
441 {
442 if ( ioctl(fh, RTC_IRQP_SET, 1024) < 0
443 || ioctl(fh, RTC_PIE_ON, 0) < 0)
444 Log(("RTTimerCreate: couldn't configure rtc! errno=%d\n", errno));
445 ioctl(fh, F_SETFL, O_ASYNC);
446 ioctl(fh, F_SETOWN, getpid());
447 /* not so sure if closing it is a good idea... */
448 //close(fh);
449 }
450 else
451 Log(("RTTimerCreate: couldn't configure rtc! open failed with errno=%d\n", errno));
452#endif
453 }
454 /* disable it */
455 TimerVal.it_interval.tv_sec = 0;
456 TimerVal.it_interval.tv_usec = 0;
457 TimerVal.it_value = TimerVal.it_interval;
458 setitimer(ITIMER_REAL, &TimerVal, NULL);
459 }
460
461 /*
462 * Create a new timer.
463 */
464 int rc;
465 PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
466 if (pTimer)
467 {
468 pTimer->u32Magic = RTTIMER_MAGIC;
469 pTimer->fSuspended = true;
470 pTimer->fDestroyed = false;
471 pTimer->Thread = NIL_RTTHREAD;
472 pTimer->Event = NIL_RTSEMEVENT;
473 pTimer->pfnTimer = pfnTimer;
474 pTimer->pvUser = pvUser;
475 pTimer->u64NanoInterval = u64NanoInterval;
476 pTimer->u64NanoFirst = 0;
477 pTimer->iTick = 0;
478 pTimer->iError = 0;
479 rc = RTSemEventCreate(&pTimer->Event);
480 AssertRC(rc);
481 if (RT_SUCCESS(rc))
482 {
483 rc = RTThreadCreate(&pTimer->Thread, rttimerThread, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
484 AssertRC(rc);
485 if (RT_SUCCESS(rc))
486 {
487 /*
488 * Wait for the timer thread to initialize it self.
489 * This might take a little while...
490 */
491 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
492 AssertRC(rc);
493 if (RT_SUCCESS(rc))
494 {
495 rc = RTThreadUserReset(pTimer->Thread); AssertRC(rc);
496 rc = pTimer->iError;
497 AssertRC(rc);
498 if (RT_SUCCESS(rc))
499 {
500 RTThreadYield(); /* <-- Horrible hack to make tstTimer work. (linux 2.6.12) */
501 *ppTimer = pTimer;
502 return VINF_SUCCESS;
503 }
504 }
505
506 /* bail out */
507 ASMAtomicXchgU8(&pTimer->fDestroyed, true);
508 ASMAtomicXchgU32(&pTimer->u32Magic, RTTIMER_MAGIC + 1);
509 RTThreadWait(pTimer->Thread, 45*1000, NULL);
510 }
511 RTSemEventDestroy(pTimer->Event);
512 pTimer->Event = NIL_RTSEMEVENT;
513 }
514 RTMemFree(pTimer);
515 }
516 else
517 rc = VERR_NO_MEMORY;
518#else /* IPRT_WITH_POSIX_TIMERS */
519 /*
520 * Create a new timer.
521 */
522 LogFlow(("RTTimerCreateEx: u64NanoInterval=%llu fFlags=%lu\n", u64NanoInterval, fFlags));
523
524 int rc = VINF_SUCCESS;
525 PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
526 if (pTimer)
527 {
528 struct sigevent evt;
529
530 /* Initialize timer structure. */
531 pTimer->u32Magic = RTTIMER_MAGIC;
532 pTimer->fSuspended = true;
533 pTimer->fDestroyed = false;
534 pTimer->pfnTimer = pfnTimer;
535 pTimer->pvUser = pvUser;
536 pTimer->u64NanoInterval = u64NanoInterval;
537 pTimer->iTick = 0;
538
539 /* Create the signal handling thread if it is the first instance. */
540 if (ASMAtomicIncU32(&g_cTimerInstances) == 1)
541 rc = rttimerCreateSignalThread();
542
543 if (RT_SUCCESS(rc))
544 {
545 /* Ask to deliver RT_TIMER_SIGNAL upon timer expiration. */
546 evt.sigev_notify = SIGEV_SIGNAL;
547 evt.sigev_signo = RT_TIMER_SIGNAL;
548 evt.sigev_value.sival_ptr = pTimer; /* sigev_value gets copied to siginfo. */
549
550 rc = RTErrConvertFromErrno(timer_create(CLOCK_REALTIME, &evt, &pTimer->timer));
551 LogFlow(("RTTimerCreateEx: rc=%Vrc pTimer=%p\n", rc, pTimer));
552 if (RT_SUCCESS(rc))
553 {
554 *ppTimer = pTimer;
555 return VINF_SUCCESS;
556 }
557 }
558 /**
559 * Roll back the timer instance counter. This will cause
560 * termination of the signal handling thread if it is the only
561 * timer.
562 */
563 ASMAtomicDecU32(&g_cTimerInstances);
564 RTMemFree(pTimer);
565 }
566 else
567 rc = VERR_NO_MEMORY;
568
569#endif /* IPRT_WITH_POSIX_TIMERS */
570 return rc;
571}
572
573
574RTR3DECL(int) RTTimerDestroy(PRTTIMER pTimer)
575{
576 LogFlow(("RTTimerDestroy: pTimer=%p\n", pTimer));
577
578 /*
579 * Validate input.
580 */
581 /* NULL is ok. */
582 if (!pTimer)
583 return VINF_SUCCESS;
584 int rc = VINF_SUCCESS;
585 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
586 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
587#ifndef IPRT_WITH_POSIX_TIMERS
588 AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
589
590 /*
591 * Tell the thread to terminate and wait for it do complete.
592 */
593 ASMAtomicXchgU8(&pTimer->fDestroyed, true);
594 ASMAtomicXchgU32(&pTimer->u32Magic, RTTIMER_MAGIC + 1);
595 rc = RTSemEventSignal(pTimer->Event);
596 AssertRC(rc);
597 if (!pTimer->fSuspended)
598 {
599#ifndef RT_OS_OS2
600 pthread_kill((pthread_t)RTThreadGetNative(pTimer->Thread), SIGALRM);
601#endif
602 }
603 rc = RTThreadWait(pTimer->Thread, 30 * 1000, NULL);
604 AssertRC(rc);
605
606 RTSemEventDestroy(pTimer->Event);
607 pTimer->Event = NIL_RTSEMEVENT;
608#else /* IPRT_WITH_POSIX_TIMERS */
609 if (ASMAtomicXchgU8(&pTimer->fDestroyed, true))
610 {
611 /* It is already being destroyed by another thread. */
612 return VINF_SUCCESS;
613 }
614 rc = RTErrConvertFromErrno(timer_delete(pTimer->timer));
615 /**
616 * Decrement the timer instance counter. This will cause
617 * termination of the signal handling thread if it is the last
618 * remaining timer.
619 */
620 if (ASMAtomicDecU32(&g_cTimerInstances) == 0)
621 {
622 /* Wake up the timer thread so it can exit. */
623 kill(getpid(), RT_TIMER_SIGNAL);
624 }
625#endif /* IPRT_WITH_POSIX_TIMERS */
626 if (RT_SUCCESS(rc))
627 RTMemFree(pTimer);
628 return rc;
629}
630
631
632RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
633{
634 /*
635 * Validate input.
636 */
637 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
638 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
639#ifndef IPRT_WITH_POSIX_TIMERS
640 AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
641
642 /*
643 * Already running?
644 */
645 if (!pTimer->fSuspended)
646 return VERR_TIMER_ACTIVE;
647
648 /*
649 * Tell the thread to start servicing the timer.
650 */
651 RTThreadUserReset(pTimer->Thread);
652 ASMAtomicUoWriteU64(&pTimer->u64NanoFirst, u64First);
653 ASMAtomicUoWriteU64(&pTimer->iTick, 0);
654 ASMAtomicWriteU8(&pTimer->fSuspended, false);
655 int rc = RTSemEventSignal(pTimer->Event);
656 if (RT_SUCCESS(rc))
657 {
658 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
659 AssertRC(rc);
660 RTThreadUserReset(pTimer->Thread);
661 }
662 else
663 AssertRC(rc);
664 if (RT_FAILURE(rc))
665 ASMAtomicXchgU8(&pTimer->fSuspended, false);
666#else /* IPRT_WITH_POSIX_TIMERS */
667 LogFlow(("RTTimerStart: pTimer=%p u64First=%llu u64NanoInterval=%llu\n", pTimer, u64First, pTimer->u64NanoInterval));
668
669 struct itimerspec ts;
670
671 if (!ASMAtomicXchgU8(&pTimer->fSuspended, false))
672 return VERR_TIMER_ACTIVE;
673
674 ts.it_value.tv_sec = u64First / 1000000000; /* nanosec => sec */
675 ts.it_value.tv_nsec = u64First ? u64First % 1000000000 : 10; /* 0 means disable, replace it with 10. */
676 ts.it_interval.tv_sec = pTimer->u64NanoInterval / 1000000000;
677 ts.it_interval.tv_nsec = pTimer->u64NanoInterval % 1000000000;
678 int rc = RTErrConvertFromErrno(timer_settime(pTimer->timer, 0, &ts, NULL));
679#endif /* IPRT_WITH_POSIX_TIMERS */
680
681 return rc;
682}
683
684
685RTDECL(int) RTTimerStop(PRTTIMER pTimer)
686{
687 /*
688 * Validate input.
689 */
690 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
691 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
692
693#ifndef IPRT_WITH_POSIX_TIMERS
694 /*
695 * Already running?
696 */
697 if (pTimer->fSuspended)
698 return VERR_TIMER_SUSPENDED;
699
700 /*
701 * Tell the thread to stop servicing the timer.
702 */
703 RTThreadUserReset(pTimer->Thread);
704 ASMAtomicXchgU8(&pTimer->fSuspended, true);
705 int rc = VINF_SUCCESS;
706 if (RTThreadSelf() != pTimer->Thread)
707 {
708#ifndef RT_OS_OS2
709 pthread_kill((pthread_t)RTThreadGetNative(pTimer->Thread), SIGALRM);
710#endif
711 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
712 AssertRC(rc);
713 RTThreadUserReset(pTimer->Thread);
714 }
715#else /* IPRT_WITH_POSIX_TIMERS */
716 LogFlow(("RTTimerStop: pTimer=%p\n", pTimer));
717
718 struct itimerspec ts;
719
720 if (ASMAtomicXchgU8(&pTimer->fSuspended, true))
721 return VERR_TIMER_SUSPENDED;
722
723 ts.it_value.tv_sec = 0;
724 ts.it_value.tv_nsec = 0;
725 int rc = RTErrConvertFromErrno(timer_settime(pTimer->timer, 0, &ts, NULL));
726#endif /* IPRT_WITH_POSIX_TIMERS */
727
728 return rc;
729}
730
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette