+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
#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
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;
fd_set in;
fd_set out;
int max_fd;
+ void *wakeup;
SILC_MUTEX_DEFINE(lock);
};
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;
}
silc_free(schedule->fd_list.fd);
}
+ /* Uninit the wakeup */
+ silc_schedule_wakeup_uninit(schedule->wakeup);
+
silc_mutex_free(schedule->lock);
return TRUE;
SilcTask task;
SilcTaskQueue queue;
struct timeval curtime;
+ int ret;
SILC_LOG_DEBUG(("In scheduler loop"));
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;
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)
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
+}
***/
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
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
}
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. */
}
/* 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)
{
if (!queue)
return FALSE;
- silc_mutex_lock(queue->lock);
-
if (!queue->task) {
- silc_mutex_unlock(queue->lock);
return FALSE;
}
}
queue->task = NULL;
- silc_mutex_unlock(queue->lock);
return TRUE;
}
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;
}
}
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. */
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,
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,
{
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
+}
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
+}