Added SILC_UNIX define for unix systems.
[silc.git] / lib / silcutil / silcschedule.c
index 884091838213d1b0f92dbf7600b0fcb66e33780a..cb4a9693c5f23d829b6fc72a07fba25905a90dde 100644 (file)
 /* Forward declarations */
 typedef struct SilcTaskQueueStruct *SilcTaskQueue;
 
-/* System specific routines. Implemented under unix/ and win32/. */
+/* System specific routines. Implemented under unix/, win32/ and such. */
 
 /* System specific select(). Returns same values as normal select(). */
-int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count, struct timeval *timeout);
+int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count, 
+               struct timeval *timeout);
 
-/* Initializes the wakeup of the scheduler. In multi-threaded environment
+/* Initializes the platform specific scheduler.  This for example initializes
+   the wakeup mechanism of the scheduler.  In multi-threaded environment
    the scheduler needs to be wakenup when tasks are added or removed from
-   the task queues. This will initialize the wakeup for the scheduler.
-   Any tasks that needs to be registered must be registered to the `queue'.
-   It is guaranteed that the scheduler will automatically free any
-   registered tasks in this queue. This is system specific routine. */
-void *silc_schedule_wakeup_init(SilcSchedule schedule);
+   the task queues.  Returns context to the platform specific scheduler. */
+void *silc_schedule_internal_init(SilcSchedule schedule);
 
-/* Uninitializes the system specific wakeup. */
-void silc_schedule_wakeup_uninit(void *context);
+/* Uninitializes the platform specific scheduler context. */
+void silc_schedule_internal_uninit(void *context);
 
 /* Wakes up the scheduler. This is platform specific routine */
-void silc_schedule_wakeup_internal(void *context);
+void silc_schedule_internal_wakeup(void *context);
 
+/* Register signal */
+void silc_schedule_internal_signal_register(void *context,
+                                           SilcUInt32 signal);
+
+/* Unregister signal */
+void silc_schedule_internal_signal_unregister(void *context,
+                                             SilcUInt32 signal);
+
+/* Block registered signals in scheduler. */
+void silc_schedule_internal_signals_block(void *context);
+
+/* Unblock registered signals in schedule. */
+void silc_schedule_internal_signals_unblock(void *context);
 
 /* Internal task management routines. */
 
@@ -64,11 +76,25 @@ static void silc_task_del_by_callback(SilcTaskQueue queue,
 static void silc_task_del_by_fd(SilcTaskQueue queue, SilcUInt32 fd);
 
 /* Returns the task queue by task type */
-#define SILC_SCHEDULE_GET_QUEUE(type)                                  \
-  (type == SILC_TASK_FD ? schedule->fd_queue :                         \
-   type == SILC_TASK_TIMEOUT ? schedule->timeout_queue :               \
+#define SILC_SCHEDULE_GET_QUEUE(type)                          \
+  (type == SILC_TASK_FD ? schedule->fd_queue :                 \
+   type == SILC_TASK_TIMEOUT ? schedule->timeout_queue :       \
    schedule->generic_queue)
 
+/* Locks. These also blocks signals that we care about and thus guarantee
+   that while we are in scheduler no signals can happen.  This way we can
+   synchronise signals with SILC Scheduler. */
+#define SILC_SCHEDULE_LOCK(schedule)                           \
+do {                                                           \
+  silc_schedule_internal_signals_block(schedule->internal);    \
+  silc_mutex_lock(schedule->lock);                             \
+} while (0)
+#define SILC_SCHEDULE_UNLOCK(schedule)                         \
+do {                                                           \
+  silc_mutex_unlock(schedule->lock);                           \
+  silc_schedule_internal_signals_unblock(schedule->internal);  \
+} while (0)
+
 /* SILC Task object. Represents one task in the scheduler. */
 struct SilcTaskStruct {
   SilcUInt32 fd;
@@ -156,11 +182,9 @@ struct SilcTaskQueueStruct {
        File descriptor sets for select(). These are automatically managed
        by the scheduler and should not be touched otherwise.
 
-   void *wakeup
+   void *internal
 
-       System specific wakeup context. On multi-threaded environments the
-       scheduler needs to be wakenup (in the thread) when tasks are added
-       or removed. This is initialized by silc_schedule_wakeup_init.
+       System specific scheduler context.
 
    SILC_MUTEX_DEFINE(lock)
   
@@ -176,7 +200,7 @@ struct SilcScheduleStruct {
   SilcUInt32 last_fd;
   struct timeval *timeout;
   bool valid;
-  void *wakeup;
+  void *internal;
   SILC_MUTEX_DEFINE(lock);
   bool is_locked;
 };
@@ -212,8 +236,11 @@ SilcSchedule silc_schedule_init(int max_tasks)
   /* Allocate scheduler lock */
   silc_mutex_alloc(&schedule->lock);
 
-  /* Initialize the wakeup, for multi-threads support */
-  schedule->wakeup = silc_schedule_wakeup_init(schedule);
+  /* Initialize the platform specific scheduler. */
+  schedule->internal = silc_schedule_internal_init(schedule);
+#ifdef SILC_UNIX
+  silc_schedule_signal_register(schedule, SIGALRM);
+#endif
 
   return schedule;
 }
@@ -242,8 +269,8 @@ bool silc_schedule_uninit(SilcSchedule schedule)
 
   silc_free(schedule->fd_list);
 
-  /* Uninit the wakeup */
-  silc_schedule_wakeup_uninit(schedule->wakeup);
+  /* Uninit the platform specific scheduler. */
+  silc_schedule_internal_uninit(schedule->internal);
 
   silc_mutex_free(schedule->lock);
 
@@ -254,13 +281,13 @@ bool silc_schedule_uninit(SilcSchedule schedule)
 
 bool silc_schedule_reinit(SilcSchedule schedule, int max_tasks)
 {
-  silc_mutex_lock(schedule->lock);
+  SILC_SCHEDULE_LOCK(schedule);
   if (schedule->max_fd <= max_tasks)
     return FALSE;
   schedule->fd_list = silc_realloc(schedule->fd_list, 
                                   (sizeof(*schedule->fd_list) * max_tasks));
   schedule->max_fd = max_tasks;
-  silc_mutex_unlock(schedule->lock);
+  SILC_SCHEDULE_UNLOCK(schedule);
   return TRUE;
 }
 
@@ -271,7 +298,9 @@ bool silc_schedule_reinit(SilcSchedule schedule, int max_tasks)
 void silc_schedule_stop(SilcSchedule schedule)
 {
   SILC_LOG_DEBUG(("Stopping scheduler"));
+  SILC_SCHEDULE_LOCK(schedule);
   schedule->valid = FALSE;
+  SILC_SCHEDULE_UNLOCK(schedule);
 }
 
 /* Executes nontimeout tasks. It then checks whether any of ther fd tasks
@@ -306,18 +335,18 @@ static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule)
       /* Is the task ready for reading */
       if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
        silc_mutex_unlock(schedule->fd_queue->lock);
-       silc_mutex_unlock(schedule->lock);
+       SILC_SCHEDULE_UNLOCK(schedule);
        task->callback(schedule, SILC_TASK_READ, task->fd, task->context);
-       silc_mutex_lock(schedule->lock);
+       SILC_SCHEDULE_LOCK(schedule);
        silc_mutex_lock(schedule->fd_queue->lock);
       }
 
       /* Is the task ready for writing */
       if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
        silc_mutex_unlock(schedule->fd_queue->lock);
-       silc_mutex_unlock(schedule->lock);
+       SILC_SCHEDULE_UNLOCK(schedule);
        task->callback(schedule, SILC_TASK_WRITE, task->fd, task->context);
-       silc_mutex_lock(schedule->lock);
+       SILC_SCHEDULE_LOCK(schedule);
        silc_mutex_lock(schedule->fd_queue->lock);
       }
 
@@ -345,18 +374,18 @@ static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule)
        /* Is the task ready for reading */                             
        if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
          silc_mutex_unlock(schedule->generic_queue->lock);
-         silc_mutex_unlock(schedule->lock);
+         SILC_SCHEDULE_UNLOCK(schedule);
          task->callback(schedule, SILC_TASK_READ, fd, task->context);
-         silc_mutex_lock(schedule->lock);
+         SILC_SCHEDULE_LOCK(schedule);
          silc_mutex_lock(schedule->generic_queue->lock);
        }
 
        /* Is the task ready for writing */                             
        if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
          silc_mutex_unlock(schedule->generic_queue->lock);
-         silc_mutex_unlock(schedule->lock);
+         SILC_SCHEDULE_UNLOCK(schedule);
          task->callback(schedule, SILC_TASK_WRITE, fd, task->context);
-         silc_mutex_lock(schedule->lock);
+         SILC_SCHEDULE_LOCK(schedule);
          silc_mutex_lock(schedule->generic_queue->lock);
        }
 
@@ -414,9 +443,9 @@ static void silc_schedule_dispatch_timeout(SilcSchedule schedule)
       if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
         if (task->valid) {
          silc_mutex_unlock(queue->lock);
-         silc_mutex_unlock(schedule->lock);
+         SILC_SCHEDULE_UNLOCK(schedule);
          task->callback(schedule, SILC_TASK_EXPIRE, task->fd, task->context);
-         silc_mutex_lock(schedule->lock);
+         SILC_SCHEDULE_LOCK(schedule);
          silc_mutex_lock(queue->lock);
        }
 
@@ -518,7 +547,7 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
   SILC_LOG_DEBUG(("In scheduler loop"));
 
   if (!schedule->is_locked)
-    silc_mutex_lock(schedule->lock);
+    SILC_SCHEDULE_LOCK(schedule);
 
   /* If the task queues aren't initialized or we aren't valid anymore
      we will return */
@@ -526,7 +555,7 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
        && !schedule->generic_queue) || schedule->valid == FALSE) {
     SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
     if (!schedule->is_locked)
-      silc_mutex_unlock(schedule->lock);
+      SILC_SCHEDULE_UNLOCK(schedule);
     return FALSE;
   }
 
@@ -542,7 +571,7 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
     schedule->timeout = &timeout;
   }
 
-  silc_mutex_unlock(schedule->lock);
+  SILC_SCHEDULE_UNLOCK(schedule);
 
   /* This is the main select(). The program blocks here until some
      of the selected file descriptors change status or the selected
@@ -551,7 +580,7 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
   ret = silc_select(schedule->fd_list, schedule->last_fd + 1, 
                    schedule->timeout);
 
-  silc_mutex_lock(schedule->lock);
+  SILC_SCHEDULE_LOCK(schedule);
 
   switch (ret) {
   case -1:
@@ -574,7 +603,7 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
   }
 
   if (!schedule->is_locked)
-    silc_mutex_unlock(schedule->lock);
+    SILC_SCHEDULE_UNLOCK(schedule);
 
   return TRUE;
 }
@@ -592,14 +621,14 @@ void silc_schedule(SilcSchedule schedule)
     return;
   }
 
-  silc_mutex_lock(schedule->lock);
+  SILC_SCHEDULE_LOCK(schedule);
   schedule->is_locked = TRUE;
 
   /* Start the scheduler loop */
   while (silc_schedule_one(schedule, -1)) 
     ;
 
-  silc_mutex_unlock(schedule->lock);
+  SILC_SCHEDULE_UNLOCK(schedule);
 }
 
 /* Wakes up the scheduler. This is used only in multi-threaded
@@ -613,9 +642,9 @@ void silc_schedule_wakeup(SilcSchedule schedule)
 {
 #ifdef SILC_THREADS
   SILC_LOG_DEBUG(("Wakeup scheduler"));
-  silc_mutex_lock(schedule->lock);
-  silc_schedule_wakeup_internal(schedule->wakeup);
-  silc_mutex_unlock(schedule->lock);
+  SILC_SCHEDULE_LOCK(schedule);
+  silc_schedule_internal_wakeup(schedule->internal);
+  SILC_SCHEDULE_UNLOCK(schedule);
 #endif
 }
 
@@ -798,7 +827,7 @@ void silc_schedule_set_listen_fd(SilcSchedule schedule,
   int i;
   bool found = FALSE;
 
-  silc_mutex_lock(schedule->lock);
+  SILC_SCHEDULE_LOCK(schedule);
 
   for (i = 0; i < schedule->max_fd; i++)
     if (schedule->fd_list[i].fd == fd) {
@@ -820,7 +849,7 @@ void silc_schedule_set_listen_fd(SilcSchedule schedule,
        break;
       }
 
-  silc_mutex_unlock(schedule->lock);
+  SILC_SCHEDULE_UNLOCK(schedule);
 }
 
 /* Removes a file descriptor from listen list. */
@@ -829,7 +858,7 @@ void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
 {
   int i;
 
-  silc_mutex_lock(schedule->lock);
+  SILC_SCHEDULE_LOCK(schedule);
 
   SILC_LOG_DEBUG(("Unset listen fd %d", fd));
 
@@ -842,7 +871,21 @@ void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
       break;
     }
 
-  silc_mutex_unlock(schedule->lock);
+  SILC_SCHEDULE_UNLOCK(schedule);
+}
+
+/* Register a new signal */
+
+void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal)
+{
+  silc_schedule_internal_signal_register(schedule->internal, signal);
+}
+
+/* Unregister a new signal */
+
+void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal)
+{
+  silc_schedule_internal_signal_unregister(schedule->internal, signal);
 }
 
 /* Allocates a newtask task queue into the scheduler */