Added new SILC_STATUS_ERR_TIMEDOUT status type.
[silc.git] / lib / silcutil / unix / silcunixschedule.c
index bf12f7b21ac387a0f3c4383016fead9b060f15d6..846cfa4bf68e2aac91751e0e83253dbeeca5e556 100644 (file)
@@ -65,12 +65,23 @@ int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count,
   return ret;
 }
 
+#define SIGNAL_COUNT 32
+
+typedef struct {
+  SilcUInt32 signal;
+  SilcTaskCallback callback;
+  void *context;
+  bool call;
+} SilcUnixSignal;
+
 /* Internal context. */
 typedef struct {
+  void *app_context;
   int wakeup_pipe[2];
   SilcTask wakeup_task;
   sigset_t signals;
   sigset_t signals_blocked;
+  SilcUnixSignal signal_call[SIGNAL_COUNT];
 } *SilcUnixScheduler;
 
 #ifdef SILC_THREADS
@@ -90,7 +101,8 @@ SILC_TASK_CALLBACK(silc_schedule_wakeup_cb)
    the scheduler needs to be wakenup when tasks are added or removed from
    the task queues.  Returns context to the platform specific scheduler. */
 
-void *silc_schedule_internal_init(SilcSchedule schedule)
+void *silc_schedule_internal_init(SilcSchedule schedule,
+                                 void *app_context)
 {
   SilcUnixScheduler internal;
 
@@ -102,6 +114,7 @@ void *silc_schedule_internal_init(SilcSchedule schedule)
 
 #ifdef SILC_THREADS
   if (pipe(internal->wakeup_pipe)) {
+    SILC_LOG_ERROR(("pipe() fails: %s", strerror(errno)));
     silc_free(internal);
     return NULL;
   }
@@ -112,6 +125,7 @@ void *silc_schedule_internal_init(SilcSchedule schedule)
                           0, 0, SILC_TASK_FD, 
                           SILC_TASK_PRI_NORMAL);
   if (!internal->wakeup_task) {
+    SILC_LOG_ERROR(("Could not add a wakeup task, threads won't work"));
     close(internal->wakeup_pipe[0]);
     close(internal->wakeup_pipe[1]);
     silc_free(internal);
@@ -119,6 +133,8 @@ void *silc_schedule_internal_init(SilcSchedule schedule)
   }
 #endif
 
+  internal->app_context = app_context;
+
   return (void *)internal;
 }
 
@@ -154,24 +170,116 @@ void silc_schedule_internal_wakeup(void *context)
 }
 
 void silc_schedule_internal_signal_register(void *context,
-                                           SilcUInt32 signal)
+                                           SilcUInt32 signal,
+                                            SilcTaskCallback callback,
+                                            void *callback_context)
 {
   SilcUnixScheduler internal = (SilcUnixScheduler)context;
+  int i;
+
+  if (!internal)
+    return;
+
+  sigprocmask(SIG_BLOCK, &internal->signals, &internal->signals_blocked);
+
+  for (i = 0; i < SIGNAL_COUNT; i++) {
+    if (!internal->signal_call[i].signal) {
+      internal->signal_call[i].signal = signal;
+      internal->signal_call[i].callback = callback;
+      internal->signal_call[i].context = callback_context;
+      internal->signal_call[i].call = FALSE;
+      break;
+    }
+  }
+
+  sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
   sigaddset(&internal->signals, signal);
 }
 
 void silc_schedule_internal_signal_unregister(void *context,
-                                             SilcUInt32 signal)
+                                             SilcUInt32 signal,
+                                              SilcTaskCallback callback,
+                                              void *callback_context)
 {
   SilcUnixScheduler internal = (SilcUnixScheduler)context;
+  int i;
+
+  if (!internal)
+    return;
+
+  sigprocmask(SIG_BLOCK, &internal->signals, &internal->signals_blocked);
+
+  for (i = 0; i < SIGNAL_COUNT; i++) {
+    if (internal->signal_call[i].signal == signal &&
+       internal->signal_call[i].callback == callback &&
+       internal->signal_call[i].context == callback_context) {
+      internal->signal_call[i].signal = 0;
+      internal->signal_call[i].callback = NULL;
+      internal->signal_call[i].context = NULL;
+      internal->signal_call[i].call = FALSE;
+    }
+  }
+
+  sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
   sigdelset(&internal->signals, signal);
 }
 
+/* Mark signal to be called later. */
+
+void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal)
+{
+  SilcUnixScheduler internal = (SilcUnixScheduler)context;
+  int i;
+
+  if (!internal)
+    return;
+
+  sigprocmask(SIG_BLOCK, &internal->signals, &internal->signals_blocked);
+
+  for (i = 0; i < SIGNAL_COUNT; i++) {
+    if (internal->signal_call[i].signal == signal)
+      internal->signal_call[i].call = TRUE;
+  }
+
+  sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
+}
+                                        
+/* Call all signals */
+
+void silc_schedule_internal_signals_call(void *context,
+                                         SilcSchedule schedule)
+{
+  SilcUnixScheduler internal = (SilcUnixScheduler)context;
+  int i;
+
+  if (!internal)
+    return;
+
+  sigprocmask(SIG_BLOCK, &internal->signals, &internal->signals_blocked);
+
+  for (i = 0; i < SIGNAL_COUNT; i++) {
+    if (internal->signal_call[i].call &&
+        internal->signal_call[i].callback) {
+      internal->signal_call[i].callback(schedule, internal->app_context,
+                                       SILC_TASK_INTERRUPT,
+                                       internal->signal_call[i].signal,
+                                       internal->signal_call[i].context);
+      internal->signal_call[i].call = FALSE;
+    }
+  }
+
+  sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
+}
+
 /* Block registered signals in scheduler. */
 
 void silc_schedule_internal_signals_block(void *context)
 {
   SilcUnixScheduler internal = (SilcUnixScheduler)context;
+
+  if (!internal)
+    return;
+
   sigprocmask(SIG_BLOCK, &internal->signals, &internal->signals_blocked);
 }
 
@@ -180,5 +288,9 @@ void silc_schedule_internal_signals_block(void *context)
 void silc_schedule_internal_signals_unblock(void *context)
 {
   SilcUnixScheduler internal = (SilcUnixScheduler)context;
+
+  if (!internal)
+    return;
+
   sigprocmask(SIG_SETMASK, &internal->signals_blocked, NULL);
 }