Added asynchronous event tasks to SILC Scheduler. Added
authorPekka Riikonen <priikone@silcnet.org>
Sun, 30 Dec 2007 12:47:31 +0000 (12:47 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Sun, 30 Dec 2007 12:47:31 +0000 (12:47 +0000)
concept of parent and child schedulers.

CHANGES.RUNTIME
TODO
lib/silcclient/client.c
lib/silcutil/silcfsm.c
lib/silcutil/silcfsm.h
lib/silcutil/silcschedule.c
lib/silcutil/silcschedule.h
lib/silcutil/silcschedule_i.h
lib/silcutil/tests/test_silcschedule.c
lib/silcutil/unix/silcunixschedule.c

index 05b131c5ba8866fe7d941d242c05f9a6bddaf74b..33f5b70da16ef6a3bb72d82dd7cbe618ad97e6b9 100644 (file)
@@ -1,3 +1,32 @@
+Sun Dec 30 14:35:33 EET 2007  Pekka Riikonen <priikone@silcnet.org>
+
+       * Implemented asynchronous events to SILC Scheduler.  Added
+         silc_schedule_task_add_event, silc_schedule_event_connect,
+         silc_schedule_event_dissconnect, silc_schedule_task_del_event
+         and silc_schedule_event_signal.  Affected files are
+         lib/silcutil/silcschedule.[ch], silcschedule_i.h.
+
+       * Added concept of child and parent scheduler to SILC Scheduler
+         API.  silc_schedule_init now takes optional parent argument.
+         Each child scheduler is still independent, only the event tasks
+         are shared among parent and children.  Affected files are
+         lib/silcutil/silcschedule.[ch].
+
+       * The SILC FSM real thread now adds the created SilcSchedule
+         as the thread's global scheduler.  Affected file is
+         lib/silcutil/silcfsm.[ch].
+
+       * Moved generic string and data hashing and comparison functions
+         from lib/silcutil/silcutil.[ch] to lib/silcutil/silchashtable.[ch]
+         as they are usable by the hash table.  Added case sensitive
+         and insensitive string hashing and comparison funtions.
+         Changed string and data hashing to use Bob Jenkin's one-at-a-time
+         hash function.
+       
+       * Moved SILC_PARAM_* types from silcbuffmt.h to silctypes.h
+         under a generic SilcParam type.  Affected files are
+         lib/silcutil/silcbuffmt.[ch] and lib/silcutil/silctypes.h.
+
 Wed Dec 26 13:10:30 EET 2007  Pekka Riikonen <priikone@silcnet.org>
 
        * Added silc_schedule_[set|get]_global to
diff --git a/TODO b/TODO
index 31f8c01ad9fed1d0aef0042d33b8f931a3147b3d..4c61e9884e6e8aa1ef0a974c01bb558e4fd60fc7 100644 (file)
--- a/TODO
+++ b/TODO
@@ -109,49 +109,10 @@ Runtime library, lib/silcutil/
 
  o The SILC Event signals.  Asynchronous events that can be created,
    connected to and signalled.  Either own event routines or glued into
-   SilcSchedule:
-
-   SilcTask silc_schedule_task_add_event(SilcSchedule schedule,
-                                        const char *event, ...);
-   SilcBool silc_schedule_event_connect(SilcSchedule schedule,
-                                       const char *event,
-                                       SilcTaskCallback event_callback,
-                                       void *context);
-   SilcBool silc_schedule_event_signal(SilcSchedule schedule,
-                                      const char *event, ...);
-
-   Example:
-     silc_schedule_task_add_event(schedule, "connected",
-                                  SILC_PARAM_UI32_INT,
-                                  SILC_PARAM_BUFFER,
-                                  SILC_PARAM_END);
-     silc_schedule_event_connect(schedule, "connected", connected_cb, ctx);
-     silc_schedule_event_signal(schedule, "connected", integer, buf,
-                                 SILC_PARAM_END);
-     SILC_TASK_CALLBACK(connected_cb)
-     {
-       FooCtx ctx = context;
-       va_list args;
-       SilcUInt32 integer;
-       SilcBuffer buf;
-
-       va_start(args, context);
-       integer = va_arg(args, SilcUInt32);
-       buf = va_arg(args, SilcBuffer);
-       va_end(args);
-       ...
-     }
-
-   Problems: Events would be SilcSchedule specific, and would not work on
-   multi-thread/multi-scheduler system.  The events should be copyable
-   between schedulers.  Another problem is the signal delivery.  Do we
-   deliver them synchronously possibly from any thread to any other thread
-   or do we deliver them through the target schedulers.  If we use the
-   schedulers then signalling would be asynchronous (data must be
-   duplicated and later freed) which is not very nice.
+   SilcSchedule. (***DONE)
 
  o If the event signals are added, the SILC_PARAM_* stuff needs to be
-   moved from silcbuffmt.h to silctypes.h or something similar.
+   moved from silcbuffmt.h to silctypes.h or something similar. (***DONE)
 
  o In case the SILC Events are done we shall create a new concept of
    parent and child SilcSchedule's.  When new SilcSchedule is created a
@@ -164,10 +125,7 @@ Runtime library, lib/silcutil/
    would be linked and could be accessed from any of the schedulers.
    It should be possible to retrieve the parent and enumerate all children
    from any of the schedulers.
-
-   SilcSchedule silc_schedule_init(int max_tasks, void *app_context,
-                                  SilcSchedule parent);
-   SilcSchedule silc_schedule_get_parent(SilcSchedule schedule);
+   (***DONE)
 
  o Additional scheduler changes: optimize silc_schedule_wakeup.  Wakeup
    only if the scheduler is actually waiting something.  If it is
@@ -209,25 +167,7 @@ Runtime library, lib/silcutil/
 
  o silc_malloc et. al. to respect --with-alignment.
 
- o Add '%@' format to silc_snprintf functions.  It marks for external
-   rendering function of following type:
-
-     /* Snprintf rendering function.  The `data' is rendered into a string
-        and allocated string is returned.  If NULL is returned the
-        rendering is skipped and ignored.  If the returned string does
-       not fit to the destination buffer it may be truncated. */
-     typedef char *(*SilcSnprintfRender)(void *data);
-
-   It can work like following:
-
-   char *id_renderer(void *data)
-   {
-     char tmp[32];
-     id_to_str(tmp, sizeof(tmp), (SilcID *)data);
-     return strdup(tmp);
-   }
-
-   silc_snprintf(buf, sizeof(buf), "Client ID %@", id_renderer, client_id);
+ o Add '%@' format to silc_snprintf functions.
    (***DONE)
 
  o SILC Tls (Thread-local storage) API to lib/silcutil/silcthread.[ch].
@@ -347,6 +287,10 @@ Crypto Library, lib/silccrypt/
    All PKCS routines should now take callbacks as argument and they should
    be delivered to SilcPKCSObject and SilcPKCSAlgorithm too. (***DONE)
 
+ o The asynchronous functions to perhaps to _async to preserve backwards
+   compatibility with synchronous versions, and make easier to migrate
+   from 1.1 to 1.2.
+
  o Change PKCS Algorithm API to take SilcPKCSAlgorithm as argument to
    encrypt, decrypt, sign and verify functions.  We may need to for exmaple
    check the alg->hash, supported hash functions.  Maybe deliver it also
index 1a925206c31d38124721b7dc6c05ef9404b441c3..c27d055e1e3e230ac2a65ae7bd54d5094761fd15 100644 (file)
@@ -1040,7 +1040,7 @@ SilcBool silc_client_init(SilcClient client, const char *username,
   silc_rng_global_init(client->rng);
 
   /* Initialize the scheduler */
-  client->schedule = silc_schedule_init(0, client, NULL);
+  client->schedule = silc_schedule_init(0, client, NULL, NULL);
   if (!client->schedule)
     return FALSE;
 
index 5e6fe2a5c7e001705c93255a160c7832e9baf3b2..2ded983253aa77906cd439210b69306afe92116c 100644 (file)
@@ -770,12 +770,15 @@ void *silc_fsm_thread(void *context)
   /* We allocate new SilcSchedule for the FSM, as the old SilcSchedule
      cannot be used in this thread.  Application may still use it if it
      wants but we use our own. */
-  fsm->schedule = silc_schedule_init(0, old, silc_schedule_get_stack(old));
+  fsm->schedule = silc_schedule_init(0, old, silc_schedule_get_stack(old), old);
   if (silc_unlikely(!fsm->schedule)) {
     fsm->schedule = old;
     return NULL;
   }
 
+  /* The new scheduler is a global scheduler in this thread */
+  silc_schedule_set_global(fsm->schedule);
+
   /* Start the FSM thread */
   if (silc_unlikely(!silc_schedule_task_add_timeout(fsm->schedule,
                                                    silc_fsm_run, fsm, 0, 0))) {
@@ -787,6 +790,9 @@ void *silc_fsm_thread(void *context)
   /* Run the scheduler */
   silc_schedule(fsm->schedule);
 
+  /* Reset global scheduler */
+  silc_schedule_set_global(NULL);
+
   /* Free resources */
   silc_schedule_uninit(fsm->schedule);
 
index 366edf704406b3cd48033f8d833279f7b1fdda1c..503b8f43eafebff4ad8f81992582c095dca56e66 100644 (file)
@@ -498,8 +498,11 @@ SilcBool silc_fsm_init(SilcFSM fsm,
  *    for the FSM thread. If you need scheduler in the real thread it is
  *    strongly recommended that you use the SilcSchedule that is allocated
  *    for the thread.  You can retrieve the SilcSchedule from the thread
- *    using silc_fsm_get_schedule function.  Note that, the allocated
- *    SilcSchedule will become invalid after the thread finishes.
+ *    using silc_fsm_get_schedule function.  The new scheduler is a child
+ *    scheduler of the original scheduler used with `fsm'.  Note that, the
+ *    allocated SilcSchedule will become invalid after the thread finishes.
+ *    Note also that the scheduler is automatically set as global scheduler
+ *    in that thread by calling silc_schedule_set_global.
  *
  *    If `real_thread' is FALSE the silc_fsm_get_schedule will return
  *    the SilcSchedule that was originally given to silc_fsm_alloc or
index 476d11f1031e3518b39442dab2a7fe31b1242dc8..5550181a7da686d070e2103a6a1d8ac33140e9dd 100644 (file)
 
 /************************** Types and definitions ***************************/
 
+/* Connected event context */
+typedef struct SilcScheduleEventConnectionStruct {
+  SilcSchedule schedule;
+  SilcTaskEventCallback callback;
+  void *context;
+  struct SilcScheduleEventConnectionStruct *next;
+} *SilcScheduleEventConnection;
+
 /* Platform specific implementation */
 extern const SilcScheduleOps schedule_ops;
 
@@ -29,7 +37,7 @@ static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task);
 static void silc_schedule_dispatch_fd(SilcSchedule schedule);
 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
                                           SilcBool dispatch_all);
-
+SILC_TASK_CALLBACK(silc_schedule_event_del_timeout);
 
 /************************ Static utility functions **************************/
 
@@ -196,10 +204,11 @@ static void silc_schedule_select_timeout(SilcSchedule schedule)
 
 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
 {
-  SilcTaskFd ftask;
+  SilcSchedule parent;
 
   if (silc_unlikely(task == SILC_ALL_TASKS)) {
     SilcTask task;
+    SilcEventTask etask;
     SilcHashTableList htl;
     void *fd;
 
@@ -216,19 +225,59 @@ static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
       silc_free(task);
     }
 
+    /* Delete even tasks */
+    parent = silc_schedule_get_parent(schedule);
+    silc_hash_table_list(parent->events, &htl);
+    while (silc_hash_table_get(&htl, NULL, (void *)&etask)) {
+      silc_hash_table_del_by_context(parent->events, etask->event, etask);
+      silc_free(etask->event);
+      silc_free(etask);
+    }
+    silc_hash_table_list_reset(&htl);
     return;
   }
 
-  if (silc_likely(task->type == 1)) {
-    /* Delete from timeout queue */
-    silc_list_del(schedule->timeout_queue, task);
+  switch (task->type) {
+  case SILC_TASK_FD:
+    {
+      /* Delete from fd queue */
+      SilcTaskFd ftask = (SilcTaskFd)task;
+      silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
+    }
+    break;
 
-    /* Put to free list */
-    silc_list_add(schedule->free_tasks, task);
-  } else {
-    /* Delete from fd queue */
-    ftask = (SilcTaskFd)task;
-    silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
+  case SILC_TASK_TIMEOUT:
+    {
+      /* Delete from timeout queue */
+      silc_list_del(schedule->timeout_queue, task);
+
+      /* Put to free list */
+      silc_list_add(schedule->free_tasks, task);
+    }
+    break;
+
+  case SILC_TASK_EVENT:
+    {
+      SilcEventTask etask = (SilcEventTask)task;
+      SilcScheduleEventConnection conn;
+
+      parent = silc_schedule_get_parent(schedule);
+
+      /* Delete event */
+      silc_hash_table_del_by_context(parent->events, etask->event, etask);
+
+      /* Remove all connections */
+      silc_list_start(etask->connections);
+      while ((conn = silc_list_get(etask->connections)))
+       silc_free(conn);
+
+      silc_free(etask->event);
+      silc_free(etask);
+    }
+    break;
+
+  default:
+    break;
   }
 }
 
@@ -313,7 +362,7 @@ void silc_schedule_stats(SilcSchedule schedule)
    context that is delivered to task callbacks. */
 
 SilcSchedule silc_schedule_init(int max_tasks, void *app_context,
-                               SilcStack stack)
+                               SilcStack stack, SilcSchedule parent)
 {
   SilcSchedule schedule;
 
@@ -343,10 +392,15 @@ SilcSchedule silc_schedule_init(int max_tasks, void *app_context,
   silc_list_init(schedule->timeout_queue, struct SilcTaskStruct, next);
   silc_list_init(schedule->free_tasks, struct SilcTaskStruct, next);
 
+  /* Get the parent */
+  if (parent && parent->parent)
+    parent = parent->parent;
+
   schedule->stack = stack;
   schedule->app_context = app_context;
   schedule->valid = TRUE;
   schedule->max_tasks = max_tasks;
+  schedule->parent = parent;
 
   /* Allocate scheduler lock */
   silc_mutex_alloc(&schedule->lock);
@@ -552,6 +606,13 @@ void silc_schedule_wakeup(SilcSchedule schedule)
 #endif
 }
 
+/* Returns parent scheduler */
+
+SilcSchedule silc_schedule_get_parent(SilcSchedule schedule)
+{
+  return schedule->parent ? schedule->parent : schedule;
+}
+
 /* Returns the application specific context that was saved into the
    scheduler in silc_schedule_init function.  The context is also
    returned to application in task callback functions, but this function
@@ -775,6 +836,8 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
 
 SilcBool silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
 {
+  SilcSchedule parent;
+
   if (!schedule) {
     schedule = silc_schedule_get_global();
     SILC_VERIFY(schedule);
@@ -815,20 +878,34 @@ SilcBool silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
                         schedule->notify_context);
     }
 
+    /* Delete even tasks */
+    parent = silc_schedule_get_parent(schedule);
+    silc_hash_table_list(parent->events, &htl);
+    while (silc_hash_table_get(&htl, NULL, (void *)&task))
+      task->valid = FALSE;
+    silc_hash_table_list_reset(&htl);
+
     SILC_SCHEDULE_UNLOCK(schedule);
     return TRUE;
   }
 
-  SILC_LOG_DEBUG(("Unregistering task %p", task));
+  SILC_LOG_DEBUG(("Unregistering task %p, type %d", task, task->type));
   SILC_SCHEDULE_LOCK(schedule);
   task->valid = FALSE;
 
   /* Call notify callback */
-  if (schedule->notify)
-    schedule->notify(schedule, FALSE, task, !task->type, 0, 0, 0, 0,
-                    schedule->notify_context);
+  if (schedule->notify && task->type != SILC_TASK_EVENT)
+    schedule->notify(schedule, FALSE, task, task->type == SILC_TASK_FD,
+                    0, 0, 0, 0, schedule->notify_context);
   SILC_SCHEDULE_UNLOCK(schedule);
 
+  if (task->type == SILC_TASK_EVENT) {
+    /* Schedule removal of deleted event task */
+    parent = silc_schedule_get_parent(schedule);
+    silc_schedule_task_add_timeout(parent, silc_schedule_event_del_timeout,
+                                  task, 0, 1);
+  }
+
   return TRUE;
 }
 
@@ -1140,3 +1217,365 @@ void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
 {
   silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
 }
+
+/*************************** Asynchronous Events ****************************/
+
+/* Add event */
+
+SilcTask silc_schedule_task_add_event(SilcSchedule schedule,
+                                     const char *event, ...)
+{
+  SilcEventTask task;
+  SilcSchedule parent;
+
+  if (!schedule) {
+    schedule = silc_schedule_get_global();
+    SILC_VERIFY(schedule);
+    if (!schedule) {
+      silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+      return NULL;
+    }
+  }
+
+  /* Get parent scheduler */
+  parent = silc_schedule_get_parent(schedule);
+
+  SILC_LOG_DEBUG(("Adding event '%s' to scheduler %p", event, parent));
+
+  SILC_SCHEDULE_LOCK(parent);
+
+  /* Create events hash table if not already done */
+  if (!parent->events) {
+    parent->events = silc_hash_table_alloc(NULL, 3,
+                                          silc_hash_string, NULL,
+                                          silc_hash_string_compare, NULL,
+                                          NULL, NULL, FALSE);
+    if (!parent->events) {
+      SILC_SCHEDULE_UNLOCK(parent);
+      return NULL;
+    }
+  }
+
+  /* Check if this event is added already */
+  if (silc_hash_table_find(parent->events, (void *)event, NULL, NULL)) {
+    SILC_SCHEDULE_UNLOCK(parent);
+    return NULL;
+  }
+
+  /* Add new event */
+  task = silc_calloc(1, sizeof(*task));
+  if (!task) {
+    SILC_SCHEDULE_UNLOCK(parent);
+    return NULL;
+  }
+
+  task->header.type = SILC_TASK_EVENT;
+  task->header.valid = TRUE;
+  task->event = silc_strdup(event);
+  if (!task->event) {
+    SILC_SCHEDULE_UNLOCK(parent);
+    silc_free(task);
+    return NULL;
+  }
+  silc_list_init(task->connections, struct SilcScheduleEventConnectionStruct,
+                next);
+
+  if (!silc_hash_table_add(parent->events, task->event, task)) {
+    SILC_SCHEDULE_UNLOCK(parent);
+    silc_free(task->event);
+    silc_free(task);
+    return NULL;
+  }
+
+  SILC_SCHEDULE_UNLOCK(parent);
+
+  return (SilcTask)task;
+}
+
+/* Connect to event task */
+
+SilcBool silc_schedule_event_connect(SilcSchedule schedule,
+                                    const char *event, SilcTask task,
+                                    SilcTaskEventCallback callback,
+                                    void *context)
+{
+  SilcSchedule parent;
+  SilcScheduleEventConnection conn;
+  SilcEventTask etask;
+
+  if (!schedule) {
+    schedule = silc_schedule_get_global();
+    SILC_VERIFY(schedule);
+    if (!schedule) {
+      silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+      return FALSE;
+    }
+  }
+
+  if (!event && !task) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    return FALSE;
+  }
+
+  if (task && task->type != SILC_TASK_EVENT) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    return FALSE;
+  }
+
+  /* Get parent scheduler */
+  parent = silc_schedule_get_parent(schedule);
+
+  SILC_SCHEDULE_LOCK(parent);
+
+  if (!task) {
+    /* Get the event task */
+    if (!silc_hash_table_find(parent->events, (void *)event, NULL,
+                             (void *)&task)) {
+      SILC_SCHEDULE_UNLOCK(parent);
+      return FALSE;
+    }
+  }
+  etask = (SilcEventTask)task;
+
+  /* See if task is deleted */
+  if (task->valid == FALSE) {
+    SILC_SCHEDULE_UNLOCK(parent);
+    silc_set_errno(SILC_ERR_NOT_VALID);
+    return FALSE;
+  }
+
+  SILC_LOG_DEBUG(("Connect callback %p with context %p to event '%s'",
+                 callback, context, etask->event));
+
+  /* See if already connected */
+  silc_list_start(etask->connections);
+  while ((conn = silc_list_get(etask->connections))) {
+    if (conn->callback == callback && conn->context == context) {
+      SILC_SCHEDULE_UNLOCK(parent);
+      silc_set_errno(SILC_ERR_ALREADY_EXISTS);
+      return FALSE;
+    }
+  }
+
+  conn = silc_calloc(1, sizeof(*conn));
+  if (!conn) {
+    SILC_SCHEDULE_UNLOCK(parent);
+    return FALSE;
+  }
+
+  /* Connect to the event */
+  conn->schedule = schedule;
+  conn->callback = callback;
+  conn->context = context;
+  silc_list_add(etask->connections, conn);
+
+  SILC_SCHEDULE_UNLOCK(parent);
+
+  return TRUE;
+}
+
+/* Disconnect from event */
+
+SilcBool silc_schedule_event_disconnect(SilcSchedule schedule,
+                                       const char *event, SilcTask task,
+                                       SilcTaskEventCallback callback,
+                                       void *context)
+{
+  SilcSchedule parent;
+  SilcScheduleEventConnection conn;
+  SilcEventTask etask;
+
+  if (!schedule) {
+    schedule = silc_schedule_get_global();
+    SILC_VERIFY(schedule);
+    if (!schedule) {
+      silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+      return FALSE;
+    }
+  }
+
+  if (!event && !task) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    return FALSE;
+  }
+
+  if (task && task->type != SILC_TASK_EVENT) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    return FALSE;
+  }
+
+  /* Get parent scheduler */
+  parent = silc_schedule_get_parent(schedule);
+
+  SILC_SCHEDULE_LOCK(parent);
+
+  if (!task) {
+    /* Get the event task */
+    if (!silc_hash_table_find(parent->events, (void *)event, NULL,
+                             (void *)&task)) {
+      SILC_SCHEDULE_UNLOCK(parent);
+      return FALSE;
+    }
+  }
+  etask = (SilcEventTask)task;
+
+  /* See if task is deleted */
+  if (task->valid == FALSE) {
+    SILC_SCHEDULE_UNLOCK(parent);
+    silc_set_errno(SILC_ERR_NOT_VALID);
+    return FALSE;
+  }
+
+  SILC_LOG_DEBUG(("Disconnect callback %p with context %p from event '%s'",
+                 callback, context, etask->event));
+
+  /* Disconnect */
+  silc_list_start(etask->connections);
+  while ((conn = silc_list_get(etask->connections))) {
+    if (conn->callback == callback && conn->context == context) {
+      silc_list_del(etask->connections, conn);
+      silc_free(conn);
+      SILC_SCHEDULE_UNLOCK(parent);
+      return TRUE;
+    }
+  }
+
+  SILC_SCHEDULE_UNLOCK(parent);
+  silc_set_errno(SILC_ERR_NOT_FOUND);
+  return FALSE;
+}
+
+/* Signal event */
+
+SilcBool silc_schedule_event_signal(SilcSchedule schedule, const char *event,
+                                   SilcTask task, ...)
+{
+  SilcSchedule parent;
+  SilcScheduleEventConnection conn;
+  SilcEventTask etask;
+  SilcBool stop;
+  va_list ap, cp;
+
+  if (silc_unlikely(!schedule)) {
+    schedule = silc_schedule_get_global();
+    SILC_VERIFY(schedule);
+    if (!schedule) {
+      silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+      return FALSE;
+    }
+  }
+
+  if (silc_unlikely(!event && !task)) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    return FALSE;
+  }
+
+  if (silc_unlikely(task && task->type != SILC_TASK_EVENT)) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    return FALSE;
+  }
+
+  /* Get parent scheduler */
+  parent = silc_schedule_get_parent(schedule);
+
+  SILC_SCHEDULE_LOCK(parent);
+
+  if (!task) {
+    /* Get the event task */
+    if (!silc_hash_table_find(parent->events, (void *)event, NULL,
+                             (void *)&task)) {
+      SILC_SCHEDULE_UNLOCK(parent);
+      return FALSE;
+    }
+  }
+  etask = (SilcEventTask)task;
+
+  /* See if task is deleted */
+  if (task->valid == FALSE) {
+    SILC_SCHEDULE_UNLOCK(parent);
+    silc_set_errno(SILC_ERR_NOT_VALID);
+    return FALSE;
+  }
+
+  SILC_LOG_DEBUG(("Signal event '%s'", etask->event));
+
+  va_start(ap, task);
+
+  /* Deliver the signal */
+  silc_list_start(etask->connections);
+  while ((conn = silc_list_get(etask->connections))) {
+    SILC_SCHEDULE_UNLOCK(parent);
+
+    silc_va_copy(cp, ap);
+    stop = conn->callback(conn->schedule, conn->schedule->app_context,
+                         task, conn->context, cp);
+    va_end(cp);
+
+    SILC_SCHEDULE_LOCK(parent);
+
+    /* Stop signal if wanted or if the task was deleted */
+    if (!stop || !task->valid)
+      break;
+  }
+
+  va_end(ap);
+
+  SILC_SCHEDULE_UNLOCK(parent);
+
+  return TRUE;
+}
+
+/* Delete event */
+
+SilcBool silc_schedule_task_del_event(SilcSchedule schedule, const char *event)
+{
+  SilcSchedule parent;
+  SilcTask task;
+
+  if (!schedule) {
+    schedule = silc_schedule_get_global();
+    SILC_VERIFY(schedule);
+    if (!schedule) {
+      silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+      return FALSE;
+    }
+  }
+
+  if (!event) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+    return FALSE;
+  }
+
+  /* Get parent scheduler */
+  parent = silc_schedule_get_parent(schedule);
+
+  SILC_SCHEDULE_LOCK(parent);
+
+  /* Get the event task */
+  if (!silc_hash_table_find(parent->events, (void *)event, NULL,
+                           (void *)&task)) {
+    SILC_SCHEDULE_UNLOCK(parent);
+    return FALSE;
+  }
+
+  /* See if already deleted */
+  if (task->valid == FALSE)
+    return TRUE;
+
+  SILC_LOG_DEBUG(("Delete event '%s'", ((SilcEventTask)task)->event));
+
+  SILC_SCHEDULE_UNLOCK(parent);
+
+  silc_schedule_task_del(parent, task);
+
+  return TRUE;
+}
+
+/* Timeout to remove deleted event task */
+
+SILC_TASK_CALLBACK(silc_schedule_event_del_timeout)
+{
+  SILC_SCHEDULE_LOCK(schedule);
+  silc_schedule_task_remove(schedule, context);
+  SILC_SCHEDULE_UNLOCK(schedule);
+}
index af2c8cf3d336877212e1a940736ee5333c1c4fb9..c37ecc6690dad71c522a4a29697ee2dbe5e8c7ae 100644 (file)
@@ -158,6 +158,39 @@ typedef void (*SilcTaskCallback)(SilcSchedule schedule, void *app_context,
                                 SilcTaskEvent type, SilcUInt32 fd,
                                 void *context);
 
+/****f* silcutil/SilcScheduleAPI/SilcTaskEventCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcTaskEventCallback)(SilcSchedule schedule,
+ *                                          void *app_context,
+ *                                          SilcTask task, void *context,
+ *                                          va_list va);
+ *
+ * DESCRIPTION
+ *
+ *    Task callback for event tasks added with silc_schedule_task_add_event.
+ *    The callback of this type is called when an event task is signalled.
+ *    The signal is delivered to all that have connected to the event.
+ *
+ *    The `task' is the event task.  The `context' is the context given as
+ *    argument to silc_schedule_event_connect.  The `schedule' is the
+ *    scheduler given as argument to silc_schedule_event_connect.
+ *
+ *    If FALSE is returned in this callback function the signal delivery to
+ *    other connected entities is stopped.  Normally, TRUE is returned.
+ *    If the `task' is deleted in this callback, the signal delivery is also
+ *    stopped.
+ *
+ *    To specify task event callback function in the application using the
+ *    SILC_TASK_EVENT_CALLBACK macro is recommended.
+ *
+ ***/
+typedef SilcBool (*SilcTaskEventCallback)(SilcSchedule schedule,
+                                         void *app_context,
+                                         SilcTask task, void *context,
+                                         va_list va);
+
 /****f* silcutil/SilcScheduleAPI/SilcTaskNotifyCb
  *
  * SYNOPSIS
@@ -237,6 +270,25 @@ void func(SilcSchedule schedule, void *app_context, SilcTaskEvent type,    \
          SilcUInt32 fd, void *context)
 /***/
 
+/****d* silcutil/SilcScheduleAPI/SILC_TASK_EVENT_CALLBACK
+ *
+ * NAME
+ *
+ *    #define SILC_TASK_EVENT_CALLBACK ...
+ *
+ * DESCRIPTION
+ *
+ *    Generic macro to declare event task callback functions. This defines a
+ *    function with name `func' as a event task callback function.
+ *
+ * SOURCE
+ */
+#define SILC_TASK_EVENT_CALLBACK(func)                                 \
+SilcBool func(SilcSchedule schedule, void *app_context,                        \
+             SilcTask task, void *context, va_list va)
+
+/***/
+
 /* Prototypes */
 
 #include "silcschedule_i.h"
@@ -246,15 +298,13 @@ void func(SilcSchedule schedule, void *app_context, SilcTaskEvent type,   \
  * SYNOPSIS
  *
  *    SilcSchedule silc_schedule_init(int max_tasks, void *app_context,
- *                                    SilcStack stack);
+ *                                    SilcStack stack, SilcSchedule parent);
  *
  * DESCRIPTION
  *
- *    Initializes the scheduler. This returns the scheduler context that
- *    is given as argument usually to all silc_schedule_* functions.
- *    The `app_context' is application specific context that is delivered
- *    to all task callbacks. The caller must free that context.  The
- *    'app_context' can be for example the application itself.
+ *    Initializes the scheduler. This returns the scheduler context or NULL
+ *    on error.  The `app_context' is application specific context that is
+ *    delivered to all task callbacks. The caller must free that context.
  *
  *    The `max_tasks' is the maximum number of file descriptor and socket
  *    tasks in the scheduler.  Set value to 0 to use default.  Operating
@@ -262,6 +312,14 @@ void func(SilcSchedule schedule, void *app_context, SilcTaskEvent type,    \
  *    limit can be significantly increased when this function is called in
  *    priviliged mode (as super user).
  *
+ *    If `parent' is non-NULL it will be the parent of the new scheduler.
+ *    If it is NULL this will create a new parent scheduler.  If `parent'
+ *    is already a child scheduler, this will create a new child to the
+ *    child's parent.  Even if `parent' is non-NULL the new child scheduler
+ *    is still independent scheduler and will run independently of its
+ *    parent.  However, each child and parent will share event tasks
+ *    added with silc_schedule_task_add_event.
+ *
  *    If `stack' is non-NULL all memory allocation for the scheduler is done
  *    from the `stack'.  Scheduler's stack may be retrieved by calling
  *    silc_schedule_get_stack.  A stack is created for scheduler always even
@@ -272,7 +330,7 @@ void func(SilcSchedule schedule, void *app_context, SilcTaskEvent type,     \
  *
  ***/
 SilcSchedule silc_schedule_init(int max_tasks, void *app_context,
-                               SilcStack stack);
+                               SilcStack stack, SilcSchedule parent);
 
 /****f* silcutil/SilcScheduleAPI/silc_schedule_uninit
  *
@@ -376,6 +434,19 @@ SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs);
  ***/
 void silc_schedule_wakeup(SilcSchedule schedule);
 
+/****f* silcutil/SilcScheduleAPI/silc_schedule_get_parent
+ *
+ * SYNOPSIS
+ *
+ *    SilcSchedule silc_schedule_get_parent(SilcSchedule schedule);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the parent scheduler of the `schedule'.  Never returns NULL.
+ *
+ ***/
+SilcSchedule silc_schedule_get_parent(SilcSchedule schedule);
+
 /****f* silcutil/SilcScheduleAPI/silc_schedule_get_context
  *
  * SYNOPSIS
@@ -550,6 +621,69 @@ SilcSchedule silc_schedule_get_global(void);
   silc_schedule_task_add(schedule, sig, callback, context, 0, 0,       \
                         SILC_TASK_SIGNAL)
 
+/****f* silcutil/SilcScheduleAPI/silc_schedule_task_add_event
+ *
+ * SYNOPSIS
+ *
+ *    SilcTask
+ *    silc_schedule_task_add_event(SilcSchedule schedule,
+ *                                 const char *event, ...);
+ *
+ * DESCRIPTION
+ *
+ *    Adds an event task to scheduler.  These tasks are asynchronous events
+ *    that one or more receivers may connect to and receive information or
+ *    data when the event is signalled.  Event tasks are fast and may be
+ *    used to efficiently deliver events and data to multiple receivers.  The
+ *    `event' is the name of the event, and can be used to connect to the
+ *    event and to signal it.
+ *
+ *    The events are global among the `scheduler', its parent scheduler and
+ *    any of its child schedulers.  It does not matter to which scheduler
+ *    event is added to, connected to or signalled.  Signal will reach any
+ *    connected entity, as long as it is the parent or one of the fellow
+ *    children of `schedule'.
+ *
+ *    To connect to an event call silc_schedule_event_connect.
+ *    To disconnect from event call silc_schedule_event_disconnect.
+ *    To signal event call silc_schedule_event_signal.
+ *    To delete event task call silc_schedule_task_del or
+ *    silc_schedule_task_del_event.
+ *
+ *    The variable argument list is used to describe the arguments of the
+ *    event.  The variable arguments are a list of zero or more SilcParam
+ *    values.  This function returns the event task context or NULL on error.
+ *
+ * EXAMPLE
+ *
+ *    // Register 'connected' event
+ *    silc_schedule_task_add_event(schedule, "connected",
+ *                                 SILC_PARAM_UINT32,
+ *                                 SILC_PARAM_BUFFER);
+ *
+ *    // Connect to 'connected' event
+ *    silc_schedule_event_connect(schedule, "connected", NULL,
+ *                                connected_cb, ctx);
+ *
+ *    // Signal 'connected' event
+ *    silc_schedule_event_signal(schedule, "connected", NULL, integer, buf);
+ *
+ *    // 'connected' event handler
+ *    SILC_TASK_CALLBACK(connected_cb)
+ *    {
+ *      FooCtx ctx = context;
+ *      SilcUInt32 integer;
+ *      SilcBuffer buf;
+ *
+ *      integer = va_arg(va, SilcUInt32);
+ *      buf = va_arg(va, SilcBuffer);
+ *      ...
+ *    }
+ *
+ ***/
+SilcTask silc_schedule_task_add_event(SilcSchedule schedule,
+                                     const char *event, ...);
+
 /****f* silcutil/SilcScheduleAPI/silc_schedule_task_del
  *
  * SYNOPSIS
@@ -667,6 +801,26 @@ SilcBool silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
                                       SilcTaskCallback callback,
                                       void *context);
 
+/****f* silcutil/SilcScheduleAPI/silc_schedule_task_del_event
+ *
+ * SYNOPSIS
+ *
+ *    void silc_schedule_task_del_event(SilcSchedule schedule,
+ *                                      const char *event);
+ *
+ * DESCRIPTION
+ *
+ *    Deletes event task by the event name `event'.  Returns FALSE if the
+ *    event does not exist.  Events can be deleted by calling the
+ *    silc_schedule_task_del also.
+ *
+ *    If `schedule' is NULL this will call silc_schedule_get_global to try to
+ *    get global scheduler.
+ *
+ ***/
+SilcBool silc_schedule_task_del_event(SilcSchedule schedule,
+                                     const char *event);
+
 /****f* silcutil/SilcScheduleAPI/silc_schedule_set_listen_fd
  *
  * SYNOPSIS
@@ -739,4 +893,100 @@ SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule,
  ***/
 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd);
 
+/****f* silcutil/SilcScheduleAPI/silc_schedule_event_connect
+ *
+ * SYNOPSIS
+ *
+ *    SilcBool silc_schedule_event_connect(SilcSchedule schedule,
+ *                                         const char *event, SilcTask task,
+ *                                         SilcTaskEventCallback callback,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Connects to an event task.  The `event' or `task' must be non-NULL.
+ *    If `event' is non-NULL it is the name of the event to connect to.  If
+ *    the `task' is non-NULL it is the event task to connect to.  The event
+ *    SilcTask pointer is returned by silc_schedule_task_add_event when the
+ *    even is added to scheduler.
+ *
+ *    The `callback' with `context' and with `schedule' are called when the
+ *    even task is signalled with silc_schedule_event_signal.
+ *
+ *    Returns FALSE on error or if the `callback' with `context' has already
+ *    been connected.  Otherwise, returns TRUE.
+ *
+ * EXAMPLE
+ *
+ *    silc_schedule_event_connect(schedule, "foo event", NULL,
+ *                                foo_signal_callback, foo_context);
+ *
+ ***/
+SilcBool silc_schedule_event_connect(SilcSchedule schedule,
+                                    const char *event, SilcTask task,
+                                    SilcTaskEventCallback callback,
+                                    void *context);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_event_disconnect
+ *
+ * SYNOPSIS
+ *
+ *    SilcBool silc_schedule_event_disconnect(SilcSchedule schedule,
+ *                                            const char *event, SilcTask task,
+ *                                            SilcTaskEventCallback callback,
+ *                                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Disconnects the `callback' and `context' from an event task.  The `event'
+ *    or `task' must be non-NULL.  If `event' is non-NULL it is the name of
+ *    the event.  If `task' is non-NULL it is the event task.
+ *
+ *    Returns FALSE on error or if the `callback' with `context' has not been
+ *    connected.  Otherwise, returns TRUE.
+ *
+ * EXAMPLE
+ *
+ *    silc_schedule_event_connect(schedule, "foo event", NULL,
+ *                                foo_signal_callback, foo_context);
+ *
+ ***/
+SilcBool silc_schedule_event_disconnect(SilcSchedule schedule,
+                                       const char *event, SilcTask task,
+                                       SilcTaskEventCallback callback,
+                                       void *context);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_event_signal
+ *
+ * SYNOPSIS
+ *
+ *    SilcBool silc_schedule_event_signal(SilcSchedule schedule,
+ *                                        const char *event,
+ *                                        SilcTask task, ...);
+ *
+ * DESCRIPTION
+ *
+ *    Signals an event task.  The `event' or `task' must be non-NULL.  If
+ *    `event' is non-NULL it is the name of the event to signal.  If the `task'
+ *    is non-NULL it is the task to be signalled.  It is marginally faster
+ *    to use the `task' pointer directly instead of `event' to send the signal.
+ *
+ *    The variable arguments are the arguments to be sent in the signal to
+ *    the connected entities.  The silc_schedule_task_add_event defines what
+ *    arguments must be sent to each signal.
+ *
+ *    Signal delivery is synchronous; the signal is delivered inside this
+ *    function.  If a receiver was originally in another thread, the signal
+ *    is delivered in the thread where this function is called.  This means
+ *    that concurrency control (locking) is required if the application uses
+ *    events in multiple threads.
+ *
+ * EXAMPLE
+ *
+ *    silc_schedule_event_signal(schedule, "foo event", NULL, intarg, buffer);
+ *
+ ***/
+SilcBool silc_schedule_event_signal(SilcSchedule schedule, const char *event,
+                                   SilcTask task, ...);
+
 #endif
index 6c8d462d56eaf2948a51250f8fb0801e73f35a56..a88df592c8fb7bad88b4f183e16b50dc7a2d3cc8 100644 (file)
@@ -38,12 +38,15 @@ typedef enum {
      automatically from the scheduler. It is safe to re-register the
      task in task callback. It is also safe to unregister a task in
      the task callback. */
-  SILC_TASK_TIMEOUT,
+  SILC_TASK_TIMEOUT      = 1,
 
   /* Platform specific process signal task.  On Unix systems this is one of
      the signals described in signal(7).  On other platforms this may not
      be available at all.  Only one callback per signal may be added. */
-  SILC_TASK_SIGNAL
+  SILC_TASK_SIGNAL       = 2,
+
+  /* Asynchronous event task. */
+  SILC_TASK_EVENT        = 3,
 } SilcTaskType;
 
 /* Task header */
@@ -51,7 +54,7 @@ struct SilcTaskStruct {
   struct SilcTaskStruct *next;
   SilcTaskCallback callback;
   void *context;
-  unsigned int type    : 1;    /* 0 = fd, 1 = timeout */
+  unsigned int type    : 2;    /* SilcTaskType */
   unsigned int valid   : 1;    /* Set if task is valid */
 };
 
@@ -66,17 +69,26 @@ typedef struct SilcTaskFdStruct {
   struct SilcTaskStruct header;
   unsigned int scheduled  : 1;
   unsigned int events     : 14;
-  unsigned int revents    : 15;
+  unsigned int revents    : 14;
   SilcUInt32 fd;
 } *SilcTaskFd;
 
+/* Event task */
+typedef struct SilcEventTaskStruct {
+  struct SilcTaskStruct header;
+  char *event;
+  SilcList connections;
+} *SilcEventTask;
+
 /* Scheduler context */
 struct SilcScheduleStruct {
+  SilcSchedule parent;            /* Parent scheduler */
   void *internal;
   void *app_context;              /* Application specific context */
   SilcTaskNotifyCb notify;        /* Notify callback */
   void *notify_context;                   /* Notify context */
   SilcStack stack;                /* Stack */
+  SilcHashTable events;                   /* Event tasks */
   SilcHashTable fd_queue;         /* FD task queue */
   SilcList fd_dispatch;                   /* Dispatched FDs */
   SilcList timeout_queue;         /* Timeout queue */
index a86ba68e2ddcc13714bef3c86975d84fddf30f2b..28d2563245557ca800dc30ee7a764cd3a3553245 100644 (file)
@@ -12,10 +12,11 @@ typedef void (*Callback)(void *context);
 #endif
 
 SilcSchedule schedule;
+int c = 0;
 
 void notify_cb(SilcSchedule schedule, SilcBool added, SilcTask task,
-              SilcBool fd_task, SilcUInt32 fd, long sec, long usec,
-              void *context)
+              SilcBool fd_task, SilcUInt32 fd, SilcTaskEvent event,
+              long sec, long usec, void *context)
 {
   SILC_LOG_DEBUG(("Notify cb, %s %s task, fd %d, sec %d usec %d",
                  added ? "added" : "deleted", fd_task ? "fd" :"timeout",
@@ -31,6 +32,10 @@ SILC_TASK_CALLBACK(timeout)
 {
   int i = (int)context;
   SILC_LOG_DEBUG(("Timeout task %d", i));
+
+  SILC_LOG_DEBUG(("Send 'timeout' signal"));
+  if (!silc_schedule_event_signal(NULL, "timeout", NULL))
+    SILC_LOG_DEBUG(("Error sending signal, error %d", silc_errno));
 }
 
 SILC_TASK_CALLBACK(cont2)
@@ -82,13 +87,38 @@ SILC_TASK_CALLBACK(start)
 
 SILC_TASK_CALLBACK(interrupt)
 {
-  SILC_LOG_DEBUG(("SIGINT signal"));
+  SILC_LOG_DEBUG(("SIGINT signal, send 'interrupted' event signal"));
+  if (!silc_schedule_event_signal(schedule, "interrupted", NULL,
+                                 schedule))
+    SILC_LOG_DEBUG(("Error sending signal, error %d", silc_errno));
+}
+
+SILC_TASK_EVENT_CALLBACK(timeout_event_cb)
+{
+  SILC_LOG_DEBUG(("timeout event signalled"));
+  if (c++ == 100) {
+    silc_schedule_task_del_event(NULL, "timeout");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+SILC_TASK_EVENT_CALLBACK(interrupted_event)
+{
+  SilcSchedule ptr = va_arg(va, void *);
+  SILC_LOG_DEBUG(("interrupted event signalled, ptr %p", ptr));
+  silc_schedule_event_disconnect(NULL, "interrupted", NULL,
+                                interrupted_event, NULL);
   silc_schedule_stop(schedule);
+  return TRUE;
 }
 
 int main(int argc, char **argv)
 {
   SilcBool success = FALSE;
+  SilcSchedule child, child2;
+  SilcTask timeout_event;
 
   if (argc > 1 && !strcmp(argv[1], "-d")) {
     silc_log_debug(TRUE);
@@ -98,19 +128,56 @@ int main(int argc, char **argv)
   }
 
   SILC_LOG_DEBUG(("Allocating scheduler"));
-  schedule = silc_schedule_init(NUM_FTASK, NULL, NULL);
+  schedule = silc_schedule_init(NUM_FTASK, NULL, NULL, NULL);
   if (!schedule)
     goto err;
   silc_schedule_set_notify(schedule, notify_cb, NULL);
 
+  SILC_LOG_DEBUG(("Allocate child scheduler"));
+  child = silc_schedule_init(0, NULL, NULL, schedule);
+  if (!child)
+    goto err;
+
+  SILC_LOG_DEBUG(("Allocate another child scheduler"));
+  child2 = silc_schedule_init(0, NULL, NULL, child);
+  if (!child2)
+    goto err;
+
+  SILC_LOG_DEBUG(("Add 'interrupted' event to child scheduler"));
+  if (!silc_schedule_task_add_event(child, "interrupted",
+                                   SILC_PARAM_PTR))
+    goto err;
+
+  SILC_LOG_DEBUG(("Add 'timeout' event to parent scheduler"));
+  timeout_event =
+    silc_schedule_task_add_event(schedule, "timeout");
+  if (!timeout_event)
+    goto err;
+
+  SILC_LOG_DEBUG(("Connect to 'interrupted' event in parent scheduler"));
+  if (!silc_schedule_event_connect(schedule, "interrupted", NULL,
+                                  interrupted_event, NULL))
+    goto err;
+
+  SILC_LOG_DEBUG(("Connect to 'timeout' event in child2 scheduler"));
+  if (!silc_schedule_event_connect(child2, NULL, timeout_event,
+                                  timeout_event_cb, NULL))
+    goto err;
+
+  SILC_LOG_DEBUG(("Add parent as global scheduler"));
+  silc_schedule_set_global(schedule);
+
   silc_schedule_task_add_signal(schedule, SIGINT, interrupt, NULL);
 
-  silc_schedule_task_add_timeout(schedule, start, NULL, 1, 0);
+  if (!silc_schedule_task_add_timeout(schedule, start, NULL, 1, 0))
+    goto err;
 
   SILC_LOG_DEBUG(("Running scheduler"));
   silc_schedule(schedule);
 
   silc_schedule_uninit(schedule);
+  silc_schedule_uninit(child);
+  silc_schedule_uninit(child2);
 
   success = TRUE;
 
index 0f2f201b696f9a95f43a1353865ba8f5301a38cc..d81cfa751668172aa9d3789a7a51c26bac0a473a 100644 (file)
@@ -495,6 +495,7 @@ void silc_schedule_internal_signal_register(SilcSchedule schedule,
       signal_call[i].sig = sig;
       signal_call[i].callback = callback;
       signal_call[i].context = callback_context;
+      signal_call[i].schedule = schedule;
       signal_call[i].call = FALSE;
       signal(sig, silc_schedule_internal_sighandler);
       break;
@@ -524,6 +525,7 @@ void silc_schedule_internal_signal_unregister(SilcSchedule schedule,
       signal_call[i].sig = 0;
       signal_call[i].callback = NULL;
       signal_call[i].context = NULL;
+      signal_call[i].schedule = NULL;
       signal_call[i].call = FALSE;
       signal(sig, SIG_DFL);
     }