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