summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/CMakeLists.txt3
-rw-r--r--core/Messenger.c46
-rw-r--r--core/Messenger.h16
-rw-r--r--core/timer.c275
-rw-r--r--core/timer.h104
5 files changed, 422 insertions, 22 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 ebde5a78..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);
@@ -267,10 +269,18 @@ int setname(Messenger *m, uint8_t * name, uint16_t length)
267 put it in name 269 put it in name
268 name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH bytes. 270 name needs to be a valid memory location with a size of at least MAX_NAME_LENGTH bytes.
269 return the length of the name */ 271 return the length of the name */
270uint16_t getself_name(Messenger *m, uint8_t *name) 272uint16_t getself_name(Messenger *m, uint8_t *name, uint16_t nlen)
271{ 273{
274 uint16_t len;
275
276 if (name == NULL || nlen == 0) {
277 return 0;
278 }
279
280 len = MIN(nlen, m->name_length);
272 memcpy(name, m->name, m->name_length); 281 memcpy(name, m->name, m->name_length);
273 return m->name_length; 282
283 return len;
274} 284}
275 285
276/* get name of friendnumber 286/* get name of friendnumber
@@ -480,7 +490,20 @@ int write_cryptpacket_id(Messenger *m, int friendnumber, uint8_t packet_id, uint
480 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);
481} 491}
482 492
493
494/*Interval in seconds between LAN discovery packet sending*/
495#define LAN_DISCOVERY_INTERVAL 60
496
483#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
484/* run this at startup */ 507/* run this at startup */
485Messenger * initMessenger(void) 508Messenger * initMessenger(void)
486{ 509{
@@ -502,6 +525,8 @@ Messenger * initMessenger(void)
502 friendreq_init(); 525 friendreq_init();
503 LANdiscovery_init(); 526 LANdiscovery_init();
504 527
528 timer_single(&LANdiscovery, 0, LAN_DISCOVERY_INTERVAL);
529
505 return m; 530 return m;
506} 531}
507 532
@@ -660,20 +685,6 @@ void doInbound(Messenger *m)
660 } 685 }
661} 686}
662 687
663/*Interval in seconds between LAN discovery packet sending*/
664#define LAN_DISCOVERY_INTERVAL 60
665
666static uint64_t last_LANdiscovery;
667
668/*Send a LAN discovery packet every LAN_DISCOVERY_INTERVAL seconds*/
669void LANdiscovery(Messenger *m)
670{
671 if (last_LANdiscovery + LAN_DISCOVERY_INTERVAL < unix_time()) {
672 send_LANdiscovery(htons(PORT));
673 last_LANdiscovery = unix_time();
674 }
675}
676
677 688
678/* 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. */
679void doMessenger(Messenger *m) 690void doMessenger(Messenger *m)
@@ -685,7 +696,8 @@ void doMessenger(Messenger *m)
685 doNetCrypto(); 696 doNetCrypto();
686 doInbound(m); 697 doInbound(m);
687 doFriends(m); 698 doFriends(m);
688 LANdiscovery(m); 699
700 timer_poll();
689} 701}
690 702
691/* returns the size of the messenger data (for saving) */ 703/* returns the size of the messenger data (for saving) */
diff --git a/core/Messenger.h b/core/Messenger.h
index fa69d104..aa9611a4 100644
--- a/core/Messenger.h
+++ b/core/Messenger.h
@@ -196,10 +196,18 @@ int m_sendaction(Messenger *m, int friendnumber, uint8_t *action, uint32_t lengt
196 return -1 if failure */ 196 return -1 if failure */
197int setname(Messenger *m, uint8_t *name, uint16_t length); 197int setname(Messenger *m, uint8_t *name, uint16_t length);
198 198
199/* get our nickname 199/**
200 put it in name 200 * @brief Get your nickname.
201 return the length of the name*/ 201 *
202uint16_t getself_name(Messenger *m, uint8_t *name); 202 * @param[in] m The messanger context to use.
203 *
204 * @param[inout] name Pointer to a string for the name.
205 *
206 * @param[in] nlen The length of the string buffer.
207 *
208 * @return Return the length of the name, 0 on error.
209 */
210uint16_t getself_name(Messenger *m, uint8_t *name, uint16_t nlen);
203 211
204/* get name of friendnumber 212/* get name of friendnumber
205 put it in name 213 put it in name
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