From f5cbb61bf4d2188dda8784f5ceb25a9cad4eadc8 Mon Sep 17 00:00:00 2001 From: Daniel Zaoui Date: Mon, 8 May 2017 22:26:44 +0300 Subject: [PATCH] Add a better support of timers Now, the timers are handled in one dedicated thread. Multiple timers are supported. --- src/Makefile_Eina.am | 1 + src/lib/eina/eina_debug.c | 96 ++++----------- src/lib/eina/eina_debug.h | 21 +++- src/lib/eina/eina_debug_private.h | 3 + src/lib/eina/eina_debug_timer.c | 198 ++++++++++++++++++++++++++++++ 5 files changed, 242 insertions(+), 77 deletions(-) create mode 100644 src/lib/eina/eina_debug_timer.c diff --git a/src/Makefile_Eina.am b/src/Makefile_Eina.am index 60981fb541..0b0bea1e59 100644 --- a/src/Makefile_Eina.am +++ b/src/Makefile_Eina.am @@ -126,6 +126,7 @@ lib/eina/eina_debug_bt_file.c \ lib/eina/eina_debug_chunk.c \ lib/eina/eina_debug_thread.c \ lib/eina/eina_debug_cpu.c \ +lib/eina/eina_debug_timer.c \ lib/eina/eina_error.c \ lib/eina/eina_evlog.c \ lib/eina/eina_file_common.h \ diff --git a/src/lib/eina/eina_debug.c b/src/lib/eina/eina_debug.c index b2548e35ab..a8f72642c0 100644 --- a/src/lib/eina/eina_debug.c +++ b/src/lib/eina/eina_debug.c @@ -86,10 +86,6 @@ static Eina_Hash *_modules_hash = NULL; static int _bridge_keep_alive_opcode = EINA_DEBUG_OPCODE_INVALID; -static unsigned int _poll_time = 0; -static Eina_Debug_Timer_Cb _poll_timer_cb = NULL; -static void *_poll_timer_data = NULL; - static Eina_Semaphore _thread_cmd_ready_sem; typedef void *(*Eina_Debug_Encode_Cb)(const void *buffer, int size, int *ret_size); @@ -767,15 +763,6 @@ eina_debug_shell_remote_connect(const char *cmds_str) #endif } -EAPI Eina_Bool -eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data) -{ - _poll_time = timeout_ms; - _poll_timer_cb = cb; - _poll_timer_data = data; - return EINA_TRUE; -} - // this is a DEDICATED debug thread to monitor the application so it works // even if the mainloop is blocked or the app otherwise deadlocked in some // way. this is an alternative to using external debuggers so we can get @@ -784,17 +771,7 @@ static void * _monitor(void *_data) { #ifndef _WIN32 -#define MAX_EVENTS 4 - int ret; - struct epoll_event event; - struct epoll_event events[MAX_EVENTS]; - int epfd = epoll_create(MAX_EVENTS); - _session = _data; - event.data.fd = _session->fd_in; - event.events = EPOLLIN; - ret = epoll_ctl(epfd, EPOLL_CTL_ADD, _session->fd_in, &event); - if (ret) perror("epoll_ctl/add"); // set a name for this thread for system debugging #ifdef EINA_HAVE_PTHREAD_SETNAME @@ -811,63 +788,34 @@ _monitor(void *_data) // impact the application specifically for (;_session;) { - // if we are in a polling mode then set up a timeout and wait for it - int timeout = _poll_time ? (int)_poll_time : -1; //in milliseconds + int size; + unsigned char *buffer; - ret = epoll_wait(epfd, events, MAX_EVENTS, timeout); - - // if the fd for debug daemon says it's alive, process it - if (ret) + size = _packet_receive(&buffer); + // if not negative - we have a real message + if (size > 0) { - int i; - //check which fd are set/ready for read - for (i = 0; i < ret; i++) + if (EINA_DEBUG_OK != _session->dispatch_cb(_session, buffer)) { - if (events[i].events & EPOLLHUP) - { - _opcodes_unregister_all(_session); - free(_session); - _session = NULL; - } - else if (events[i].events & EPOLLIN) - { - int size; - unsigned char *buffer; - - size = _packet_receive(&buffer); - // if not negative - we have a real message - if (size > 0) - { - if (EINA_DEBUG_OK != _session->dispatch_cb(_session, buffer)) - { - // something we don't understand - e_debug("EINA DEBUG ERROR: Unknown command"); - } - /* Free the buffer only if the default dispatcher is used */ - if (_session->dispatch_cb == eina_debug_dispatch) - free(buffer); - } - else if (size == 0) - { - // May be due to a response from a script line - } - else - { - // major failure on debug daemon control fd - get out of here. - // else goto fail; - close(_session->fd_in); - //TODO if its not main _session we will tell the main_loop - //that it disconneted - } - } + // something we don't understand + e_debug("EINA DEBUG ERROR: Unknown command"); } + /* Free the buffer only if the default dispatcher is used */ + if (_session->dispatch_cb == eina_debug_dispatch) + free(buffer); } +#if 0 + else if (size == 0) + { + // May be due to a response from a script line + } +#endif else { - if (_poll_time && _poll_timer_cb) - { - if (!_poll_timer_cb(_poll_timer_data)) _poll_time = 0; - } + close(_session->fd_in); + _opcodes_unregister_all(_session); + free(_session); + _session = NULL; } } #endif @@ -1169,12 +1117,14 @@ eina_debug_init(void) _signal_init(); _eina_debug_cpu_init(); _eina_debug_bt_init(); + _eina_debug_timer_init(); return EINA_TRUE; } Eina_Bool eina_debug_shutdown(void) { + _eina_debug_timer_shutdown(); _eina_debug_bt_shutdown(); _eina_debug_cpu_shutdown(); eina_semaphore_free(&_thread_cmd_ready_sem); diff --git a/src/lib/eina/eina_debug.h b/src/lib/eina/eina_debug.h index bda8a64bef..52305f3989 100644 --- a/src/lib/eina/eina_debug.h +++ b/src/lib/eina/eina_debug.h @@ -107,6 +107,11 @@ typedef Eina_Debug_Error (*Eina_Debug_Dispatch_Cb)(Eina_Debug_Session *session, */ typedef Eina_Bool (*Eina_Debug_Timer_Cb)(void *); +/** + * @typedef Eina_Debug_Timer + */ +typedef struct _Eina_Debug_Timer Eina_Debug_Timer; + /** * @typedef Eina_Debug_Packet_Header * @@ -275,15 +280,23 @@ EAPI int eina_debug_session_send_to_thread(Eina_Debug_Session *session, int dest /** * @brief Add a timer * - * Needed for polling debug - * * @param timeout_ms timeout in ms * @param cb callback to call when the timeout is reached * @param data user data * - * @return EINA_TRUE on success, EINA_FALSE otherwise + * @return the timer handle, NULL on error */ -EAPI Eina_Bool eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data); +EAPI Eina_Debug_Timer *eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data); + +/** + * @brief Delete a timer + * + * @param timer the timer to delete + * + * If the timer reaches the end and has not be renewed, trying to delete it will lead to a crash, as + * it has already been deleted internally. + */ +EAPI void eina_debug_timer_del(Eina_Debug_Timer *timer); EAPI int eina_debug_thread_id_get(void); diff --git a/src/lib/eina/eina_debug_private.h b/src/lib/eina/eina_debug_private.h index 2f733b5f11..56baff18a0 100644 --- a/src/lib/eina/eina_debug_private.h +++ b/src/lib/eina/eina_debug_private.h @@ -69,5 +69,8 @@ Eina_Bool _eina_debug_cpu_shutdown(void); Eina_Bool _eina_debug_bt_init(void); Eina_Bool _eina_debug_bt_shutdown(void); + +Eina_Bool _eina_debug_timer_init(void); +Eina_Bool _eina_debug_timer_shutdown(void); #endif diff --git a/src/lib/eina/eina_debug_timer.c b/src/lib/eina/eina_debug_timer.c new file mode 100644 index 0000000000..a43427ff02 --- /dev/null +++ b/src/lib/eina/eina_debug_timer.c @@ -0,0 +1,198 @@ +/* EINA - EFL data type library + * Copyright (C) 2017 Carsten Haitzler + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see . + */ + +# ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +# endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eina_debug.h" +#include "eina_debug_private.h" + +static Eina_Spinlock _lock; + +struct _Eina_Debug_Timer +{ + unsigned int rel_time; + unsigned int timeout; + Eina_Debug_Timer_Cb cb; + void *data; +}; + +static Eina_List *_timers = NULL; + +static Eina_Bool _thread_runs = EINA_FALSE; +static Eina_Bool _exit_required = EINA_FALSE; +static pthread_t _thread; + +static int pipeToThread[2]; + +static void +_timer_append(Eina_Debug_Timer *t) +{ + Eina_Debug_Timer *t2; + Eina_List *itr; + unsigned int prev_time = 0; + char c = '\0'; + EINA_LIST_FOREACH(_timers, itr, t2) + { + if (t2->timeout > t->timeout) goto end; + prev_time = t2->timeout; + } + t2 = NULL; +end: + t->rel_time = t->timeout - prev_time; + if (!t2) _timers = eina_list_append(_timers, t); + else _timers = eina_list_prepend_relative(_timers, t, t2); + write(pipeToThread[1], &c, 1); +} + +static void * +_monitor(void *_data EINA_UNUSED) +{ +#ifndef _WIN32 +#define MAX_EVENTS 4 + struct epoll_event event; + struct epoll_event events[MAX_EVENTS]; + int epfd = epoll_create(MAX_EVENTS), ret; + + event.data.fd = pipeToThread[0]; + event.events = EPOLLIN; + ret = epoll_ctl(epfd, EPOLL_CTL_ADD, event.data.fd, &event); + if (ret) perror("epoll_ctl/add"); +#ifdef EINA_HAVE_PTHREAD_SETNAME +# ifndef __linux__ + pthread_set_name_np +# else + pthread_setname_np +# endif + (pthread_self(), "Edbg-tim"); +#endif + for (;!_exit_required;) + { + int timeout = -1; //in milliseconds + eina_spinlock_take(&_lock); + if (_timers) + { + Eina_Debug_Timer *t = eina_list_data_get(_timers); + timeout = t->timeout; + } + eina_spinlock_release(&_lock); + + ret = epoll_wait(epfd, events, MAX_EVENTS, timeout); + if (_exit_required) continue; + + /* Some timer has been add/removed or we need to exit */ + if (ret) + { + char c; + read(pipeToThread[0], &c, 1); + } + else + { + Eina_List *itr, *itr2, *renew = NULL; + Eina_Debug_Timer *t; + eina_spinlock_take(&_lock); + EINA_LIST_FOREACH_SAFE(_timers, itr, itr2, t) + { + if (itr == _timers || t->rel_time == 0) + { + _timers = eina_list_remove(_timers, t); + if (t->cb(t->data)) renew = eina_list_append(renew, t); + else free(t); + } + } + EINA_LIST_FREE(renew, t) _timer_append(t); + eina_spinlock_release(&_lock); + } + } +#endif + _thread_runs = EINA_FALSE; + close(pipeToThread[0]); + close(pipeToThread[1]); + return NULL; +} + +EAPI Eina_Debug_Timer * +eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data) +{ + if (!cb || !timeout_ms) return NULL; + Eina_Debug_Timer *t = calloc(1, sizeof(*t)); + t->cb = cb; + t->data = data; + t->timeout = timeout_ms; + eina_spinlock_take(&_lock); + _timer_append(t); + if (!_thread_runs) + { + int err = pthread_create(&_thread, NULL, _monitor, NULL); + if (err != 0) + { + e_debug("EINA DEBUG ERROR: Can't create debug timer thread!"); + abort(); + } + _thread_runs = EINA_TRUE; + } + eina_spinlock_release(&_lock); + return t; +} + +EAPI void +eina_debug_timer_del(Eina_Debug_Timer *t) +{ + eina_spinlock_take(&_lock); + Eina_List *itr = eina_list_data_find_list(_timers, t); + if (itr) + { + _timers = eina_list_remove_list(_timers, itr); + free(t); + } + eina_spinlock_release(&_lock); +} + +Eina_Bool +_eina_debug_timer_init(void) +{ + eina_spinlock_new(&_lock); + pipe(pipeToThread); + return EINA_TRUE; +} + +Eina_Bool +_eina_debug_timer_shutdown(void) +{ + char c = '\0'; + _exit_required = EINA_TRUE; + write(pipeToThread[1], &c, 1); + eina_spinlock_free(&_lock); + return EINA_TRUE; +} +