VirtualBox

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

Last change on this file since 28800 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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

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