summaryrefslogtreecommitdiff
path: root/toxav/event.c
diff options
context:
space:
mode:
Diffstat (limited to 'toxav/event.c')
-rw-r--r--toxav/event.c384
1 files changed, 384 insertions, 0 deletions
diff --git a/toxav/event.c b/toxav/event.c
new file mode 100644
index 00000000..f79fd60e
--- /dev/null
+++ b/toxav/event.c
@@ -0,0 +1,384 @@
1/** event.c
2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved.
4 *
5 * This file is part of Tox.
6 *
7 * Tox is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Tox is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
19 *
20 *
21 * Report bugs/suggestions at #tox-dev @ freenode.net:6667
22 */
23
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif /* HAVE_CONFIG_H */
28
29#include <stdlib.h>
30#include "event.h"
31
32#include "../toxcore/util.h"
33#include "../toxcore/network.h"
34
35#define _GNU_SOURCE
36
37#include <assert.h>
38#include <unistd.h>
39#include <stddef.h>
40#include <inttypes.h>
41#include <pthread.h>
42#include <stdio.h>
43
44#define RUN_IN_THREAD(func, args) { pthread_t _tid; \
45pthread_create(&_tid, NULL, func, args); assert( pthread_detach(_tid) == 0 ); }
46
47#define LOCK(event_handler) pthread_mutex_lock (&event_handler->mutex)
48#define UNLOCK(event_handler) pthread_mutex_unlock(&event_handler->mutex)
49
50#define FREQUENCY 10000
51
52#define inline__ inline __attribute__((always_inline))
53
54
55typedef struct _EventContainer {
56 void *(*func)(void *);
57 void *func_args;
58 unsigned timeout;
59 long long id;
60
61} EventContainer;
62
63typedef struct _EventHandler {
64 EventContainer *timed_events;
65 size_t timed_events_count;
66
67 int running;
68
69 pthread_mutex_t mutex;
70
71} EventHandler;
72
73int throw_event( void * (func)(void *), void *arg );
74int reset_timer_event ( int id, uint32_t timeout );
75int throw_timer_event ( void * (func)(void *), void *arg, unsigned timeout);
76int cancel_timer_event ( int id );
77int execute_timer_event ( int id );
78
79struct _Event event = {
80 throw_event,
81 /* reset_timer_event */ NULL,
82 throw_timer_event,
83 cancel_timer_event,
84 /*execute_timer_event*/ NULL
85};
86
87/*
88 * Random functions used by this file
89 */
90void clear_events (EventContainer **event_container, size_t *counter)
91{
92 free(*event_container );
93
94 *event_container = NULL;
95 *counter = 0;
96}
97
98int pop_id ( EventContainer **event_container, size_t *counter, int id )
99{
100 if ( !*event_container || !*counter || !id )
101 return -1;
102
103 EventContainer *_it = *event_container;
104 int i;
105
106 for ( i = *counter; i; -- i ) {
107 if ( _it->id == id ) { /* Hit! */
108 break;
109 }
110
111 ++_it;
112 }
113
114 if ( i ) {
115 for ( ; i; -- i ) {
116 *_it = *(_it + 1);
117 ++_it;
118 }
119
120 -- (*counter );
121
122 if ( !(*counter)) { /* Free and set to NULL */
123 free(*event_container);
124 *event_container = NULL;
125 } else {
126 void *_result = realloc(*event_container, sizeof(EventContainer) * (*counter )); /* resize */
127
128
129 if ( _result != NULL ) {
130 *event_container = _result;
131 return 0;
132 } else {
133 /* Not sure what would happen next so abort execution.
134 */
135 fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__);
136 abort();
137 return -1;
138 }
139 }
140 }
141
142 /* not found here */
143
144 return -1;
145}
146
147void push_event ( EventContainer **container, size_t *counter, void * (func)(void *), void *arg )
148{
149 EventContainer *_new = realloc((*container ), sizeof(EventContainer) * ((*counter ) + 1));
150
151 if ( _new == NULL ) {
152 /* Not sure what would happen next so abort execution.
153 * TODO: This could notice the calling function
154 * about realloc failing.
155 */
156 fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__);
157 abort();
158 }
159
160 _new[*counter].func = func;
161 _new[*counter].func_args = arg;
162 _new[*counter].timeout = 0;
163 _new[*counter].id = 0;
164
165 (*container) = _new;
166
167 (*counter )++;
168}
169
170void reorder_events ( size_t counter, EventContainer *container, unsigned timeout )
171{
172 if ( counter > 1 ) {
173
174 int i = counter - 1;
175
176 /* start from behind excluding last added member */
177 EventContainer *_it = &container[i - 1];
178
179 EventContainer _last_added = container[i];
180
181 for ( ; i; --i ) {
182 if ( _it->timeout > timeout ) {
183 *(_it + 1) = *_it;
184 *_it = _last_added;
185 -- _it;
186 }
187 }
188
189 }
190}
191
192/* ============================================= */
193
194/* main poll for event execution */
195void *event_poll( void *arg )
196{
197 EventHandler *_event_handler = arg;
198
199 while ( _event_handler->running ) {
200
201 LOCK( _event_handler );
202
203 if ( _event_handler->timed_events ) {
204
205 uint32_t _time = ((uint32_t)(current_time() / 1000));
206
207 if ( _event_handler->timed_events[0].timeout < _time ) {
208
209 RUN_IN_THREAD ( _event_handler->timed_events[0].func,
210 _event_handler->timed_events[0].func_args );
211
212 pop_id(&_event_handler->timed_events,
213 &_event_handler->timed_events_count,
214 _event_handler->timed_events[0].id);
215
216 }
217
218 }
219
220 UNLOCK( _event_handler );
221
222 usleep(FREQUENCY);
223 }
224
225 LOCK( _event_handler );
226
227 clear_events(&_event_handler->timed_events, &_event_handler->timed_events_count);
228
229 UNLOCK( _event_handler );
230
231 _event_handler->running = -1;
232 pthread_exit(NULL);
233}
234
235int throw_event( void * (func)(void *), void *arg )
236{
237 pthread_t _tid;
238 int _rc =
239 pthread_create(&_tid, NULL, func, arg );
240
241 return (0 != _rc ) ? _rc : pthread_detach(_tid);
242}
243
244EventHandler event_handler;
245
246/* Place and order array of timers */
247int throw_timer_event ( void * (func)(void *), void *arg, unsigned timeout)
248{
249 static int _unique_id = 1;
250
251 push_event(&event_handler.timed_events, &(event_handler.timed_events_count), func, arg );
252
253 size_t _counter = event_handler.timed_events_count;
254
255 event_handler.timed_events[_counter - 1].timeout = timeout + ((uint32_t)(current_time() / 1000));
256 event_handler.timed_events[_counter - 1].id = _unique_id;
257 ++_unique_id;
258
259
260 /* reorder */
261
262 reorder_events(_counter, event_handler.timed_events, timeout );
263
264 return _unique_id - 1;
265}
266
267int execute_timer_event ( int id )
268{
269 int _status;
270
271 LOCK((&event_handler));
272 EventContainer *_it = event_handler.timed_events;
273
274 int _i = event_handler.timed_events_count;
275
276 /* Find it and execute */
277 for ( ; _i; _i-- ) {
278 if ( _it->id == id ) {
279 RUN_IN_THREAD ( _it->func, _it->func_args );
280 break;
281 }
282
283 ++_it;
284 }
285
286 /* Now remove it from the queue */
287
288 if ( _i ) {
289 for ( ; _i; -- _i ) {
290 *_it = *(_it + 1);
291 ++_it;
292 }
293
294 -- event_handler.timed_events_count;
295
296 if ( !event_handler.timed_events_count ) { /* Free and set to null */
297 free(event_handler.timed_events);
298 event_handler.timed_events = NULL;
299 } else {
300 void *_result = realloc(event_handler.timed_events,
301 sizeof(EventContainer) * event_handler.timed_events_count); /* resize */
302
303 if ( _result != NULL ) {
304 event_handler.timed_events = _result;
305 } else {
306 /* Not sure what would happen next so abort execution.
307 */
308 fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__);
309 abort();
310 return -1;
311 }
312 }
313
314 _status = 0;
315
316 } else _status = -1;
317
318 UNLOCK((&event_handler));
319
320 return _status;
321}
322
323int reset_timer_event ( int id, uint32_t timeout )
324{
325 int _status;
326
327 LOCK((&event_handler));
328
329 EventContainer *_it = event_handler.timed_events;
330
331 int _i = event_handler.timed_events_count;
332
333 /* Find it and change */
334 for ( ; _i; _i-- ) {
335 if ( _it->id == id ) {
336 _it->timeout = timeout + ((uint32_t)(current_time() / 1000));
337 break;
338 }
339
340 ++_it;
341 }
342
343 _status = _i ? -1 : 0;
344
345 UNLOCK((&event_handler));
346
347 return _status;
348}
349
350/* Remove timer from array */
351inline__ int cancel_timer_event ( int id )
352{
353 return pop_id (&event_handler.timed_events, &event_handler.timed_events_count, id );
354}
355
356
357/* Initialization and termination of event polls
358 * This will be run at the beginning and the end of the program execution.
359 * I think that's the best way to do it.
360 */
361
362void __attribute__((constructor)) init_event_poll ()
363{
364 event_handler.timed_events = NULL;
365 event_handler.timed_events_count = 0;
366
367 event_handler.running = 1;
368
369 pthread_mutex_init(&event_handler.mutex, NULL);
370
371 RUN_IN_THREAD(event_poll, &event_handler);
372}
373
374/* NOTE: Do we need this? */
375void __attribute__((destructor)) terminate_event_poll()
376{
377 /* Exit thread */
378 event_handler.running = 0;
379
380 /* Give it enought time to exit */
381 usleep(FREQUENCY * 2);
382
383 pthread_mutex_destroy( &event_handler.mutex );
384} \ No newline at end of file