VirtualBox

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

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

Return VERR_NOT_SUPPORTED if RTTIMER_FLAGS_CPU_* is set.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.9 KB
Line 
1/* $Id: timer-posix.cpp 9416 2008-06-05 12:47:29Z 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 * We don't support the fancy MP features.
288 */
289 if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
290 return VERR_NOT_SUPPORTED;
291
292 /*
293 * Check if timer is busy.
294 */
295 struct itimerval TimerVal;
296 if (getitimer(ITIMER_REAL, &TimerVal))
297 {
298 AssertMsgFailed(("getitimer() -> errno=%d\n", errno));
299 return VERR_NOT_IMPLEMENTED;
300 }
301 if ( TimerVal.it_value.tv_usec || TimerVal.it_value.tv_sec
302 || TimerVal.it_interval.tv_usec || TimerVal.it_interval.tv_sec
303 )
304 {
305 AssertMsgFailed(("A timer is running. System limit is one timer per process!\n"));
306 return VERR_TIMER_BUSY;
307 }
308
309 /*
310 * Block SIGALRM from calling thread.
311 */
312 sigset_t SigSet;
313 sigemptyset(&SigSet);
314 sigaddset(&SigSet, SIGALRM);
315 sigprocmask(SIG_BLOCK, &SigSet, NULL);
316
317 /** @todo Move this RTC hack else where... */
318 static bool fDoneRTC;
319 if (!fDoneRTC)
320 {
321 fDoneRTC = true;
322 /* check resolution. */
323 TimerVal.it_interval.tv_sec = 0;
324 TimerVal.it_interval.tv_usec = 1000;
325 TimerVal.it_value = TimerVal.it_interval;
326 if ( setitimer(ITIMER_REAL, &TimerVal, NULL)
327 || getitimer(ITIMER_REAL, &TimerVal)
328 || TimerVal.it_interval.tv_usec > 1000)
329 {
330 /*
331 * Try open /dev/rtc to set the irq rate to 1024 and
332 * turn periodic
333 */
334 Log(("RTTimerCreate: interval={%ld,%ld} trying to adjust /dev/rtc!\n", TimerVal.it_interval.tv_sec, TimerVal.it_interval.tv_usec));
335#ifdef RT_OS_LINUX
336 int fh = open("/dev/rtc", O_RDONLY);
337 if (fh >= 0)
338 {
339 if ( ioctl(fh, RTC_IRQP_SET, 1024) < 0
340 || ioctl(fh, RTC_PIE_ON, 0) < 0)
341 Log(("RTTimerCreate: couldn't configure rtc! errno=%d\n", errno));
342 ioctl(fh, F_SETFL, O_ASYNC);
343 ioctl(fh, F_SETOWN, getpid());
344 /* not so sure if closing it is a good idea... */
345 //close(fh);
346 }
347 else
348 Log(("RTTimerCreate: couldn't configure rtc! open failed with errno=%d\n", errno));
349#endif
350 }
351 /* disable it */
352 TimerVal.it_interval.tv_sec = 0;
353 TimerVal.it_interval.tv_usec = 0;
354 TimerVal.it_value = TimerVal.it_interval;
355 setitimer(ITIMER_REAL, &TimerVal, NULL);
356 }
357
358 /*
359 * Create a new timer.
360 */
361 int rc;
362 PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
363 if (pTimer)
364 {
365 pTimer->u32Magic = RTTIMER_MAGIC;
366 pTimer->fSuspended = true;
367 pTimer->fDestroyed = false;
368 pTimer->Thread = NIL_RTTHREAD;
369 pTimer->Event = NIL_RTSEMEVENT;
370 pTimer->pfnTimer = pfnTimer;
371 pTimer->pvUser = pvUser;
372 pTimer->u64NanoInterval = u64NanoInterval;
373 pTimer->u64NanoFirst = 0;
374 pTimer->iError = 0;
375 rc = RTSemEventCreate(&pTimer->Event);
376 AssertRC(rc);
377 if (RT_SUCCESS(rc))
378 {
379 rc = RTThreadCreate(&pTimer->Thread, rttimerThread, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
380 AssertRC(rc);
381 if (RT_SUCCESS(rc))
382 {
383 /*
384 * Wait for the timer thread to initialize it self.
385 * This might take a little while...
386 */
387 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
388 AssertRC(rc);
389 if (RT_SUCCESS(rc))
390 {
391 rc = RTThreadUserReset(pTimer->Thread); AssertRC(rc);
392 rc = pTimer->iError;
393 AssertRC(rc);
394 if (RT_SUCCESS(rc))
395 {
396 RTThreadYield(); /* <-- Horrible hack to make tstTimer work. (linux 2.6.12) */
397 *ppTimer = pTimer;
398 return VINF_SUCCESS;
399 }
400 }
401
402 /* bail out */
403 ASMAtomicXchgU8(&pTimer->fDestroyed, true);
404 ASMAtomicXchgU32(&pTimer->u32Magic, RTTIMER_MAGIC + 1);
405 RTThreadWait(pTimer->Thread, 45*1000, NULL);
406 }
407 RTSemEventDestroy(pTimer->Event);
408 pTimer->Event = NIL_RTSEMEVENT;
409 }
410 RTMemFree(pTimer);
411 }
412 else
413 rc = VERR_NO_MEMORY;
414 return rc;
415}
416
417
418RTR3DECL(int) RTTimerDestroy(PRTTIMER pTimer)
419{
420 LogFlow(("RTTimerDestroy: pTimer=%p\n", pTimer));
421
422 /*
423 * Validate input.
424 */
425 /* NULL is ok. */
426 if (!pTimer)
427 return VINF_SUCCESS;
428 int rc = VINF_SUCCESS;
429 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
430 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
431 AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
432
433 /*
434 * Tell the thread to terminate and wait for it do complete.
435 */
436 ASMAtomicXchgU8(&pTimer->fDestroyed, true);
437 ASMAtomicXchgU32(&pTimer->u32Magic, RTTIMER_MAGIC + 1);
438 rc = RTSemEventSignal(pTimer->Event);
439 AssertRC(rc);
440 if (!pTimer->fSuspended)
441 {
442#ifndef RT_OS_OS2
443 pthread_kill((pthread_t)RTThreadGetNative(pTimer->Thread), SIGALRM);
444#endif
445 }
446 rc = RTThreadWait(pTimer->Thread, 30 * 1000, NULL);
447 AssertRC(rc);
448
449 RTSemEventDestroy(pTimer->Event);
450 pTimer->Event = NIL_RTSEMEVENT;
451 if (RT_SUCCESS(rc))
452 RTMemFree(pTimer);
453 return rc;
454}
455
456
457RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
458{
459 /*
460 * Validate input.
461 */
462 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
463 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
464 AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
465
466 /*
467 * Already running?
468 */
469 if (!pTimer->fSuspended)
470 return VERR_TIMER_ACTIVE;
471
472 /*
473 * Tell the thread to start servicing the timer.
474 */
475 RTThreadUserReset(pTimer->Thread);
476 ASMAtomicXchgU64(&pTimer->u64NanoFirst, u64First);
477 ASMAtomicXchgU8(&pTimer->fSuspended, false);
478 int rc = RTSemEventSignal(pTimer->Event);
479 if (RT_SUCCESS(rc))
480 {
481 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
482 AssertRC(rc);
483 RTThreadUserReset(pTimer->Thread);
484 }
485 else
486 AssertRC(rc);
487 if (RT_FAILURE(rc))
488 ASMAtomicXchgU8(&pTimer->fSuspended, false);
489
490 return rc;
491}
492
493
494RTDECL(int) RTTimerStop(PRTTIMER pTimer)
495{
496 /*
497 * Validate input.
498 */
499 AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
500 AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
501
502 /*
503 * Already running?
504 */
505 if (pTimer->fSuspended)
506 return VERR_TIMER_SUSPENDED;
507
508 /*
509 * Tell the thread to stop servicing the timer.
510 */
511 RTThreadUserReset(pTimer->Thread);
512 ASMAtomicXchgU8(&pTimer->fSuspended, true);
513 int rc = VINF_SUCCESS;
514 if (RTThreadSelf() != pTimer->Thread)
515 {
516#ifndef RT_OS_OS2
517 pthread_kill((pthread_t)RTThreadGetNative(pTimer->Thread), SIGALRM);
518#endif
519 rc = RTThreadUserWait(pTimer->Thread, 45*1000);
520 AssertRC(rc);
521 RTThreadUserReset(pTimer->Thread);
522 }
523
524 return rc;
525}
526
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