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