Add a better support of timers
authorDaniel Zaoui <daniel.zaoui@yahoo.com>
Mon, 8 May 2017 19:26:44 +0000 (22:26 +0300)
committerDaniel Zaoui <daniel.zaoui@yahoo.com>
Mon, 5 Jun 2017 05:55:36 +0000 (08:55 +0300)
Now, the timers are handled in one dedicated thread. Multiple timers are
supported.

src/Makefile_Eina.am
src/lib/eina/eina_debug.c
src/lib/eina/eina_debug.h
src/lib/eina/eina_debug_private.h
src/lib/eina/eina_debug_timer.c [new file with mode: 0644]

index 60981fb..0b0bea1 100644 (file)
@@ -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 \
index b2548e3..a8f7264 100644 (file)
@@ -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
-
-        ret = epoll_wait(epfd, events, MAX_EVENTS, timeout);
+        int size;
+        unsigned char *buffer;
 
-        // 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);
index bda8a64..52305f3 100644 (file)
@@ -108,6 +108,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
  *
  * Header of Eina Debug packet
@@ -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_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 Eina_Bool eina_debug_timer_add(unsigned int timeout_ms, Eina_Debug_Timer_Cb cb, void *data);
+EAPI void eina_debug_timer_del(Eina_Debug_Timer *timer);
 
 EAPI int eina_debug_thread_id_get(void);
 
index 2f733b5..56baff1 100644 (file)
@@ -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 (file)
index 0000000..a43427f
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+# ifndef _GNU_SOURCE
+#  define _GNU_SOURCE 1
+# endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pthread.h>
+#include <signal.h>
+#include <time.h>
+#include <fcntl.h>
+
+#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;
+}
+