VirtualBox

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

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

rebranding: IPRT files again.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.7 KB
Line 
1/* $Id: timer-posix.cpp 8245 2008-04-21 17:24:28Z 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.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 * 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/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#include <iprt/timer.h>
36#include <iprt/alloc.h>
37#include <iprt/assert.h>
38#include <iprt/thread.h>
39#include <iprt/log.h>
40#include <iprt/asm.h>
41#include <iprt/semaphore.h>
42#include <iprt/string.h>
43#include <iprt/err.h>
44#include "internal/magics.h"
45
46#include <unistd.h>
47#include <sys/fcntl.h>
48#include <sys/ioctl.h>
49#ifdef RT_OS_LINUX
50# include <linux/rtc.h>
51#endif
52#include <sys/time.h>
53#include <signal.h>
54#include <errno.h>
55#ifndef RT_OS_OS2
56# include <pthread.h>
57#endif
58
59
60/*******************************************************************************
61* Structures and Typedefs *
62*******************************************************************************/
63/**
64 * The internal representation of a timer handle.
65 */
66typedef struct RTTIMER
67{
68 /** Magic.
69 * This is RTTIMER_MAGIC, but changes to something else before the timer
70 * is destroyed to indicate clearly that thread should exit. */
71 uint32_t volatile u32Magic;
72 /** Flag indicating the the timer is suspended. */
73 uint8_t volatile fSuspended;
74 /** Flag indicating that the timer has been destroyed. */
75 uint8_t volatile fDestroyed;
76 /** The timer thread. */
77 RTTHREAD Thread;
78 /** Event semaphore on which the thread is blocked. */
79 RTSEMEVENT Event;
80 /** User argument. */
81 void *pvUser;
82 /** Callback. */
83 PFNRTTIMER pfnTimer;
84 /** The timer interval. 0 if one-shot. */
85 uint64_t u64NanoInterval;
86 /** The first shot interval. 0 if ASAP. */
87 uint64_t volatile u64NanoFirst;
88 /** The error/status of the timer.
89 * Initially -1, set to 0 when the timer have been successfully started, and
90 * to errno on failure in starting the timer. */
91 int volatile iError;
92
93} RTTIMER;
94
95
96/**
97 * Signal handler which ignore everything it gets.
98 *
99 * @param iSignal The signal number.
100 */
101static void rttimerSignalIgnore(int iSignal)
102{
103 //AssertBreakpoint();
104}
105
106
107/**
108 * SIGALRM wait thread.
109 */
110static DECLCALLBACK(int) rttimerThread(RTTHREAD Thread, void *pvArg)
111{
112 PRTTIMER pTimer = (PRTTIMER)(void *)pvArg;
113 RTTIMER Timer = *pTimer;
114 Assert(pTimer->u32Magic == RTTIMER_MAGIC);
115
116 /*
117 * Install signal handler.
118 */
119 struct sigaction SigAct;
120 memset(&SigAct, 0, sizeof(SigAct));
121 SigAct.sa_flags = SA_RESTART;
122 sigemptyset(&SigAct.sa_mask);
123 SigAct.sa_handler = rttimerSignalIgnore;
124 if (sigaction(SIGALRM, &SigAct, NULL))
125 {
126 SigAct.sa_flags &= ~SA_RESTART;
127 if (sigaction(SIGALRM, &SigAct, NULL))
128 AssertMsgFailed(("sigaction failed, errno=%d\n", errno));
129 }
130
131 /*
132 * Mask most signals except those which might be used by the pthread implementation (linux).
133 */
134 sigset_t SigSet;
135 sigfillset(&SigSet);
136 sigdelset(&SigSet, SIGTERM);
137 sigdelset(&SigSet, SIGHUP);
138 sigdelset(&SigSet, SIGINT);
139 sigdelset(&SigSet, SIGABRT);
140 sigdelset(&SigSet, SIGKILL);
141#ifdef SIGRTMIN
142 for (int iSig = SIGRTMIN; iSig < SIGRTMAX; iSig++)
143 sigdelset(&SigSet, iSig);
144#endif
145 if (sigprocmask(SIG_SETMASK, &SigSet, NULL))
146 {
147 int rc = pTimer->iError = RTErrConvertFromErrno(errno);
148 AssertMsgFailed(("sigprocmask -> errno=%d\n", errno));
149 return rc;
150 }
151
152 /*
153 * The work loop.
154 */
155 RTThreadUserSignal(Thread);
156 while ( !pTimer->fDestroyed
157 && pTimer->u32Magic == RTTIMER_MAGIC)
158 {
159 /*
160 * Wait for a start or destroy event.
161 */
162 if (pTimer->fSuspended)
163 {
164 int rc = RTSemEventWait(pTimer->Event, RT_INDEFINITE_WAIT);
165 if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED)
166 {
167 AssertRC(rc);
168 RTThreadSleep(1000); /* Don't cause trouble! */
169 }
170 if ( pTimer->fSuspended
171 || pTimer->fDestroyed)
172 continue;
173 }
174
175 /*
176 * Start the timer.
177 *
178 * For some SunOS (/SysV?) threading compatibility Linux will only
179 * deliver the SIGALRM to the thread calling setitimer(). Therefore
180 * we have to call it here.
181 *
182 * It turns out this might not always be the case, see SIGALRM killing
183 * processes on RH 2.4.21.
184 */
185 struct itimerval TimerVal;
186 if (pTimer->u64NanoFirst)
187 {
188 uint64_t u64 = RT_MAX(1000, pTimer->u64NanoFirst);
189 TimerVal.it_value.tv_sec = u64 / 1000000000;
190 TimerVal.it_value.tv_usec = (u64 % 1000000000) / 1000;
191 }
192 else
193 {
194 TimerVal.it_value.tv_sec = 0;
195 TimerVal.it_value.tv_usec = 10;
196 }
197 if (pTimer->u64NanoInterval)
198 {
199 uint64_t u64 = RT_MAX(1000, pTimer->u64NanoInterval);
200 TimerVal.it_interval.tv_sec = u64 / 1000000000;
201 TimerVal.it_interval.tv_usec = (u64 % 1000000000) / 1000;
202 }
203 else
204 {
205 TimerVal.it_interval.tv_sec = 0;
206 TimerVal.it_interval.tv_usec = 0;
207 }
208
209 if (setitimer(ITIMER_REAL, &TimerVal, NULL))
210 {
211 ASMAtomicXchgU8(&pTimer->fSuspended, true);
212 pTimer->iError = RTErrConvertFromErrno(errno);
213 RTThreadUserSignal(Thread);
214 continue; /* back to suspended mode. */
215 }
216 pTimer->iError = 0;
217 RTThreadUserSignal(Thread);
218
219 /*
220 * Timer Service Loop.
221 */
222 sigemptyset(&SigSet);
223 sigaddset(&SigSet, SIGALRM);
224 do
225 {
226 siginfo_t SigInfo = {0};
227#ifdef RT_OS_DARWIN
228 if (RT_LIKELY(sigwait(&SigSet, &SigInfo.si_signo) >= 0))
229 {
230#else
231 if (RT_LIKELY(sigwaitinfo(&SigSet, &SigInfo) >= 0))
232 {
233 if (RT_LIKELY(SigInfo.si_signo == SIGALRM))
234#endif
235 {
236 if (RT_UNLIKELY( pTimer->fSuspended
237 || pTimer->fDestroyed
238 || pTimer->u32Magic != RTTIMER_MAGIC))
239 break;
240
241 pTimer->pfnTimer(pTimer, pTimer->pvUser);
242
243 /* auto suspend one-shot timers. */
244 if (RT_UNLIKELY(!pTimer->u64NanoInterval))
245 {
246 ASMAtomicXchgU8(&pTimer->fSuspended, true);
247 break;
248 }
249 }
250 }
251 else if (errno != EINTR)
252 AssertMsgFailed(("sigwaitinfo -> errno=%d\n", errno));
253 } while (RT_LIKELY( !pTimer->fSuspended
254 && !pTimer->fDestroyed
255 && pTimer->u32Magic == RTTIMER_MAGIC));
256
257 /*
258 * Disable the timer.
259 */
260 struct itimerval TimerVal2 = {{0,0}, {0,0}};
261 if (setitimer(ITIMER_REAL, &TimerVal2, NULL))
262 AssertMsgFailed(("setitimer(ITIMER_REAL,&{0}, NULL) failed, errno=%d\n", errno));
263
264 /*
265 * ACK any pending suspend request.
266 */
267 if (!pTimer->fDestroyed)
268 {
269 pTimer->iError = 0;
270 RTThreadUserSignal(Thread);
271 }
272 }
273
274 /*
275 * Exit.
276 */
277 pTimer->iError = 0;
278 RTThreadUserSignal(Thread);
279
280 return VINF_SUCCESS;
281}
282
283
284RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, unsigned fFlags, PFNRTTIMER pfnTimer, void *pvUser)
285{
286 /*
287 * Check if timer is busy.
288 */
289 struct itimerval TimerVal;
290 if (getitimer(ITIMER_REAL, &TimerVal))
291 {
292 AssertMsgFailed(("getitimer() -> errno=%d\n", errno));
293 return VERR_NOT_IMPLEMENTED;
294 }
295 if ( TimerVal.it_value.tv_usec || TimerVal.it_value.tv_sec
296 || TimerVal.it_interval.tv_usec || TimerVal.it_interval.tv_sec
297 )
298 {
299 AssertMsgFailed(("A timer is running. System limit is one timer per process!\n"));
300 return VERR_TIMER_BUSY;
301 }
302
303 /*
304 * Block SIGALRM from calling thread.
305 */
306 sigset_t SigSet;
307 sigemptyset(&SigSet);
308 sigaddset(&SigSet, SIGALRM);
309 sigprocmask(SIG_BLOCK, &SigSet, NULL);
310
311 /** @todo Move this RTC hack else where... */
312 static bool fDoneRTC;
313 if (!fDoneRTC)
314 {
315 fDoneRTC = true;
316 /* check resolution. */
317 TimerVal.it_interval.tv_sec = 0;
318 TimerVal.it_interval.tv_usec = 1000;
319 TimerVal.it_value = TimerVal.it_interval;
320 if ( setitimer(ITIMER_REAL, &TimerVal, NULL)
321 || getitimer(ITIMER_REAL, &TimerVal)
322 || TimerVal.it_interval.tv_usec > 1000)
323 {
324 /*
325 * Try open /dev/rtc to set the irq rate to 1024 and
326 * turn periodic
327 */
328 Log(("RTTimerCreate: interval={%ld,%ld} trying to adjust /dev/rtc!\n", TimerVal.it_interval.tv_sec, TimerVal.it_interval.tv_usec));
329#ifdef RT_OS_LINUX
330 int fh = open("/dev/rtc", O_RDONLY);
331 if (fh >= 0)
332 {
333 if ( ioctl(fh, RTC_IRQP_SET, 1024) < 0
334 || ioctl(fh, RTC_PIE_ON, 0) < 0)
335 Log(("RTTimerCreate: couldn't configure rtc! errno=%d\n", errno));
336 ioctl(fh, F_SETFL, O_ASYNC);
337 ioctl(fh, F_SETOWN, getpid());
338 /* not so sure if closing it is a good idea... */
339 //close(fh);
340 }
341 else
342 Log(("RTTimerCreate: couldn't configure rtc! open failed with errno=%d\n", errno));
343#endif
344 }
345 /* disable it */
346 TimerVal.it_interval.tv_sec = 0;
347 TimerVal.it_interval.tv_usec = 0;
348 TimerVal.it_value = TimerVal.it_interval;
349 setitimer(ITIMER_REAL, &TimerVal, NULL);
350 }
351
352 /*
353 * Create a new timer.
354 */
355 int rc;
356 PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
357 if (pTimer)
358 {
359 pTimer->u32Magic = RTTIMER_MAGIC;
360 pTimer->fSuspended = true;
361 pTimer->fDestroyed = false;
362 pTimer->Thread = NIL_RTTHREAD;
363 pTimer->Event = NIL_RTSEMEVENT;
364 pTimer->pfnTimer = pfnTimer;
365 pTimer->pvUser = pvUser;
366 pTimer->u64NanoInterval = u64NanoInterval;
367 pTimer->u64NanoFirst = 0;
368 pTimer->iError = 0;
369 rc = RTSemEventCreate(&pTimer->Event);
370 AssertRC(rc);
371 if (RT_SUCCESS(rc))
372 {
373 rc = RTThreadCreate(&pTimer->Thread, rttimerThread, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
374 AssertRC(rc);
375 if (RT_SUCCESS(rc))
376 {
377 /*
378 * Wait for the timer thread to initialize it self.
379 * This might take a little while...
380 */
381 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
382 AssertRC(rc);
383 if (RT_SUCCESS(rc))
384 {
385 rc = RTThreadUserReset(pTimer->Thread); AssertRC(rc);
386 rc = pTimer->iError;
387 AssertRC(rc);
388 if (RT_SUCCESS(rc))
389 {
390 RTThreadYield(); /* <-- Horrible hack to make tstTimer work. (linux 2.6.12) */
391 *ppTimer = pTimer;
392 return VINF_SUCCESS;
393 }
394 }
395
396 /* bail out */
397 ASMAtomicXchgU8(&pTimer->fDestroyed, true);
398 ASMAtomicXchgU32(&pTimer->u32Magic, RTTIMER_MAGIC + 1);
399 RTThreadWait(pTimer->Thread, 45*1000, NULL);
400 }
401 RTSemEventDestroy(pTimer->Event);
402 pTimer->Event = NIL_RTSEMEVENT;
403 }
404 RTMemFree(pTimer);
405 }
406 else
407 rc = VERR_NO_MEMORY;
408 return rc;
409}
410
411
412RTR3DECL(int) RTTimerDestroy(PRTTIMER pTimer)
413{
414 LogFlow(("RTTimerDestroy: pTimer=%p\n", pTimer));
415
416 /*
417 * Validate input.
418 */
419 /* NULL is ok. */
420 if (!pTimer)
421 return VINF_SUCCESS;
422 int rc = VINF_SUCCESS;
423 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
424 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
425 AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
426
427 /*
428 * Tell the thread to terminate and wait for it do complete.
429 */
430 ASMAtomicXchgU8(&pTimer->fDestroyed, true);
431 ASMAtomicXchgU32(&pTimer->u32Magic, RTTIMER_MAGIC + 1);
432 rc = RTSemEventSignal(pTimer->Event);
433 AssertRC(rc);
434 if (!pTimer->fSuspended)
435 {
436#ifndef RT_OS_OS2
437 pthread_kill((pthread_t)RTThreadGetNative(pTimer->Thread), SIGALRM);
438#endif
439 }
440 rc = RTThreadWait(pTimer->Thread, 30 * 1000, NULL);
441 AssertRC(rc);
442
443 RTSemEventDestroy(pTimer->Event);
444 pTimer->Event = NIL_RTSEMEVENT;
445 if (RT_SUCCESS(rc))
446 RTMemFree(pTimer);
447 return rc;
448}
449
450
451RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
452{
453 /*
454 * Validate input.
455 */
456 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
457 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
458 AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
459
460 /*
461 * Already running?
462 */
463 if (!pTimer->fSuspended)
464 return VERR_TIMER_ACTIVE;
465
466 /*
467 * Tell the thread to start servicing the timer.
468 */
469 RTThreadUserReset(pTimer->Thread);
470 ASMAtomicXchgU64(&pTimer->u64NanoFirst, u64First);
471 ASMAtomicXchgU8(&pTimer->fSuspended, false);
472 int rc = RTSemEventSignal(pTimer->Event);
473 if (RT_SUCCESS(rc))
474 {
475 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
476 AssertRC(rc);
477 RTThreadUserReset(pTimer->Thread);
478 }
479 else
480 AssertRC(rc);
481 if (RT_FAILURE(rc))
482 ASMAtomicXchgU8(&pTimer->fSuspended, false);
483
484 return rc;
485}
486
487
488RTDECL(int) RTTimerStop(PRTTIMER pTimer)
489{
490 /*
491 * Validate input.
492 */
493 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
494 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
495
496 /*
497 * Already running?
498 */
499 if (pTimer->fSuspended)
500 return VERR_TIMER_SUSPENDED;
501
502 /*
503 * Tell the thread to stop servicing the timer.
504 */
505 RTThreadUserReset(pTimer->Thread);
506 ASMAtomicXchgU8(&pTimer->fSuspended, true);
507 int rc = VINF_SUCCESS;
508 if (RTThreadSelf() != pTimer->Thread)
509 {
510#ifndef RT_OS_OS2
511 pthread_kill((pthread_t)RTThreadGetNative(pTimer->Thread), SIGALRM);
512#endif
513 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
514 AssertRC(rc);
515 RTThreadUserReset(pTimer->Thread);
516 }
517
518 return rc;
519}
520
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