summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorirungentoo <irungentoo@gmail.com>2013-08-13 04:14:01 -0700
committerirungentoo <irungentoo@gmail.com>2013-08-13 04:14:01 -0700
commitcb4f67b37a1a5070387c27d475c23366645bc1cf (patch)
treedd8d64ea8814f7a26a0838a0a2e9508f62a18e95 /core
parent4cc4e79088c3478e1d50606006a39c2c872da0db (diff)
parent471c14809004bd0ac531cbb79b06906f128cb666 (diff)
Merge pull request #441 from slvr/timer
Timer Subsystem
Diffstat (limited to 'core')
-rw-r--r--core/CMakeLists.txt3
-rw-r--r--core/Messenger.c34
-rw-r--r--core/timer.c275
-rw-r--r--core/timer.h104
4 files changed, 400 insertions, 16 deletions
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index 55a41912..ad6eea01 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -10,7 +10,8 @@ set(core_sources
10 LAN_discovery.c 10 LAN_discovery.c
11 Messenger.c 11 Messenger.c
12 util.c 12 util.c
13 ping.c) 13 ping.c
14 timer.c)
14 15
15if(SHARED_TOXCORE) 16if(SHARED_TOXCORE)
16 add_library(toxcore SHARED ${core_sources}) 17 add_library(toxcore SHARED ${core_sources})
diff --git a/core/Messenger.c b/core/Messenger.c
index 1c81163c..af102406 100644
--- a/core/Messenger.c
+++ b/core/Messenger.c
@@ -22,6 +22,8 @@
22 */ 22 */
23 23
24#include "Messenger.h" 24#include "Messenger.h"
25#include "timer.h"
26
25#define MIN(a,b) (((a)<(b))?(a):(b)) 27#define MIN(a,b) (((a)<(b))?(a):(b))
26 28
27static void set_friend_status(Messenger *m, int friendnumber, uint8_t status); 29static void set_friend_status(Messenger *m, int friendnumber, uint8_t status);
@@ -488,7 +490,20 @@ int write_cryptpacket_id(Messenger *m, int friendnumber, uint8_t packet_id, uint
488 return write_cryptpacket(m->friendlist[friendnumber].crypt_connection_id, packet, length + 1); 490 return write_cryptpacket(m->friendlist[friendnumber].crypt_connection_id, packet, length + 1);
489} 491}
490 492
493
494/*Interval in seconds between LAN discovery packet sending*/
495#define LAN_DISCOVERY_INTERVAL 60
496
491#define PORT 33445 497#define PORT 33445
498
499/*Send a LAN discovery packet every LAN_DISCOVERY_INTERVAL seconds*/
500int LANdiscovery(timer* t, void* arg)
501{
502 send_LANdiscovery(htons(PORT));
503 timer_start(t, LAN_DISCOVERY_INTERVAL);
504 return 0;
505}
506
492/* run this at startup */ 507/* run this at startup */
493Messenger * initMessenger(void) 508Messenger * initMessenger(void)
494{ 509{
@@ -510,6 +525,8 @@ Messenger * initMessenger(void)
510 friendreq_init(); 525 friendreq_init();
511 LANdiscovery_init(); 526 LANdiscovery_init();
512 527
528 timer_single(&LANdiscovery, 0, LAN_DISCOVERY_INTERVAL);
529
513 return m; 530 return m;
514} 531}
515 532
@@ -668,20 +685,6 @@ void doInbound(Messenger *m)
668 } 685 }
669} 686}
670 687
671/*Interval in seconds between LAN discovery packet sending*/
672#define LAN_DISCOVERY_INTERVAL 60
673
674static uint64_t last_LANdiscovery;
675
676/*Send a LAN discovery packet every LAN_DISCOVERY_INTERVAL seconds*/
677void LANdiscovery(Messenger *m)
678{
679 if (last_LANdiscovery + LAN_DISCOVERY_INTERVAL < unix_time()) {
680 send_LANdiscovery(htons(PORT));
681 last_LANdiscovery = unix_time();
682 }
683}
684
685 688
686/* the main loop that needs to be run at least 200 times per second. */ 689/* the main loop that needs to be run at least 200 times per second. */
687void doMessenger(Messenger *m) 690void doMessenger(Messenger *m)
@@ -693,7 +696,8 @@ void doMessenger(Messenger *m)
693 doNetCrypto(); 696 doNetCrypto();
694 doInbound(m); 697 doInbound(m);
695 doFriends(m); 698 doFriends(m);
696 LANdiscovery(m); 699
700 timer_poll();
697} 701}
698 702
699/* returns the size of the messenger data (for saving) */ 703/* returns the size of the messenger data (for saving) */
diff --git a/core/timer.c b/core/timer.c
new file mode 100644
index 00000000..06e25693
--- /dev/null
+++ b/core/timer.c
@@ -0,0 +1,275 @@
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{
51 enum timer_state state;
52 timer* _prev;
53 timer* _next;
54 timer_callback cb;
55 void* userdata;
56 uint64_t deadline;
57};
58
59static timer* timer_main_queue;
60static timer* timer_us_queue; /* hi-speed queue */
61
62inline static void timer_dequeue(timer* t, timer** queue)
63{
64 if (t->state == STATE_INACTIVE) return; /* not in a queue */
65
66 if (t->_prev) {
67 t->_prev->_next = t->_next;
68 } else {
69 *queue = t->_next;
70 }
71 if (t->_next) t->_next->_prev = t->_prev;
72 t->state = STATE_INACTIVE;
73}
74
75static void timer_enqueue(timer* t, timer** queue, timer* prev)
76{
77 t->state = STATE_ACTIVE;
78 while (true) {
79 if (!*queue) {
80 t->_next = 0;
81 t->_prev = prev;
82 *queue = t;
83 return;
84 }
85
86 if ((*queue)->deadline > t->deadline) {
87 (*queue)->_prev = t;
88 t->_next = *queue;
89 t->_prev = prev;
90 *queue = t;
91 return;
92 }
93
94 prev = *queue;
95 queue = &((*queue)->_next);
96 }
97}
98
99/*** interface ***/
100
101void timer_init()
102{
103 /* Nothing needs to be done... yet. */
104}
105
106/* Do not depend on fields being zeroed */
107static timer* timer_pool; /* timer_pool is SINGLY LINKED!! */
108
109timer* new_timer(void)
110{
111 timer* ret;
112 if (timer_pool) {
113 ret = timer_pool;
114 timer_pool = timer_pool->_next;
115 } else {
116 ret = calloc(1, sizeof(struct timer));
117 }
118 ret->state = STATE_INACTIVE;
119 return ret;
120}
121
122void delete_timer(timer* t)
123{
124 timer_dequeue(t, &timer_main_queue);
125 t->_next = timer_pool;
126 t->state = STATE_INACTIVE;
127 timer_pool = t;
128}
129
130void timer_setup(timer* t, timer_callback cb, void* userarg)
131{
132 t->cb = cb;
133 t->userdata = userarg;
134}
135
136void* timer_get_userdata(timer* t)
137{
138 return t->userdata;
139}
140
141static void timer_delay_us(timer* t, int us)
142{
143 t->deadline += us;
144 timer** queue = t->_prev ? &(t->_prev->_next) : &timer_main_queue;
145 timer_dequeue(t, &timer_main_queue);
146 timer_enqueue(t, queue, t->_prev);
147}
148
149/* 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.
151 * This function may be called again after a timer has been started to adjust
152 * the expiry time. */
153void timer_start(timer* t, int sec)
154{
155 uint64_t newdeadline = current_time() + sec * US_PER_SECOND;
156 if (timer_is_active(t)){
157 if (t->deadline < newdeadline) {
158 timer_delay_us(t, newdeadline - t->deadline);
159 return;
160 }
161 timer_dequeue(t, &timer_main_queue);
162 }
163 t->deadline = newdeadline;
164 timer_enqueue(t, &timer_main_queue, 0);
165}
166
167/* Stops the timer. Returns -1 if the timer was not active. */
168int timer_stop(timer* t)
169{
170 int ret = timer_is_active(t) ? -1 : 0;
171 timer_dequeue(t, &timer_main_queue);
172 return ret;
173}
174
175/* Adds additionalsec seconds to the timer.
176 * Returns -1 and does nothing if the timer was not active. */
177int timer_delay(timer* t, int additonalsec)
178{
179 if (!timer_is_active(t)) return -1;
180 timer_delay_us(t, additonalsec * US_PER_SECOND);
181 return 0;
182}
183
184static uint64_t timer_diff(timer* t, uint64_t time)
185{
186 if (t->deadline <= time) return 0;
187 return time - t->deadline;
188}
189
190/* Returns the time remaining on a timer in seconds.
191 * 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. */
193int timer_time_remaining(timer* t)
194{
195 if (!timer_is_active(t)) return -1;
196 return timer_diff(t, current_time()) / US_PER_SECOND;
197}
198
199bool timer_is_active(timer* t)
200{
201 return t->state != STATE_INACTIVE;
202}
203
204/* Single-use timer.
205 * Creates a new timer, preforms setup and starts it. */
206void timer_single(timer_callback cb, void* userarg, int sec)
207{
208 timer* t = new_timer();
209 timer_setup(t, cb, userarg);
210 timer_start(t, sec);
211}
212
213/* Single-use microsecond timer. */
214void timer_us(timer_callback cb, void* userarg, int us)
215{
216 timer* t = new_timer();
217 timer_setup(t, cb, userarg);
218 t->deadline = current_time() + us;
219 t->state = STATE_ACTIVE;
220 timer_enqueue(t, &timer_us_queue, 0);
221}
222
223uint64_t prevtime = 0;
224void timer_poll(void)
225{
226 uint64_t time = current_time();
227
228 /* Handle millisecond timers */
229 while (timer_us_queue) {
230 if (timer_diff(timer_us_queue, time) != 0) break;
231 timer* t = timer_us_queue;
232 timer_dequeue(t, &timer_us_queue);
233 t->cb(0, t->userdata);
234 delete_timer(t);
235 }
236
237 if (time - prevtime > US_PER_SECOND || prevtime == 0 || prevtime > time) {
238 /* time moving backwards is just a sanity check */
239 prevtime = time;
240
241 while (timer_main_queue) {
242 if (timer_diff(timer_main_queue, time) != 0) break;
243 timer* t = timer_main_queue;
244 t->state = STATE_CALLBACK;
245 int rv = t->cb(t, t->userdata);
246 if (rv != 0) {
247 timer_dequeue(t, &timer_main_queue);
248 delete_timer(t);
249 continue;
250 }
251 if (t->state != STATE_ACTIVE) {
252 timer_dequeue(t, &timer_main_queue);
253 }
254 }
255 }
256}
257
258/*** Internal Testing ***/
259
260/* I do not want to expose internals to the public,
261 * which is why internals testing is done this way. */
262void timer_internal_tests(bool (*assert)(bool, char*))
263{
264
265}
266
267void timer_debug_print()
268{
269 timer* t = timer_main_queue;
270 printf("Queue:\n");
271 while (t) {
272 printf("%" PRIu64 " (%" PRIu64 ") : %s\n", t->deadline, t->deadline/US_PER_SECOND, (char*)t->userdata);
273 t = t->_next;
274 }
275}
diff --git a/core/timer.h b/core/timer.h
new file mode 100644
index 00000000..8844a1dd
--- /dev/null
+++ b/core/timer.h
@@ -0,0 +1,104 @@
1/* timer.h
2 *
3 * Timing subsystem. Provides deadline timers.
4 * All times are aliased to a second for efficiency.
5 *
6 * Timer Guarantees:
7 * - The callback will not be called before the timer expires.
8 * - The callback will be called sometime after the timer expires,
9 * on a best effort basis.
10 * - If timer_poll is called at least once a second, the callback
11 * will be called at most one second after it expires.
12 *
13 * Copyright (C) 2013 Tox project All Rights Reserved.
14 *
15 * This file is part of Tox.
16 *
17 * Tox is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * Tox is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
29 *
30 */
31
32#ifndef TIMER_H
33#define TIMER_H
34
35#include <stdint.h>
36#include <stdbool.h>
37
38#define US_PER_SECOND 1000000 /* 1 s = 10^6 us */
39
40struct timer;
41typedef struct timer timer;
42
43/* If time_callback returns a non-zero value, timer t is deleted.
44 * You may call any of the timer functions within the callback:
45 * For example, you may call timer_start to restart the timer from
46 * within a callback. */
47typedef int (*timer_callback)(timer* t, void* userarg);
48
49/* Initisalise timer subsystem */
50void timer_init(void);
51
52/* Poll. (I will eventually replace all polling in Tox with an async system.) */
53void timer_poll(void);
54
55/* Creates a new timer. Does not enqueue/start it. */
56timer* new_timer(void);
57
58/* Destroys a timer instance. */
59void delete_timer(timer* t);
60
61/* Sets up the timer callback. */
62void timer_setup(timer* t, timer_callback cb, void* userarg);
63
64/* Accessor Function. */
65void* timer_get_userdata(timer* t);
66
67/* Starts the timer so that it's called in sec seconds in the future from now.
68 * A non-positive value of sec results in the callback being called immediately.
69 * This function may be called again after a timer has been started to adjust
70 * the expiry time. */
71void timer_start(timer* t, int sec);
72
73/* Stops the timer. Returns -1 if the timer was not active. */
74int timer_stop(timer* t);
75
76/* Adds additionalsec seconds to the timer.
77 * Returns -1 and does nothing if the timer was not active. */
78int timer_delay(timer* t, int additonalsec);
79
80/* Returns the time remaining on a timer in seconds.
81 * Returns -1 if the timer is not active.
82 * Returns 0 if the timer has expired and the callback hasn't been called yet. */
83int timer_time_remaining(timer* t);
84
85/* Determines if timer is active. Returns TRUE if it is active */
86bool timer_is_active(timer* t);
87
88/* Single-use timer.
89 * Creates a new timer, preforms setup and starts it.
90 * Callback must return a non-zero value to prevent memory leak. */
91void timer_single(timer_callback cb, void* userarg, int sec);
92
93/* Single-use microsecond timer.
94 * Creates a new timer, preforms setup and starts it.
95 * Please do not use this when accuracy is not absolutely required.
96 * Use when one needs to time a period < 1 s.
97 * Use the more coarse timers above for periods > 5 s.
98 * WARNING: the callback will be called with NULL as the first argument */
99void timer_us(timer_callback cb, void* userarg, int us);
100
101/* Internal Testing */
102void timer_internal_tests(bool(*)(bool, char*));
103
104#endif