1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2006-2019 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "E.h"
25 #include "list.h"
26 #include "timers.h"
27 
28 struct _timer {
29    unsigned int        in_time;
30    unsigned int        at_time;
31    struct _timer      *next;
32    int                 (*func)(void *data);
33    void               *data;
34    char                again;
35 };
36 
37 static int
tdiff(unsigned int t1,unsigned int t2)38 tdiff(unsigned int t1, unsigned int t2)
39 {
40    return (int)(t1 - t2);
41 }
42 
43 static Timer       *q_first = NULL;
44 
45 static void
_TimerSet(Timer * timer)46 _TimerSet(Timer * timer)
47 {
48    Timer              *ptr, *pptr;
49 
50    if (EDebug(EDBUG_TYPE_TIMERS) > 1)
51       Eprintf("%s %p: func=%p data=%p\n", __func__, timer, timer->func,
52 	      timer->data);
53 
54    /* if there is no queue it becomes the queue */
55    if (!q_first)
56      {
57 	q_first = timer;
58 	timer->next = NULL;
59      }
60    else
61      {
62 	pptr = NULL;
63 	for (ptr = q_first; ptr; pptr = ptr, ptr = ptr->next)
64 	  {
65 	     if (tdiff(ptr->at_time, timer->at_time) > 0)
66 		break;
67 	  }
68 	if (pptr)
69 	   pptr->next = timer;
70 	else
71 	   q_first = timer;
72 	timer->next = ptr;
73      }
74 }
75 
76 static void
_TimerDel(Timer * timer)77 _TimerDel(Timer * timer)
78 {
79    if (EDebug(EDBUG_TYPE_TIMERS))
80       Eprintf("%s %p: func=%p data=%p\n", __func__, timer, timer->func,
81 	      timer->data);
82    Efree(timer);
83 }
84 
85 Timer              *
TimerAdd(int dt_ms,int (* func)(void * data),void * data)86 TimerAdd(int dt_ms, int (*func)(void *data), void *data)
87 {
88    Timer              *timer;
89 
90    timer = EMALLOC(Timer, 1);
91    if (!timer)
92       return NULL;
93 
94    timer->in_time = (unsigned int)dt_ms;
95    timer->at_time = GetTimeMs() + dt_ms;
96    timer->func = func;
97    timer->data = data;
98    timer->again = 1;
99 
100    if (EDebug(EDBUG_TYPE_TIMERS))
101       Eprintf("%s %p: func=%p data=%p: %8d\n", __func__, timer,
102 	      timer->func, timer->data, dt_ms);
103 
104    _TimerSet(timer);		/* Add to timer queue */
105 
106    return timer;
107 }
108 
109 void
TimersRun(unsigned int t_ms)110 TimersRun(unsigned int t_ms)
111 {
112    Timer              *timer, *q_old, *q_run;
113 
114    timer = q_first;
115    if (!timer)
116       return;			/* No timers pending */
117 
118    q_run = q_old = timer;
119    for (; timer; timer = q_first)
120      {
121 	if (tdiff(timer->at_time, t_ms) > 0)
122 	   break;
123 
124 	if (EDebug(EDBUG_TYPE_TIMERS))
125 	   Eprintf("%s - run %p: func=%p data=%p: %8d\n", __func__, timer,
126 		   timer->func, timer->data, timer->at_time - t_ms);
127 
128 	q_first = timer->next;
129 
130 	/* Run this callback */
131 	timer->again = timer->func(timer->data);
132 	q_run = timer;
133      }
134 
135    if (q_old != q_first)
136      {
137 	/* At least one timer has run */
138 	q_run->next = NULL;	/* Terminate expired timer list */
139 
140 	/* Re-schedule/remove timers that have run */
141 	for (timer = q_old; timer; timer = q_old)
142 	  {
143 	     q_old = timer->next;
144 	     if (timer->again)
145 	       {
146 		  timer->at_time += timer->in_time;
147 		  _TimerSet(timer);	/* Add to timer queue */
148 	       }
149 	     else
150 	       {
151 		  _TimerDel(timer);
152 	       }
153 	  }
154      }
155 
156    if (EDebug(EDBUG_TYPE_TIMERS) > 1)
157      {
158 	for (timer = q_first; timer; timer = timer->next)
159 	   Eprintf("%s - pend %p: func=%p data=%p: %8d (%d)\n", __func__,
160 		   timer, timer->func, timer->data, timer->at_time - t_ms,
161 		   timer->in_time);
162      }
163 }
164 
165 int
TimersRunNextIn(unsigned int t_ms)166 TimersRunNextIn(unsigned int t_ms)
167 {
168    Timer              *timer;
169    int                 dt;
170 
171    timer = q_first;
172 
173    /* If the next (rescheduled) timer is already expired, set timeout time
174     * to 1 ms. This avoids starving the fd's and should maintain the intended
175     * (mean) timer rate.
176     * The (mean) amount of work done in a timer function should of course not
177     * exceed the timeout time. */
178    if (timer)
179       dt = (int)(timer->at_time - t_ms) > 0 ? (int)(timer->at_time - t_ms) : 1;
180    else
181       dt = 0;
182 
183    if (EDebug(EDBUG_TYPE_TIMERS))
184       Eprintf("%s - next in %8d\n", __func__, dt);
185 
186    return dt;
187 }
188 
189 void
TimerDel(Timer * timer)190 TimerDel(Timer * timer)
191 {
192    Timer              *qe, *ptr, *pptr;
193 
194    pptr = NULL;
195    for (ptr = q_first; ptr; pptr = ptr, ptr = ptr->next)
196      {
197 	qe = ptr;
198 	if (qe != timer)
199 	   continue;
200 
201 	/* Match - remove it from the queue */
202 	if (pptr)
203 	   pptr->next = qe->next;
204 	else
205 	   q_first = qe->next;
206 
207 	/* free it */
208 	_TimerDel(timer);
209 	break;
210      }
211 }
212 
213 void
TimerSetInterval(Timer * timer,int dt_ms)214 TimerSetInterval(Timer * timer, int dt_ms)
215 {
216    timer->in_time = (unsigned int)dt_ms;
217 }
218 
219 /*
220  * Idlers
221  */
222 static              LIST_HEAD(idler_list);
223 
224 typedef void        (IdlerFunc) (void *data);
225 
226 struct _idler {
227    dlist_t             list;
228    IdlerFunc          *func;
229    void               *data;
230 };
231 
232 Idler              *
IdlerAdd(IdlerFunc * func,void * data)233 IdlerAdd(IdlerFunc * func, void *data)
234 {
235    Idler              *id;
236 
237    id = EMALLOC(Idler, 1);
238    if (!id)
239       return NULL;
240 
241    id->func = func;
242    id->data = data;
243 
244    LIST_APPEND(Idler, &idler_list, id);
245 
246    return id;
247 }
248 
249 void
IdlerDel(Idler * id)250 IdlerDel(Idler * id)
251 {
252    LIST_REMOVE(Idler, &idler_list, id);
253    Efree(id);
254 }
255 
256 static void
_IdlerRun(void * _id,void * prm __UNUSED__)257 _IdlerRun(void *_id, void *prm __UNUSED__)
258 {
259    Idler              *id = (Idler *) _id;
260 
261    if (EDebug(EDBUG_TYPE_IDLERS) > 1)
262       Eprintf("%s: func=%p\n", __func__, id->func);
263    id->func(id->data);
264 }
265 
266 void
IdlersRun(void)267 IdlersRun(void)
268 {
269    Idler              *id;
270 
271    if (EDebug(EDBUG_TYPE_IDLERS))
272       Eprintf("%s B\n", __func__);
273    LIST_FOR_EACH(Idler, &idler_list, id) _IdlerRun(id, NULL);
274    if (EDebug(EDBUG_TYPE_IDLERS))
275       Eprintf("%s E\n", __func__);
276 }
277