From: Pekka Riikonen Date: Mon, 9 Jul 2001 12:27:13 +0000 (+0000) Subject: updates. X-Git-Tag: robodoc-323~92 X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=commitdiff_plain;h=262aba230e36a455a53fbde13f901a44472eae15 updates. --- diff --git a/CHANGES b/CHANGES index 7efbdc0c..df8b1b93 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,24 @@ +Mon Jul 9 13:40:03 EEST 2001 Pekka Riikonen + + * 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 * Added new function silc_socket_host_lookup to perform diff --git a/lib/silcutil/silcschedule.c b/lib/silcutil/silcschedule.c index 036b0bda..260ed50d 100644 --- a/lib/silcutil/silcschedule.c +++ b/lib/silcutil/silcschedule.c @@ -22,10 +22,29 @@ #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 +} diff --git a/lib/silcutil/silcschedule.h b/lib/silcutil/silcschedule.h index def68e1c..07549c47 100644 --- a/lib/silcutil/silcschedule.h +++ b/lib/silcutil/silcschedule.h @@ -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 diff --git a/lib/silcutil/silcsockconn.c b/lib/silcutil/silcsockconn.c index e837fa9c..0fdcb904 100644 --- a/lib/silcutil/silcsockconn.c +++ b/lib/silcutil/silcsockconn.c @@ -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 } diff --git a/lib/silcutil/silctask.c b/lib/silcutil/silctask.c index 738edae8..18089ae5 100644 --- a/lib/silcutil/silctask.c +++ b/lib/silcutil/silctask.c @@ -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. */ diff --git a/lib/silcutil/silctask.h b/lib/silcutil/silctask.h index 7719c519..c07a86eb 100644 --- a/lib/silcutil/silctask.h +++ b/lib/silcutil/silctask.h @@ -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, diff --git a/lib/silcutil/unix/silcunixschedule.c b/lib/silcutil/unix/silcunixschedule.c index 951e1aea..4c70827a 100644 --- a/lib/silcutil/unix/silcunixschedule.c +++ b/lib/silcutil/unix/silcunixschedule.c @@ -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 +} diff --git a/lib/silcutil/win32/silcwin32schedule.c b/lib/silcutil/win32/silcwin32schedule.c index 6e8b4562..cd6327c7 100644 --- a/lib/silcutil/win32/silcwin32schedule.c +++ b/lib/silcutil/win32/silcwin32schedule.c @@ -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 +}