1 /* PEAK Library
2 *
3 * Copyright (c) 2003, 2004
4 * Stephane Thiell <mbuna@bugged.org>. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 */
30 #define RCSID "$Id: timer.c,v 1.6 2004/01/12 01:52:11 mbuna Exp $"
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #include <peak/timer.h>
37 #include <peak/time.h>
38 #include "internal.h"
39 #include "task_private.h"
40
41 #include <assert.h>
42 #include <limits.h>
43 #include <stdlib.h>
44 #include <sys/time.h>
45 #include <unistd.h>
46
47 /* "FreeBSD is constricting for the max timeval value passed to select(),
48 * which might return EINVAL. The following value should be ok on machines
49 * with standard HZ (100). It seems to avoid some additional calculations
50 * and roundings on Linux, too."
51 */
52 #define TV_MAX_SECONDS ((long) (LONG_MAX / 100) - 1)
53
54
55 static void __peak_timer_init(peak_timer ti, va_list vp, void *ctcx);
56 static void __peak_timer_finalize(peak_timer ti);
57
58 PEAK_CLASS_BASE_DEFINE_VIRTUAL(timer);
59
60 /* Local
61 */
62
63 static inline double
__peak_timer_abmax(void)64 __peak_timer_abmax(void)
65 {
66 return (double)LONG_MAX;
67 }
68
69 static inline double
__peak_timer_tv2ab(struct timeval * tv)70 __peak_timer_tv2ab(struct timeval *tv)
71 {
72 return (double)tv->tv_sec + 1.0E-6 * (double)tv->tv_usec;
73 }
74
75 static inline struct timeval
__peak_timer_ab2tv(double date)76 __peak_timer_ab2tv(double date)
77 {
78 struct timeval tv;
79 double floor_date = (double)((long)date);
80 tv.tv_sec = (date < 0. || TV_MAX_SECONDS <= date)
81 ? TV_MAX_SECONDS
82 : (time_t)floor_date;
83 tv.tv_usec = (long)((date - floor_date) * 1.0E+6);
84 return tv;
85 }
86
87 static inline struct timespec
__peak_timer_ab2ts(double date)88 __peak_timer_ab2ts(double date)
89 {
90 struct timespec ts;
91 double floor_date = (double)((long)date);
92 ts.tv_sec = (date < 0. || (double)TV_MAX_SECONDS <= date)
93 ? TV_MAX_SECONDS
94 : (time_t)floor_date;
95 ts.tv_nsec = (long)((date - floor_date) * 1.0E+9);
96 return ts;
97 }
98
99 peak_timer
peak_timer_create(double fire,double interval,peak_timer_callback callout,void * context)100 peak_timer_create(double fire, double interval, peak_timer_callback callout,
101 void *context)
102 {
103 return PEAK_CLASS_CONSTRUCT4(timer, fire, interval, callout, context);
104 }
105
106 static void
__peak_timer_init(peak_timer ti,va_list vp,void * ctcx)107 __peak_timer_init(peak_timer ti, va_list vp, void *ctcx)
108 {
109 double fire, interval;
110 double fire_date;
111
112 fire = va_arg(vp, double);
113 interval = va_arg(vp, double);
114
115 ti->left = NULL;
116 ti->right = NULL;
117
118 if (fire < -0.5)
119 {
120 ti->_fire = __peak_timer_abmax();
121 }
122 else
123 {
124 fire_date = peak_time_float() + fire;
125
126 if (__peak_timer_abmax() < fire_date)
127 ti->_fire = __peak_timer_abmax();
128 else
129 ti->_fire = fire_date;
130 }
131 if (interval <= 0. || __peak_timer_abmax() < interval)
132 {
133 ti->_interval = __peak_timer_abmax();
134 ti->_mode = PEAK_TIMER_MODE_ONCE;
135 }
136 else
137 {
138 ti->_interval = interval;
139 ti->_mode = PEAK_TIMER_MODE_REQUEUE;
140 }
141
142 ti->_callout = va_arg(vp, peak_timer_callback);
143 ti->_context = va_arg(vp, void *);
144 ti->_task = NULL;
145 }
146
147 static void
__peak_timer_finalize(peak_timer ti)148 __peak_timer_finalize(peak_timer ti)
149 {
150 if (ti->_task)
151 peak_task_timer_remove(ti->_task, ti);
152 }
153
154 void
peak_timer_configure(peak_timer ti,double fire,double interval)155 peak_timer_configure(peak_timer ti, double fire, double interval)
156 {
157 _peak_task_timer_lock_configure(ti->_task, ti, fire, interval);
158 }
159
160 __private_extern__ void
_peak_timer_configure(peak_timer ti,double fire,double interval)161 _peak_timer_configure(peak_timer ti, double fire, double interval)
162 {
163 if (fire < -0.5)
164 {
165 ti->_fire = __peak_timer_abmax();
166 }
167 else
168 {
169 double fire_date = peak_time_float() + fire;
170
171 if (__peak_timer_abmax() < fire)
172 ti->_fire = __peak_timer_abmax();
173 else
174 ti->_fire = fire_date;
175 }
176
177 /* -1.0 ? do not repeat then */
178 if (interval <= 0.0 || __peak_timer_abmax() < interval)
179 {
180 ti->_interval = __peak_timer_abmax();
181 ti->_mode = PEAK_TIMER_MODE_ONCE;
182 }
183 else
184 {
185 ti->_interval = interval;
186 ti->_mode = PEAK_TIMER_MODE_REQUEUE;
187 }
188 }
189
190 double
peak_timer_get_firetime(peak_timer ti)191 peak_timer_get_firetime(peak_timer ti)
192 {
193 return ti->_fire;
194 }
195
196 double
peak_timer_get_interval(peak_timer ti)197 peak_timer_get_interval(peak_timer ti)
198 {
199 return ti->_interval;
200 }
201
202 void*
peak_timer_get_context(peak_timer ti)203 peak_timer_get_context(peak_timer ti)
204 {
205 return ti->_context;
206 }
207
208 void
peak_timer_set_context(peak_timer t,void * context)209 peak_timer_set_context(peak_timer t, void *context)
210 {
211 t->_context = context;
212 }
213
214 /* Private
215 */
216
217 __private_extern__ void
_peak_timer_rearm(peak_timer ti,double t_limit)218 _peak_timer_rearm(peak_timer ti, double t_limit)
219 {
220 ti->_fire += ti->_interval;
221
222 if (ti->_fire > __peak_timer_abmax()) /* sanity */
223 ti->_fire = __peak_timer_abmax();
224
225 /* Adjust fire date if needed (for slow machine, etc.) */
226 if (ti->_fire < t_limit)
227 ti->_fire = t_limit;
228 }
229
230 __private_extern__ void
_peak_timer_fire(peak_timer ti)231 _peak_timer_fire(peak_timer ti)
232 {
233 (*ti->_callout)(ti, ti->_context);
234 }
235
236 __private_extern__ double
_peak_timer_expire_relative(peak_timer ti)237 _peak_timer_expire_relative(peak_timer ti)
238 {
239 struct timeval tv_now;
240 double result;
241
242 /* Don't use current time here. We could but... next events or timers
243 * will be much more accurate if events we've just processed took
244 * some time.
245 */
246 gettimeofday(&tv_now, NULL);
247
248 result = ti->_fire - __peak_timer_tv2ab(&tv_now);
249
250 #if 0
251 if (result < 0.0)
252 printf("_peak_timer_expire_relative: %f now: %f\n", ti->_fire,
253 __peak_timer_tv2ab(&tv_now));
254 #endif
255 return (result > 0.0) ? result : 0.0;
256 }
257
258 __private_extern__ struct timeval*
_peak_timer_expire_relative_tv(peak_timer ti,struct timeval * tv)259 _peak_timer_expire_relative_tv(peak_timer ti, struct timeval *tv)
260 {
261 *tv = __peak_timer_ab2tv(_peak_timer_expire_relative(ti));
262 return tv;
263 }
264
265 __private_extern__ struct timespec*
_peak_timer_expire_relative_ts(peak_timer ti,struct timespec * ts)266 _peak_timer_expire_relative_ts(peak_timer ti, struct timespec *ts)
267 {
268 *ts = __peak_timer_ab2ts(_peak_timer_expire_relative(ti));
269 return ts;
270 }
271
272