VirtualBox

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

Last change on this file since 1 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 11.0 KB
Line 
1/* $Id: timer-posix.cpp 1 1970-01-01 00:00:00Z 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 sighold(SIGALRM);
227 static bool fDoneRTC;
228 if (!fDoneRTC)
229 {
230 fDoneRTC = true;
231 /* check resolution. */
232 TimerVal.it_interval.tv_sec = 0;
233 TimerVal.it_interval.tv_usec = 1000;
234 TimerVal.it_value = TimerVal.it_interval;
235 if ( setitimer(ITIMER_REAL, &TimerVal, NULL)
236 || getitimer(ITIMER_REAL, &TimerVal)
237 || TimerVal.it_interval.tv_usec > 1000)
238 {
239 /*
240 * Try open /dev/rtc to set the irq rate to 1024 and
241 * turn periodic
242 */
243 Log(("RTTimerCreate: interval={%ld,%ld} trying to adjust /dev/rtc!\n", TimerVal.it_interval.tv_sec, TimerVal.it_interval.tv_usec));
244#ifdef __LINUX__
245 int fh = open("/dev/rtc", O_RDONLY);
246 if (fh >= 0)
247 {
248 if ( ioctl(fh, RTC_IRQP_SET, 1024) < 0
249 || ioctl(fh, RTC_PIE_ON, 0) < 0)
250 Log(("RTTimerCreate: couldn't configure rtc! errno=%d\n", errno));
251 ioctl(fh, F_SETFL, O_ASYNC);
252 ioctl(fh, F_SETOWN, getpid());
253 /* not so sure if closing it is a good idea... */
254 //close(fh);
255 }
256 else
257 Log(("RTTimerCreate: couldn't configure rtc! open failed with errno=%d\n", errno));
258#endif
259 }
260 /* disable it */
261 TimerVal.it_interval.tv_sec = 0;
262 TimerVal.it_interval.tv_usec = 0;
263 TimerVal.it_value = TimerVal.it_interval;
264 setitimer(ITIMER_REAL, &TimerVal, NULL);
265 }
266
267 /*
268 * Create new timer.
269 */
270 int rc;
271 PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
272 if (pTimer)
273 {
274 pTimer->u32Magic = RTTIMER_MAGIC;
275 pTimer->iError = 0;
276 pTimer->pvUser = pvUser;
277 pTimer->pfnTimer = pfnTimer;
278 pTimer->TimerVal.it_interval.tv_sec = uMilliesInterval / 1000;
279 pTimer->TimerVal.it_interval.tv_usec = (uMilliesInterval % 1000) * 1000;
280 pTimer->TimerVal.it_value = pTimer->TimerVal.it_interval;
281 rc = RTThreadCreate(&pTimer->Thread, rttimerThread, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
282 if (RT_SUCCESS(rc))
283 {
284 /*
285 * Wait for the timer to successfully create the timer
286 */
287 /** @todo something is may cause this to take very long. We're waiting 30 seconds now and hope that'll workaround it... */
288 rc = RTThreadUserWait(pTimer->Thread, 30*1000);
289 if (RT_SUCCESS(rc))
290 {
291 rc = pTimer->iError;
292 if (RT_SUCCESS(rc))
293 {
294 RTThreadYield(); /* Horrible hack to make tstTimer work. Something is really fucked related to scheduling here! (2.6.12) */
295 *ppTimer = pTimer;
296 return VINF_SUCCESS;
297 }
298 }
299 ASMAtomicXchgU32(&pTimer->u32Magic, RTTIMER_MAGIC + 1);
300 }
301
302 AssertMsgFailed(("Failed to create timer uMilliesInterval=%d. rc=%Vrc\n", uMilliesInterval, rc));
303 RTMemFree(pTimer);
304 }
305 else
306 rc = VERR_NO_MEMORY;
307 return rc;
308}
309
310
311/**
312 * Stops and destroys a running timer.
313 *
314 * @returns iprt status code.
315 * @param pTimer Timer to stop and destroy.
316 */
317RTR3DECL(int) RTTimerDestroy(PRTTIMER pTimer)
318{
319 LogFlow(("RTTimerDestroy: pTimer=%p\n", pTimer));
320
321 /*
322 * Validate input.
323 */
324 int rc = VINF_SUCCESS;
325 if (pTimer)
326 {
327 /*
328 * Modify the magic and kick it.
329 */
330 if (ASMAtomicXchgU32(&pTimer->u32Magic, RTTIMER_MAGIC + 1) == RTTIMER_MAGIC)
331 {
332#ifndef __OS2__
333 pthread_kill((pthread_t)RTThreadGetNative(pTimer->Thread), SIGALRM);
334#endif
335
336 /*
337 * Wait for the thread to exit.
338 */
339 rc = RTThreadWait(pTimer->Thread, 30 * 1000, NULL);
340 if ( RT_SUCCESS(rc)
341 || rc == VERR_INVALID_HANDLE /* we don't keep handles around, you gotta wait before it really exits! */)
342 {
343 RTMemFree(pTimer);
344 return VINF_SUCCESS;
345 }
346 AssertMsgFailed(("Failed to destroy timer %p. rc=%Vrc\n", pTimer, rc));
347 }
348 else
349 {
350 AssertMsgFailed(("Timer %p is already being destroyed!\n", pTimer));
351 rc = VERR_INVALID_MAGIC;
352 }
353 }
354 else
355 {
356 AssertMsgFailed(("An attempt was made to destroy a NULL timer!\n"));
357 rc = VERR_INVALID_POINTER;
358 }
359 return rc;
360}
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