1 |
/// @file systimer.cc |
2 |
/// @author Kimball Thurston |
3 |
/// |
4 |
|
5 |
// |
6 |
// Copyright (c) 2004 Kimball Thurston |
7 |
// |
8 |
// This program is free software; you can redistribute it and/or modify |
9 |
// it under the terms of the GNU General Public License version 2 as |
10 |
// published by the Free Software Foundation. |
11 |
// |
12 |
// This program is distributed in the hope that it will be useful, |
13 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
// GNU General Public License for more details. |
16 |
// |
17 |
// You should have received a copy of the GNU General Public License |
18 |
// along with this program; if not, write to the Free Software |
19 |
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
20 |
// |
21 |
|
22 |
#include <signal.h> |
23 |
#include <time.h> |
24 |
#include <stdio.h> |
25 |
#include <string.h> |
26 |
#include <sys/time.h> |
27 |
|
28 |
#include "system/systimer.h" |
29 |
#include "tools/snprintf.h" |
30 |
|
31 |
static const int kTimerSignal = SYSTIMER_SIGNAL; |
32 |
#ifdef USE_POSIX_REALTIME_CLOCK |
33 |
static void signal_handler(int signo, siginfo_t *extra, void *junk); |
34 |
static const int kClockRT = CLOCK_PROCESS_CPUTIME_ID; |
35 |
static const int kClock = CLOCK_REALTIME; |
36 |
#elif USE_POSIX_SETITIMER |
37 |
static void signal_handler(int signo); |
38 |
static const int kClock = ITIMER_REAL; |
39 |
struct sys_timer_struct; |
40 |
static sys_timer_struct *gSingleTimer = NULL; |
41 |
static const int kSignalFlags = 0; |
42 |
#else |
43 |
#error no timer support |
44 |
#endif |
45 |
|
46 |
struct sys_timer_struct |
47 |
{ |
48 |
#ifdef USE_POSIX_REALTIME_CLOCK |
49 |
struct sigevent event_info; |
50 |
timer_t timer_id; |
51 |
#endif |
52 |
sys_timer_callback callback; |
53 |
int clock; |
54 |
uint64 timer_res; |
55 |
|
56 |
sys_timer_struct(sys_timer_callback cb) |
57 |
: callback(cb), clock(kClock), timer_res(0) |
58 |
{ |
59 |
#ifdef USE_POSIX_REALTIME_CLOCK |
60 |
memset(&event_info, 0, sizeof event_info); |
61 |
timer_id = 0; |
62 |
|
63 |
event_info.sigev_notify = SIGEV_SIGNAL; |
64 |
event_info.sigev_signo = kTimerSignal; |
65 |
event_info.sigev_value.sival_ptr = this; |
66 |
#endif |
67 |
} |
68 |
}; |
69 |
|
70 |
#ifdef USE_POSIX_REALTIME_CLOCK |
71 |
static void signal_handler(int signo, siginfo_t *extra, void *junk) |
72 |
{ |
73 |
sys_timer_struct *timer = reinterpret_cast<sys_timer_struct *>(extra->si_value.sival_ptr); |
74 |
timer->callback(reinterpret_cast<sys_timer>(timer)); |
75 |
} |
76 |
#else |
77 |
# ifdef USE_POSIX_SETITIMER |
78 |
static void signal_handler(int signo) |
79 |
{ |
80 |
if (gSingleTimer != NULL) { |
81 |
gSingleTimer->callback(reinterpret_cast<sys_timer>(gSingleTimer)); |
82 |
} |
83 |
} |
84 |
# endif |
85 |
#endif |
86 |
|
87 |
bool sys_create_timer(sys_timer *t, sys_timer_callback cb_func) |
88 |
{ |
89 |
*t = 0; |
90 |
|
91 |
sys_timer_struct *newTimer = new sys_timer_struct(cb_func); |
92 |
|
93 |
#ifdef USE_POSIX_REALTIME_CLOCK |
94 |
int clocks[] = {kClockRT, kClock}; |
95 |
|
96 |
struct timespec clockRes; |
97 |
bool foundTimer = false; |
98 |
|
99 |
for (uint i=0; i < (sizeof clocks / sizeof clocks[0]); i++) { |
100 |
if (clock_getres(clocks[i], &clockRes) == 0 |
101 |
&& timer_create(clocks[i], &newTimer->event_info, |
102 |
&newTimer->timer_id) == 0) { |
103 |
|
104 |
newTimer->clock = clocks[i]; |
105 |
foundTimer = true; |
106 |
break; |
107 |
} |
108 |
} |
109 |
|
110 |
if (!foundTimer) { |
111 |
perror("Timer create error"); |
112 |
delete newTimer; |
113 |
return false; |
114 |
} |
115 |
|
116 |
newTimer->timer_res = (uint64)clockRes.tv_sec * 1000 * 1000 * 1000; |
117 |
newTimer->timer_res += (uint64)clockRes.tv_nsec; |
118 |
#else |
119 |
# ifdef USE_POSIX_SETITIMER |
120 |
if (gSingleTimer != NULL) { |
121 |
ht_printf("There can only be one active sys timer at a time using\n" |
122 |
"interval timers.\n"); |
123 |
delete newTimer; |
124 |
return false; |
125 |
} |
126 |
newTimer->timer_res = 10 * 1000 * 1000; |
127 |
# endif |
128 |
#endif |
129 |
|
130 |
struct sigaction act; |
131 |
|
132 |
sigemptyset(&act.sa_mask); |
133 |
#ifdef USE_POSIX_REALTIME_CLOCK |
134 |
act.sa_sigaction = signal_handler; |
135 |
act.sa_flags = SA_SIGINFO; |
136 |
#else |
137 |
# ifdef USE_POSIX_SETITIMER |
138 |
act.sa_handler = signal_handler; |
139 |
act.sa_flags = 0; |
140 |
# endif |
141 |
#endif |
142 |
|
143 |
if (sigemptyset(&act.sa_mask) == -1) { |
144 |
perror("Error calling sigemptyset"); |
145 |
return false; |
146 |
} |
147 |
|
148 |
if (sigaction(kTimerSignal, &act, 0) == -1) { |
149 |
perror("Error calling sigaction"); |
150 |
return false; |
151 |
} |
152 |
|
153 |
*t = reinterpret_cast<sys_timer>(newTimer); |
154 |
#ifdef USE_POSIX_SETITIMER |
155 |
gSingleTimer = newTimer; |
156 |
#endif |
157 |
|
158 |
return true; |
159 |
} |
160 |
|
161 |
void sys_delete_timer(sys_timer t) |
162 |
{ |
163 |
sys_timer_struct *timer = reinterpret_cast<sys_timer_struct *>(t); |
164 |
|
165 |
#ifdef USE_POSIX_REALTIME_CLOCK |
166 |
timer_delete(timer->timer_id); |
167 |
#else |
168 |
# ifdef USE_POSIX_SETITIMER |
169 |
struct itimerval itime; |
170 |
|
171 |
itime.it_value.tv_sec = 0; |
172 |
itime.it_value.tv_usec = 0; |
173 |
itime.it_interval.tv_sec = 0; |
174 |
itime.it_interval.tv_usec = 0; |
175 |
|
176 |
setitimer(timer->clock, &itime, NULL); |
177 |
gSingleTimer = NULL; |
178 |
# endif |
179 |
#endif |
180 |
|
181 |
delete timer; |
182 |
} |
183 |
|
184 |
void sys_set_timer(sys_timer t, time_t secs, long int nanosecs, bool periodic) |
185 |
{ |
186 |
sys_timer_struct *timer = reinterpret_cast<sys_timer_struct *>(t); |
187 |
#ifdef USE_POSIX_REALTIME_CLOCK |
188 |
struct itimerspec itime; |
189 |
|
190 |
itime.it_value.tv_sec = secs; |
191 |
// FIXME: Do we need to have rounding based on timer resolution here? |
192 |
itime.it_value.tv_nsec = nanosecs;// + (nanosecs % timer->timer_res); |
193 |
|
194 |
if (periodic) { |
195 |
itime.it_interval.tv_sec = secs; |
196 |
itime.it_interval.tv_nsec = nanosecs; |
197 |
} else { |
198 |
itime.it_interval.tv_sec = 0; |
199 |
itime.it_interval.tv_nsec = 0; |
200 |
} |
201 |
|
202 |
if (timer_settime(timer->timer_id, 0, &itime, NULL) < 0) { |
203 |
perror(__FUNCTION__); |
204 |
} |
205 |
|
206 |
#else |
207 |
# ifdef USE_POSIX_SETITIMER |
208 |
struct itimerval itime; |
209 |
|
210 |
itime.it_value.tv_sec = secs; |
211 |
itime.it_value.tv_usec = (nanosecs + 500) / 1000; |
212 |
|
213 |
if (periodic) { |
214 |
itime.it_interval = itime.it_value; |
215 |
} else { |
216 |
itime.it_interval.tv_sec = 0; |
217 |
itime.it_interval.tv_usec = 0; |
218 |
} |
219 |
|
220 |
setitimer(timer->clock, &itime, NULL); |
221 |
# endif |
222 |
#endif |
223 |
} |
224 |
|
225 |
uint64 sys_get_timer_resolution(sys_timer t) |
226 |
{ |
227 |
sys_timer_struct *timer = reinterpret_cast<sys_timer_struct *>(t); |
228 |
return timer->timer_res; |
229 |
} |
230 |
|
231 |
uint64 sys_get_hiresclk_ticks() |
232 |
{ |
233 |
#if HAVE_GETTIMEOFDAY |
234 |
struct timeval tv; |
235 |
struct timezone tz; |
236 |
|
237 |
gettimeofday(&tv, &tz); |
238 |
//__asm__ __volatile__("rdtsc" : "=A" (retval)); |
239 |
|
240 |
return ((uint64)tv.tv_sec * 1000000) + tv.tv_usec; |
241 |
#else |
242 |
return clock(); |
243 |
#endif |
244 |
} |
245 |
|
246 |
uint64 sys_get_hiresclk_ticks_per_second() |
247 |
{ |
248 |
#if HAVE_GETTIMEOFDAY |
249 |
return 1000000; |
250 |
#else |
251 |
return clock(); |
252 |
#endif |
253 |
} |