/** event.c * * Copyright (C) 2013 Tox project All Rights Reserved. * * This file is part of Tox. * * Tox is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Tox is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Tox. If not, see . * * * Report bugs/suggestions to me ( mannol ) at either #tox-dev @ freenode.net:6667 or * my email: eniz_vukovic@hotmail.com */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include #include "event.h" #include "util.h" #define _GNU_SOURCE #include #include #include #include #include #define RUN_IN_THREAD(func, args) { pthread_t _tid; \ pthread_create(&_tid, NULL, func, args); assert( pthread_detach(_tid) == 0 ); } #define LOCK(event_handler) pthread_mutex_lock (&event_handler->mutex) #define UNLOCK(event_handler) pthread_mutex_unlock(&event_handler->mutex) #define FREQUENCY 10000 #define inline__ inline __attribute__((always_inline)) typedef struct _EventContainer { void* (*func)(void*); void* func_args; unsigned timeout; long long id; } EventContainer; typedef struct _EventHandler { EventContainer* timed_events; size_t timed_events_count; int running; pthread_mutex_t mutex; } EventHandler; int throw_event( void* (func)(void*), void* arg ); int reset_timer_event ( int id, uint32_t timeout ); int throw_timer_event ( void* (func)(void*), void* arg, unsigned timeout); int cancel_timer_event ( int id ); int execute_timer_event ( int id ); struct _Event event = { throw_event, /* reset_timer_event */ NULL, throw_timer_event, cancel_timer_event, /*execute_timer_event*/ NULL }; /* * Random functions used by this file */ void clear_events (EventContainer** event_container, size_t* counter) { free(*event_container ); *event_container = NULL; *counter = 0; } int pop_id ( EventContainer** event_container, size_t* counter, int id ) { if ( !*event_container || !*counter || !id ) return -1; EventContainer* _it = *event_container; int i; for ( i = *counter; i; -- i ){ if ( _it->id == id ) { /* Hit! */ break; } ++_it; } if ( i ) { for ( ; i; -- i ){ *_it = *(_it + 1); ++_it; } -- (*counter ); *event_container = realloc(*event_container, sizeof(EventContainer) * (*counter )); /* resize */ return 0; } /* not found here */ return -1; } void push_event ( EventContainer** container, size_t* counter, void* (func)(void*), void* arg ) { (*container ) = realloc((*container ), sizeof(EventContainer) * ((*counter ) + 1)); assert((*container ) != NULL); (*container )[*counter].func = func; (*container )[*counter].func_args = arg; (*container )[*counter].timeout = 0; (*container )[*counter].id = 0; (*counter )++; } void reorder_events ( size_t counter, EventContainer* container, unsigned timeout ) { if ( counter > 1 ) { int i = counter - 1; /* start from behind excluding last added member */ EventContainer* _it = &container[i - 1]; EventContainer _last_added = container[i]; for ( ; i; --i ) { if ( _it->timeout > timeout ){ *(_it + 1) = *_it; *_it = _last_added; -- _it; } } } } /* ============================================= */ /* main poll for event execution */ void* event_poll( void* arg ) { EventHandler* _event_handler = arg; while ( _event_handler->running ) { LOCK( _event_handler ); if ( _event_handler->timed_events ){ uint32_t _time = ((uint32_t)(current_time() / 1000)); if ( _event_handler->timed_events[0].timeout < _time ) { RUN_IN_THREAD ( _event_handler->timed_events[0].func, _event_handler->timed_events[0].func_args ); pop_id(&_event_handler->timed_events, &_event_handler->timed_events_count, _event_handler->timed_events[0].id); } } UNLOCK( _event_handler ); usleep(FREQUENCY); } LOCK( _event_handler ); clear_events(&_event_handler->timed_events, &_event_handler->timed_events_count); UNLOCK( _event_handler ); _event_handler->running = -1; pthread_exit(NULL); } int throw_event( void* (func)(void*), void* arg ) { pthread_t _tid; int _rc = pthread_create(&_tid, NULL, func, arg ); return (0 != _rc ) ? _rc : pthread_detach(_tid); } EventHandler event_handler; /* Place and order array of timers */ int throw_timer_event ( void* (func)(void*), void* arg, unsigned timeout) { static int _unique_id = 1; push_event(&event_handler.timed_events, &(event_handler.timed_events_count), func, arg ); size_t _counter = event_handler.timed_events_count; event_handler.timed_events[_counter - 1].timeout = timeout + ((uint32_t)(current_time() / 1000)); event_handler.timed_events[_counter - 1].id = _unique_id; ++_unique_id; /* reorder */ reorder_events(_counter, event_handler.timed_events, timeout ); return _unique_id - 1; } int execute_timer_event ( int id ) { int _status; LOCK((&event_handler)); EventContainer* _it = event_handler.timed_events; int _i = event_handler.timed_events_count; /* Find it and execute */ for ( ; _i; _i-- ) { if ( _it->id == id ) { RUN_IN_THREAD ( _it->func, _it->func_args ); break; } ++_it; } /* Now remove it from the queue */ if ( _i ) { for ( ; _i; -- _i ){ *_it = *(_it + 1); ++_it; } -- event_handler.timed_events_count; event_handler.timed_events = realloc (event_handler.timed_events, sizeof(EventContainer) * event_handler.timed_events_count); /* resize */ _status = 0; } else _status = -1; UNLOCK((&event_handler)); return _status; } int reset_timer_event ( int id, uint32_t timeout ) { int _status; LOCK((&event_handler)); EventContainer* _it = event_handler.timed_events; int _i = event_handler.timed_events_count; /* Find it and change */ for ( ; _i; _i-- ) { if ( _it->id == id ) { _it->timeout = timeout + ((uint32_t)(current_time() / 1000)); break; } ++_it; } _status = _i ? -1 : 0; UNLOCK((&event_handler)); return _status; } /* Remove timer from array */ inline__ int cancel_timer_event ( int id ) { return pop_id (&event_handler.timed_events, &event_handler.timed_events_count, id ); } /* Initialization and termination of event polls * This will be run at the beginning and the end of the program execution. * I think that's the best way to do it. */ void __attribute__((constructor)) init_event_poll () { event_handler.timed_events = NULL; event_handler.timed_events_count = 0; event_handler.running = 1; pthread_mutex_init(&event_handler.mutex, NULL); RUN_IN_THREAD(event_poll, &event_handler); } void __attribute__((destructor)) terminate_event_poll() { /* Exit thread */ event_handler.running = 0; /* Keep the global until thread exits */ while (event_handler.running > -1) { event_handler.running; usleep(FREQUENCY*2); } pthread_mutex_destroy( &event_handler.mutex ); }