summaryrefslogtreecommitdiff
path: root/core/timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/timer.c')
-rw-r--r--core/timer.c290
1 files changed, 0 insertions, 290 deletions
diff --git a/core/timer.c b/core/timer.c
deleted file mode 100644
index 29190921..00000000
--- a/core/timer.c
+++ /dev/null
@@ -1,290 +0,0 @@
1#define __STDC_FORMAT_MACROS
2#include <inttypes.h>
3
4#include "timer.h"
5#include "network.h"
6
7/*
8A nested linked list increases efficiency of insertions.
9Depending on the number of timers we have, we might need to have nested linked lists
10in order to improve insertion efficiency.
11The code below is preperation for that end, should it be necessary.
12
13typedef struct {
14 struct timer_package* _next;
15 union {
16 timer_packet* _inner;
17 timer* queue;
18 };
19 uint64_t pkgtime;
20} timer_package;
21
22timer_package* timer_package_pool;
23
24static timer_package* new_package()
25{
26 timer_package* ret;
27 if (timer_package_pool) {
28 ret = timer_package_pool;
29 timer_package_pool = timer_package_pool->_next;
30 } else {
31 ret = calloc(1, sizeof(struct timer_package));
32 }
33 return ret;
34}
35
36static void delete_package(timer_package* p)
37{
38 p->_next = timer_package_pool;
39 timer_package_pool = p;
40}
41*/
42
43enum timer_state {
44 STATE_INACTIVE = 0,
45 STATE_ACTIVE,
46 STATE_CALLBACK
47};
48
49struct timer {
50 enum timer_state state;
51 timer *_prev;
52 timer *_next;
53 timer_callback cb;
54 void *userdata;
55 uint64_t deadline;
56};
57
58static timer *timer_main_queue;
59static timer *timer_us_queue; /* hi-speed queue */
60
61inline static void timer_dequeue(timer *t, timer **queue)
62{
63 if (t->state == STATE_INACTIVE) return; /* not in a queue */
64
65 if (t->_prev) {
66 t->_prev->_next = t->_next;
67 } else {
68 *queue = t->_next;
69 }
70
71 if (t->_next) t->_next->_prev = t->_prev;
72
73 t->state = STATE_INACTIVE;
74}
75
76static void timer_enqueue(timer *t, timer **queue, timer *prev)
77{
78 t->state = STATE_ACTIVE;
79
80 while (true) {
81 if (!*queue) {
82 t->_next = 0;
83 t->_prev = prev;
84 *queue = t;
85 return;
86 }
87
88 if ((*queue)->deadline > t->deadline) {
89 (*queue)->_prev = t;
90 t->_next = *queue;
91 t->_prev = prev;
92 *queue = t;
93 return;
94 }
95
96 prev = *queue;
97 queue = &((*queue)->_next);
98 }
99}
100
101/*** interface ***/
102
103void timer_init()
104{
105 /* Nothing needs to be done... yet. */
106}
107
108/* Do not depend on fields being zeroed */
109static timer *timer_pool; /* timer_pool is SINGLY LINKED!! */
110
111timer *new_timer(void)
112{
113 timer *ret;
114
115 if (timer_pool) {
116 ret = timer_pool;
117 timer_pool = timer_pool->_next;
118 } else {
119 ret = calloc(1, sizeof(struct timer));
120 }
121
122 ret->state = STATE_INACTIVE;
123 return ret;
124}
125
126void delete_timer(timer *t)
127{
128 timer_dequeue(t, &timer_main_queue);
129 t->_next = timer_pool;
130 t->state = STATE_INACTIVE;
131 timer_pool = t;
132}
133
134void timer_setup(timer *t, timer_callback cb, void *userarg)
135{
136 t->cb = cb;
137 t->userdata = userarg;
138}
139
140void *timer_get_userdata(timer *t)
141{
142 return t->userdata;
143}
144
145static void timer_delay_us(timer *t, int us)
146{
147 t->deadline += us;
148 timer **queue = t->_prev ? &(t->_prev->_next) : &timer_main_queue;
149 timer_dequeue(t, &timer_main_queue);
150 timer_enqueue(t, queue, t->_prev);
151}
152
153/* Starts the timer so that it's called in sec seconds in the future.
154 * A non-positive value of sec results in the callback being called immediately.
155 * This function may be called again after a timer has been started to adjust
156 * the expiry time. */
157void timer_start(timer *t, int sec)
158{
159 uint64_t newdeadline = current_time() + sec * US_PER_SECOND;
160
161 if (timer_is_active(t)) {
162 if (t->deadline < newdeadline) {
163 timer_delay_us(t, newdeadline - t->deadline);
164 return;
165 }
166
167 timer_dequeue(t, &timer_main_queue);
168 }
169
170 t->deadline = newdeadline;
171 timer_enqueue(t, &timer_main_queue, 0);
172}
173
174/* Stops the timer. Returns -1 if the timer was not active. */
175int timer_stop(timer *t)
176{
177 int ret = timer_is_active(t) ? -1 : 0;
178 timer_dequeue(t, &timer_main_queue);
179 return ret;
180}
181
182/* Adds additionalsec seconds to the timer.
183 * Returns -1 and does nothing if the timer was not active. */
184int timer_delay(timer *t, int additonalsec)
185{
186 if (!timer_is_active(t)) return -1;
187
188 timer_delay_us(t, additonalsec * US_PER_SECOND);
189 return 0;
190}
191
192static uint64_t timer_diff(timer *t, uint64_t time)
193{
194 if (t->deadline <= time) return 0;
195
196 return time - t->deadline;
197}
198
199/* Returns the time remaining on a timer in seconds.
200 * Returns -1 if the timer is not active.
201 * Returns 0 if the timer has expired and will be called upon the next call to timer_poll. */
202int timer_time_remaining(timer *t)
203{
204 if (!timer_is_active(t)) return -1;
205
206 return timer_diff(t, current_time()) / US_PER_SECOND;
207}
208
209bool timer_is_active(timer *t)
210{
211 return t->state != STATE_INACTIVE;
212}
213
214/* Single-use timer.
215 * Creates a new timer, preforms setup and starts it. */
216void timer_single(timer_callback cb, void *userarg, int sec)
217{
218 timer *t = new_timer();
219 timer_setup(t, cb, userarg);
220 timer_start(t, sec);
221}
222
223/* Single-use microsecond timer. */
224void timer_us(timer_callback cb, void *userarg, int us)
225{
226 timer *t = new_timer();
227 timer_setup(t, cb, userarg);
228 t->deadline = current_time() + us;
229 t->state = STATE_ACTIVE;
230 timer_enqueue(t, &timer_us_queue, 0);
231}
232
233uint64_t prevtime = 0;
234void timer_poll(void)
235{
236 uint64_t time = current_time();
237
238 /* Handle millisecond timers */
239 while (timer_us_queue) {
240 if (timer_diff(timer_us_queue, time) != 0) break;
241
242 timer *t = timer_us_queue;
243 timer_dequeue(t, &timer_us_queue);
244 t->cb(0, t->userdata);
245 delete_timer(t);
246 }
247
248 if (time - prevtime > US_PER_SECOND || prevtime == 0 || prevtime > time) {
249 /* time moving backwards is just a sanity check */
250 prevtime = time;
251
252 while (timer_main_queue) {
253 if (timer_diff(timer_main_queue, time) != 0) break;
254
255 timer *t = timer_main_queue;
256 t->state = STATE_CALLBACK;
257 int rv = t->cb(t, t->userdata);
258
259 if (rv != 0) {
260 timer_dequeue(t, &timer_main_queue);
261 delete_timer(t);
262 continue;
263 }
264
265 if (t->state != STATE_ACTIVE) {
266 timer_dequeue(t, &timer_main_queue);
267 }
268 }
269 }
270}
271
272/*** Internal Testing ***/
273
274/* I do not want to expose internals to the public,
275 * which is why internals testing is done this way. */
276void timer_internal_tests(bool (*assert)(bool, char *))
277{
278
279}
280
281void timer_debug_print()
282{
283 timer *t = timer_main_queue;
284 printf("Queue:\n");
285
286 while (t) {
287 printf("%" PRIu64 " (%" PRIu64 ") : %s\n", t->deadline, t->deadline / US_PER_SECOND, (char *)t->userdata);
288 t = t->_next;
289 }
290}