updates.
authorPekka Riikonen <priikone@silcnet.org>
Mon, 9 Jul 2001 12:27:13 +0000 (12:27 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Mon, 9 Jul 2001 12:27:13 +0000 (12:27 +0000)
CHANGES
lib/silcutil/silcschedule.c
lib/silcutil/silcschedule.h
lib/silcutil/silcsockconn.c
lib/silcutil/silctask.c
lib/silcutil/silctask.h
lib/silcutil/unix/silcunixschedule.c
lib/silcutil/win32/silcwin32schedule.c

diff --git a/CHANGES b/CHANGES
index 7efbdc0c3c44ca1830c73a825d66331190b8881a..df8b1b93df44bac15c20aac9db2f0e12091c2ead 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,24 @@
+Mon Jul  9 13:40:03 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added new function silc_schedule_wakeup that is used in
+         multi-threaded environment to wakeup the main thread's
+         schduler. It needs to be used when a thread adds a new task
+         or removes a task from task queues. After waking up, the
+         scheduler will detect the task queue changes. If threads
+         support is not compiled in this function has no effect.
+         Implemented the wakeup mechanism to both Unix and WIN32
+         systems.  Affected files are lib/silcutil/silcschedule.[ch],
+         lib/silcutil/unix/silcunixschedule.c and the
+         lib/silcutil/win32/silcwin32schedule.c.
+
+       * Added new function silc_task_queue_wakeup to wakeup the
+         scheduler by the specified task queue.  Affected file
+         lib/silcutil/silctask.[ch].
+
+       * The silc_socket_host_lookup_start now wakes up the scheduler
+         after adding the timeout task.  Affected file is
+         lib/silcutil/silcsockconn.c.
+
 Mon Jul  9 00:24:45 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
 
        * Added new function silc_socket_host_lookup to perform
index 036b0bda1e5961d0e454f275ee94a1bcdb678847..260ed50de0994be6f8cd53c3a31de8b9fafb35c5 100644 (file)
 
 #include "silcincludes.h"
 
-/* System specific implementation of the select. */
+/* Routine to remove the task. Implemented in silctask.c. */
+int silc_task_remove(SilcTaskQueue queue, SilcTask task);
+
+/* System specific routines. Implemented under unix/ and win32/. */
+
+/* System specific select(). */
 int silc_select(int n, fd_set *readfds, fd_set *writefds,
                fd_set *exceptfds, struct timeval *timeout);
 
+/* Initializes the wakeup 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(void *queue);
+
+/* Uninitializes the system specific wakeup. */
+void silc_schedule_wakeup_uninit(void *context);
+
+/* Wakes up the scheduler. This is platform specific routine */
+void silc_schedule_wakeup_internal(void *context);
+
 /* Structure holding list of file descriptors, scheduler is supposed to
    be listenning. The max_fd field is the maximum number of possible file
    descriptors in the list. This value is set at the initialization
@@ -98,6 +117,12 @@ typedef struct {
        managed automatically by the scheduler and should be considered to 
        be read-only field otherwise.
 
+   void *wakeup
+
+       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.
+
 */
 struct SilcScheduleStruct {
   SilcTaskQueue fd_queue;
@@ -109,6 +134,7 @@ struct SilcScheduleStruct {
   fd_set in;
   fd_set out;
   int max_fd;
+  void *wakeup;
   SILC_MUTEX_DEFINE(lock);
 };
 
@@ -156,8 +182,12 @@ SilcSchedule silc_schedule_init(SilcTaskQueue *fd_queue,
   schedule->max_fd = -1;
   for (i = 0; i < max_fd; i++)
     schedule->fd_list.fd[i] = -1;
+
   silc_mutex_alloc(&schedule->lock);
 
+  /* Initialize the wakeup */
+  schedule->wakeup = silc_schedule_wakeup_init(schedule->fd_queue);
+
   return schedule;
 }
 
@@ -196,6 +226,9 @@ bool silc_schedule_uninit(SilcSchedule schedule)
     silc_free(schedule->fd_list.fd);
   }
 
+  /* Uninit the wakeup */
+  silc_schedule_wakeup_uninit(schedule->wakeup);
+
   silc_mutex_free(schedule->lock);
 
   return TRUE;
@@ -537,6 +570,7 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
   SilcTask task;
   SilcTaskQueue queue;
   struct timeval curtime;
+  int ret;
 
   SILC_LOG_DEBUG(("In scheduler loop"));
 
@@ -564,8 +598,6 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
      tasks. The select() listens to these file descriptors. */
   SILC_SCHEDULE_SELECT_TASKS;
 
-  silc_mutex_unlock(schedule->lock);
-
   if (schedule->max_fd == -1 && !schedule->timeout)
     return FALSE;
 
@@ -580,12 +612,16 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
     schedule->timeout = &timeout;
   }
 
+  silc_mutex_unlock(schedule->lock);
+
   /* This is the main select(). The program blocks here until some
      of the selected file descriptors change status or the selected
      timeout expires. */
   SILC_LOG_DEBUG(("Select"));
-  switch (silc_select(schedule->max_fd + 1, &schedule->in,
-                     &schedule->out, 0, schedule->timeout)) {
+  ret = silc_select(schedule->max_fd + 1, &schedule->in,
+                   &schedule->out, 0, schedule->timeout);
+
+  switch (ret) {
   case -1:
     /* Error */
     if (errno == EINTR)
@@ -627,3 +663,20 @@ void silc_schedule(SilcSchedule schedule)
   while (silc_schedule_one(schedule, -1)) 
     ;
 }
+
+/* Wakes up the scheduler. This is used only in multi-threaded
+   environments where threads may add new tasks or remove old tasks
+   from task queues. This is called to wake up the scheduler in the
+   main thread so that it detects the changes in the task queues.
+   If threads support is not compiled in this function has no effect.
+   Implementation of this function is platform specific. */
+
+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);
+#endif
+}
index def68e1c7f379a29d808aa21412a6d19d7cfe9b7..07549c477091459d939ca3aa9a7331699def6df3 100644 (file)
@@ -160,4 +160,22 @@ void silc_schedule(SilcSchedule schedule);
  ***/
 bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs);
 
+/****f* silcutil/SilcScheduleAPI/silc_schedule_wakeup
+ *
+ * SYNOPSIS
+ *
+ *    void silc_schedule_wakeup(SilcSchedule schedule);
+ *
+ * DESCRIPTION
+ *
+ *    Wakes up the scheduler. This is used only in multi-threaded
+ *    environments where threads may add new tasks or remove old tasks
+ *    from task queues. This is called to wake up the scheduler in the
+ *    main thread so that it detects the changes in the task queues.
+ *    If threads support is not compiled in this function has no effect.
+ *    Implementation of this function may be platform specific.
+ *
+ ***/
+void silc_schedule_wakeup(SilcSchedule schedule);
+
 #endif
index e837fa9ced7e0f7cf808935bf45950507b45c87d..0fdcb90435b7b2381395e1dc6089121b306183b3 100644 (file)
@@ -233,5 +233,9 @@ void silc_socket_host_lookup(SilcSocketConnection sock,
 
   SILC_SET_HOST_LOOKUP(sock);
 
+#ifdef SILC_THREADS
   silc_thread_create(silc_socket_host_lookup_start, lookup, FALSE);
+#else
+  silc_socket_host_lookup_start((void *)lookup);
+#endif
 }
index 738edae84d2febd2d71c741a38aba7cb285e658b..18089ae58512b9bcb0921130e0b02c19aecb822e 100644 (file)
@@ -49,6 +49,17 @@ void silc_task_queue_free(SilcTaskQueue queue)
   silc_free(queue);
 }
 
+/* Wakes up the task queue. This actually wakes up the scheduler of this
+   task queue. This is called in multi-threaded environment to wake up
+   the scheduler after adding or removing tasks from the task queue. */
+
+void silc_task_queue_wakeup(SilcTaskQueue queue)
+{
+  silc_mutex_lock(queue->lock);
+  silc_schedule_wakeup(queue->schedule);
+  silc_mutex_unlock(queue->lock);
+}
+
 /* Adds a non-timeout task into the task queue. This function is used
    by silc_task_register function. Returns a pointer to the registered 
    task. */
@@ -353,9 +364,8 @@ SilcTask silc_task_register(SilcTaskQueue queue, int fd,
 }
 
 /* Removes (unregisters) a task from particular task queue. This function
-   is used internally by scheduler. One should not call this function
-   to unregister tasks, instead silc_task_unregister_task function
-   should be used. */
+   is used internally by scheduler. This must be called holding the 
+   queue->lock. */
 
 int silc_task_remove(SilcTaskQueue queue, SilcTask task)
 {
@@ -364,10 +374,7 @@ int silc_task_remove(SilcTaskQueue queue, SilcTask task)
   if (!queue)
     return FALSE;
 
-  silc_mutex_lock(queue->lock);
-
   if (!queue->task) {
-    silc_mutex_unlock(queue->lock);
     return FALSE;
   }
 
@@ -386,7 +393,6 @@ int silc_task_remove(SilcTaskQueue queue, SilcTask task)
     }
 
     queue->task = NULL;
-    silc_mutex_unlock(queue->lock);
     return TRUE;
   }
 
@@ -409,13 +415,11 @@ int silc_task_remove(SilcTaskQueue queue, SilcTask task)
        queue->task = silc_task_get_first(queue, next);
       
       silc_free(old);
-      silc_mutex_unlock(queue->lock);
       return TRUE;
     }
     old = old->prev;
 
     if (old == first) {
-      silc_mutex_unlock(queue->lock);
       return FALSE;
     }
   }
@@ -462,9 +466,13 @@ void silc_task_unregister(SilcTaskQueue queue, SilcTask task)
 
   SILC_LOG_DEBUG(("Unregistering task"));
 
+  silc_mutex_lock(queue->lock);
+
   /* Unregister the specific task */
   if (task->valid)
     task->valid = FALSE;
+
+  silc_mutex_unlock(queue->lock);
 }
 
 /* Unregister a task by file descriptor. This invalidates the task. */
index 7719c519328adfa617fc30a5c07a8b89138e552f..c07a86eb583bfcf4e3804bc3d79f002ae323d3d0 100644 (file)
@@ -239,6 +239,7 @@ void func(void *qptr, int type, void *context, int fd)
 void silc_task_queue_alloc(SilcSchedule schedule, SilcTaskQueue *queue, 
                           bool valid);
 void silc_task_queue_free(SilcTaskQueue queue);
+void silc_task_queue_wakeup(SilcTaskQueue queue);
 SilcTask silc_task_add(SilcTaskQueue queue, SilcTask task, 
                       SilcTaskPriority priority);
 SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask task,
@@ -248,7 +249,6 @@ SilcTask silc_task_register(SilcTaskQueue queue, int fd,
                            long seconds, long useconds, 
                            SilcTaskType type, 
                            SilcTaskPriority priority);
-int silc_task_remove(SilcTaskQueue queue, SilcTask task);
 void silc_task_unregister(SilcTaskQueue queue, SilcTask task);
 void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd);
 void silc_task_unregister_by_callback(SilcTaskQueue queue, 
index 951e1aeab53fa223355b8974576396733a5fe2f3..4c70827a5a290eaf4f3d93100b5a867885856139 100644 (file)
@@ -28,3 +28,86 @@ int silc_select(int n, fd_set *readfds, fd_set *writefds,
 {
   return select(n, readfds, writefds, exceptfds, timeout);
 }
+
+#ifdef SILC_THREADS
+
+/* Internal wakeup context. */
+typedef struct {
+  int wakeup_pipe[2];
+  SilcTask wakeup_task;
+} *SilcUnixWakeup;
+
+SILC_TASK_CALLBACK(silc_schedule_wakeup_cb)
+{
+  SilcUnixWakeup wakeup = (SilcUnixWakeup)context;
+  unsigned char c;
+
+  read(wakeup->wakeup_pipe[0], &c, 1);
+}
+
+#endif /* SILC_THREADS */
+
+/* Initializes the wakeup 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 quaranteed that the scheduler will automatically free any
+   registered tasks in this queue. This is system specific routine. */
+
+void *silc_schedule_wakeup_init(void *queue)
+{
+#ifdef SILC_THREADS
+  SilcUnixWakeup wakeup;
+
+  wakeup = silc_calloc(1, sizeof(*wakeup));
+
+  if (pipe(wakeup->wakeup_pipe)) {
+    silc_free(wakeup);
+    return NULL;
+  }
+
+  wakeup->wakeup_task = silc_task_register(queue, wakeup->wakeup_pipe[0],
+                                          silc_schedule_wakeup_cb, wakeup,
+                                          0, 0, SILC_TASK_FD, 
+                                          SILC_TASK_PRI_NORMAL);
+  if (!wakeup->wakeup_task) {
+    close(wakeup->wakeup_pipe[0]);
+    close(wakeup->wakeup_pipe[1]);
+    silc_free(wakeup);
+    return NULL;
+  }
+
+  return (void *)wakeup;
+#endif
+  return NULL;
+}
+
+/* Uninitializes the system specific wakeup. */
+
+void silc_schedule_wakeup_uninit(void *context)
+{
+#ifdef SILC_THREADS
+  SilcUnixWakeup wakeup = (SilcUnixWakeup)context;
+
+  if (!wakeup)
+    return;
+
+  close(wakeup->wakeup_pipe[0]);
+  close(wakeup->wakeup_pipe[1]);
+  silc_free(wakeup);
+#endif
+}
+
+/* Wakes up the scheduler */
+
+void silc_schedule_wakeup_internal(void *context)
+{
+#ifdef SILC_THREADS
+  SilcUnixWakeup wakeup = (SilcUnixWakeup)context;
+
+  if (!wakeup)
+    return;
+
+  write(wakeup->wakeup_pipe[1], "!", 1);
+#endif
+}
index 6e8b456253cf01f4df7158dfff4a1616cf4f485a..cd6327c74b0e25849097fee378efce9426d83296 100644 (file)
@@ -165,3 +165,80 @@ int silc_select(int n, fd_set *readfds, fd_set *writefds,
 
   return -1;
 }
+
+/* Internal wakeup context. */
+typedef struct {
+  HANDLE wakeup_sema;
+  SilcTask wakeup_task;
+} *SilcWin32Wakeup;
+
+SILC_TASK_CALLBACK(silc_schedule_wakeup_cb)
+{
+  /* Nothing */
+}
+
+#endif /* SILC_THREADS */
+
+/* Initializes the wakeup 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(void *queue)
+{
+#ifdef SILC_THREADS
+  SilcWin32Wakeup wakeup;
+
+  wakeup = silc_calloc(1, sizeof(*wakeup));
+
+  wakeup->wakeup_sema = CreateSemaphore(NULL, 0, 100, NULL);
+  if (!wakeup->wakeup_sema) {
+    silc_free(wakeup);
+    return NULL;
+  }
+
+  wakeup->wakeup_task = silc_task_register(queue, (int)wakeup->wakeup_sema,
+                                          silc_schedule_wakeup_cb, wakeup,
+                                          0, 0, SILC_TASK_FD, 
+                                          SILC_TASK_PRI_NORMAL);
+  if (!wakeup->wakeup_task) {
+    CloseHandle(wakeup->wakeup_sema);
+    silc_free(wakeup);
+    return NULL;
+  }
+
+  return (void *)wakeup;
+#endif
+  return NULL;
+}
+
+/* Uninitializes the system specific wakeup. */
+
+void silc_schedule_wakeup_uninit(void *context)
+{
+#ifdef SILC_THREADS
+  SilcWin32Wakeup wakeup = (SilcWin32Wakeup)context;
+
+  if (!wakeup)
+    return;
+
+  CloseHandle(wakeup->wakeup_sema);
+  silc_free(wakeup);
+#endif
+}
+
+/* Wakes up the scheduler */
+
+void silc_schedule_wakeup_internal(void *context)
+{
+#ifdef SILC_THREADS
+  SilcWin32Wakeup wakeup = (SilcWin32Wakeup)context;
+
+  if (!wakeup)
+    return;
+
+  ReleaseSemaphore(wakeup->wakeup_sema, 1, NULL);
+#endif
+}