VirtualBox

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

Last change on this file since 1507 was 1470, checked in by vboxsync, 18 years ago

Runtime: Stubbed RTTimerStart/Stop.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 11.7 KB
Line 
1/* $Id: timer-posix.cpp 1470 2007-03-14 14:58:04Z vboxsync $ */
2/** @file
3 * InnoTek Portable Runtime - Timer, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#include <iprt/timer.h>
27#include <iprt/alloc.h>
28#include <iprt/assert.h>
29#include <iprt/thread.h>
30#include <iprt/log.h>
31#include <iprt/asm.h>
32#include <iprt/semaphore.h>
33#include <iprt/string.h>
34#include <iprt/err.h>
35
36#include <unistd.h>
37#include <sys/fcntl.h>
38#include <sys/ioctl.h>
39#ifdef __LINUX__
40# include <linux/rtc.h>
41#endif
42#include <sys/time.h>
43#include <signal.h>
44#include <errno.h>
45#ifndef __OS2__
46# include <pthread.h>
47#endif
48
49
50/*******************************************************************************
51* Structures and Typedefs *
52*******************************************************************************/
53/**
54 * The internal representation of a timer handle.
55 */
56typedef struct RTTIMER
57{
58 /** Magic.
59 * This is RTTIMER_MAGIC, but changes to something else before the timer
60 * is destroyed to indicate clearly that thread should exit. */
61 volatile uint32_t u32Magic;
62 /** Win32 timer id. */
63 RTTHREAD Thread;
64 /** User argument. */
65 void *pvUser;
66 /** Callback. */
67 PFNRTTIMER pfnTimer;
68 /** The timeout values for the timer. */
69 struct itimerval TimerVal;
70 /** The error/status of the timer.
71 * Initially -1, set to 0 when the timer have been successfully started, and
72 * to errno on failure in starting the timer. */
73 volatile int iError;
74
75} RTTIMER;
76/** Timer handle magic. */
77#define RTTIMER_MAGIC 0x42424242
78
79
80/**
81 * Signal handler which ignore everything it gets.
82 *
83 * @param iSignal The signal number.
84 */
85static void rttimerSignalIgnore(int iSignal)
86{
87 //AssertBreakpoint();
88}
89
90
91/**
92 * SIGALRM wait thread.
93 */
94static DECLCALLBACK(int) rttimerThread(RTTHREAD Thread, void *pvArg)
95{
96 PRTTIMER pTimer = (PRTTIMER)(void *)pvArg;
97 RTTIMER Timer = *pTimer;
98 Assert(pTimer->u32Magic == RTTIMER_MAGIC);
99
100 /*
101 * Install signal handler.
102 */
103 struct sigaction SigAct;
104 memset(&SigAct, 0, sizeof(SigAct));
105 SigAct.sa_flags = SA_RESTART;
106 sigemptyset(&SigAct.sa_mask);
107 SigAct.sa_handler = rttimerSignalIgnore;
108 if (sigaction(SIGALRM, &SigAct, NULL))
109 {
110 SigAct.sa_flags &= ~SA_RESTART;
111 if (sigaction(SIGALRM, &SigAct, NULL))
112 AssertMsgFailed(("sigaction failed, errno=%d\n", errno));
113 }
114
115 /*
116 * Mask most signals except those which might be used during
117 * termination is by a pthread implementation.
118 */
119 sigset_t SigSet;
120 sigfillset(&SigSet);
121 sigdelset(&SigSet, SIGTERM);
122 sigdelset(&SigSet, SIGHUP);
123 sigdelset(&SigSet, SIGINT);
124 sigdelset(&SigSet, SIGABRT);
125 sigdelset(&SigSet, SIGKILL);
126#ifdef SIGRTMIN
127 for (int iSig = SIGRTMIN; iSig < SIGRTMAX; iSig++)
128 sigdelset(&SigSet, iSig);
129#endif
130 if (sigprocmask(SIG_SETMASK, &SigSet, NULL))
131 {
132 int rc = pTimer->iError = RTErrConvertFromErrno(errno);
133 AssertMsgFailed(("sigprocmask -> errno=%d\n", errno));
134 return rc;
135 }
136
137 /*
138 * Start the timer.
139 *
140 * For some SunOS (/SysV?) threading compatibility Linux will only
141 * deliver the SIGALRM to the thread calling setitimer(). Therefore
142 * we have to call it here.
143 *
144 * It turns out this might not always be the case, see SIGALRM killing
145 * processes on RH 2.4.21.
146 */
147 if (setitimer(ITIMER_REAL, &pTimer->TimerVal, NULL))
148 {
149 pTimer->iError = RTErrConvertFromErrno(errno);
150 RTThreadUserSignal(Thread);
151 return errno;
152 }
153
154 /*
155 * Signal wait loop-forever.
156 */
157 sigemptyset(&SigSet);
158 sigaddset(&SigSet, SIGALRM);
159 RTThreadUserSignal(Thread);
160 while (pTimer->u32Magic == RTTIMER_MAGIC)
161 {
162 siginfo_t SigInfo = {0};
163#ifdef __DARWIN__
164 if (sigwait(&SigSet, &SigInfo.si_signo) >= 0)
165 {
166#else
167 if (sigwaitinfo(&SigSet, &SigInfo) >= 0)
168 {
169 if ( SigInfo.si_signo == SIGALRM
170 && pTimer->u32Magic == RTTIMER_MAGIC)
171#endif
172 pTimer->pfnTimer(pTimer, pTimer->pvUser);
173 }
174 else if (errno != EINTR)
175 AssertMsgFailed(("sigwaitinfo -> errno=%d\n", errno));
176 }
177
178 /*
179 * Disable the timer.
180 */
181 struct itimerval TimerVal = {{0,0}, {0,0}};
182 if (setitimer(ITIMER_REAL, &TimerVal, NULL))
183 AssertMsgFailed(("setitimer(ITIMER_REAL,&{0}, NULL) failed, errno=%d\n", errno));
184
185 /*
186 * Exit.
187 */
188 RTThreadUserSignal(Thread);
189 return VINF_SUCCESS;
190}
191
192
193/**
194 * Create a recurring timer.
195 *
196 * @returns iprt status code.
197 * @param ppTimer Where to store the timer handle.
198 * @param uMilliesInterval Milliseconds between the timer ticks.
199 * This is rounded up to the system granularity.
200 * @param pfnCallback Callback function which shall be scheduled for execution
201 * on every timer tick.
202 * @param pvUser User argument for the callback.
203 */
204RTR3DECL(int) RTTimerCreate(PRTTIMER *ppTimer, unsigned uMilliesInterval, PFNRTTIMER pfnTimer, void *pvUser)
205{
206 /*
207 * Check if timer is busy.
208 */
209 struct itimerval TimerVal;
210 if (getitimer(ITIMER_REAL, &TimerVal))
211 {
212 AssertMsgFailed(("getitimer() -> errno=%d\n", errno));
213 return VERR_NOT_IMPLEMENTED;
214 }
215 if ( TimerVal.it_value.tv_usec || TimerVal.it_value.tv_sec
216 || TimerVal.it_interval.tv_usec || TimerVal.it_interval.tv_sec
217 )
218 {
219 AssertMsgFailed(("A timer is running. System limit is one timer per process!\n"));
220 return VERR_TIMER_BUSY;
221 }
222
223 /*
224 * Block SIGALRM from calling thread.
225 */
226#if defined(__FREEBSD__) /* sighold is missing and I don't wish to break anything atm. */
227 sigset_t SigSet;
228 sigemptyset(&SigSet);
229 sigaddset(&SigSet, SIGALRM);
230 sigprocmask(SIG_BLOCK, &SigSet, NULL);
231#else
232 sighold(SIGALRM);
233#endif
234 static bool fDoneRTC;
235 if (!fDoneRTC)
236 {
237 fDoneRTC = true;
238 /* check resolution. */
239 TimerVal.it_interval.tv_sec = 0;
240 TimerVal.it_interval.tv_usec = 1000;
241 TimerVal.it_value = TimerVal.it_interval;
242 if ( setitimer(ITIMER_REAL, &TimerVal, NULL)
243 || getitimer(ITIMER_REAL, &TimerVal)
244 || TimerVal.it_interval.tv_usec > 1000)
245 {
246 /*
247 * Try open /dev/rtc to set the irq rate to 1024 and
248 * turn periodic
249 */
250 Log(("RTTimerCreate: interval={%ld,%ld} trying to adjust /dev/rtc!\n", TimerVal.it_interval.tv_sec, TimerVal.it_interval.tv_usec));
251#ifdef __LINUX__
252 int fh = open("/dev/rtc", O_RDONLY);
253 if (fh >= 0)
254 {
255 if ( ioctl(fh, RTC_IRQP_SET, 1024) < 0
256 || ioctl(fh, RTC_PIE_ON, 0) < 0)
257 Log(("RTTimerCreate: couldn't configure rtc! errno=%d\n", errno));
258 ioctl(fh, F_SETFL, O_ASYNC);
259 ioctl(fh, F_SETOWN, getpid());
260 /* not so sure if closing it is a good idea... */
261 //close(fh);
262 }
263 else
264 Log(("RTTimerCreate: couldn't configure rtc! open failed with errno=%d\n", errno));
265#endif
266 }
267 /* disable it */
268 TimerVal.it_interval.tv_sec = 0;
269 TimerVal.it_interval.tv_usec = 0;
270 TimerVal.it_value = TimerVal.it_interval;
271 setitimer(ITIMER_REAL, &TimerVal, NULL);
272 }
273
274 /*
275 * Create new timer.
276 */
277 int rc;
278 PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
279 if (pTimer)
280 {
281 pTimer->u32Magic = RTTIMER_MAGIC;
282 pTimer->iError = 0;
283 pTimer->pvUser = pvUser;
284 pTimer->pfnTimer = pfnTimer;
285 pTimer->TimerVal.it_interval.tv_sec = uMilliesInterval / 1000;
286 pTimer->TimerVal.it_interval.tv_usec = (uMilliesInterval % 1000) * 1000;
287 pTimer->TimerVal.it_value = pTimer->TimerVal.it_interval;
288 rc = RTThreadCreate(&pTimer->Thread, rttimerThread, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
289 if (RT_SUCCESS(rc))
290 {
291 /*
292 * Wait for the timer to successfully create the timer
293 */
294 /** @todo something is may cause this to take very long. We're waiting 30 seconds now and hope that'll workaround it... */
295 rc = RTThreadUserWait(pTimer->Thread, 30*1000);
296 if (RT_SUCCESS(rc))
297 {
298 rc = pTimer->iError;
299 if (RT_SUCCESS(rc))
300 {
301 RTThreadYield(); /* Horrible hack to make tstTimer work. Something is really fucked related to scheduling here! (2.6.12) */
302 *ppTimer = pTimer;
303 return VINF_SUCCESS;
304 }
305 }
306 ASMAtomicXchgU32(&pTimer->u32Magic, RTTIMER_MAGIC + 1);
307 }
308
309 AssertMsgFailed(("Failed to create timer uMilliesInterval=%d. rc=%Vrc\n", uMilliesInterval, rc));
310 RTMemFree(pTimer);
311 }
312 else
313 rc = VERR_NO_MEMORY;
314 return rc;
315}
316
317
318RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, unsigned fFlags, PFNRTTIMER pfnTimer, void *pvUser)
319{
320 /// @todo implement
321 return VERR_NOT_IMPLEMENTED;
322}
323
324
325/**
326 * Stops and destroys a running timer.
327 *
328 * @returns iprt status code.
329 * @param pTimer Timer to stop and destroy.
330 */
331RTR3DECL(int) RTTimerDestroy(PRTTIMER pTimer)
332{
333 LogFlow(("RTTimerDestroy: pTimer=%p\n", pTimer));
334
335 /* NULL is ok. */
336 if (!pTimer)
337 return VINF_SUCCESS;
338
339 /*
340 * Validate input.
341 */
342 int rc = VINF_SUCCESS;
343 if (VALID_PTR(pTimer))
344 {
345 /*
346 * Modify the magic and kick it.
347 */
348 if (ASMAtomicXchgU32(&pTimer->u32Magic, RTTIMER_MAGIC + 1) == RTTIMER_MAGIC)
349 {
350#ifndef __OS2__
351 pthread_kill((pthread_t)RTThreadGetNative(pTimer->Thread), SIGALRM);
352#endif
353
354 /*
355 * Wait for the thread to exit.
356 */
357 rc = RTThreadWait(pTimer->Thread, 30 * 1000, NULL);
358 if ( RT_SUCCESS(rc)
359 || rc == VERR_INVALID_HANDLE /* we don't keep handles around, you gotta wait before it really exits! */)
360 {
361 RTMemFree(pTimer);
362 return VINF_SUCCESS;
363 }
364 AssertMsgFailed(("Failed to destroy timer %p. rc=%Vrc\n", pTimer, rc));
365 }
366 else
367 {
368 AssertMsgFailed(("Timer %p is already being destroyed!\n", pTimer));
369 rc = VERR_INVALID_MAGIC;
370 }
371 }
372 else
373 {
374 AssertMsgFailed(("Bad pTimer pointer %p!\n", pTimer));
375 rc = VERR_INVALID_HANDLE;
376 }
377 return rc;
378}
379
380
381RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
382{
383 /// @todo implement
384 return VERR_NOT_IMPLEMENTED;
385}
386
387
388RTDECL(int) RTTimerStop(PRTTIMER pTimer)
389{
390 /// @todo implement
391 return VERR_NOT_IMPLEMENTED;
392}
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