X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcutil%2Fsilcschedule.c;h=e0f658b08f52807e42fa93107a14f107155bbf59;hp=828e78748e639167b95b043064312d28656fb5c7;hb=382d15d447b7a95390decfa783836ae4fe255b3d;hpb=6f6c6b1f52138ed5138c530614b24a503e542024 diff --git a/lib/silcutil/silcschedule.c b/lib/silcutil/silcschedule.c index 828e7874..e0f658b0 100644 --- a/lib/silcutil/silcschedule.c +++ b/lib/silcutil/silcschedule.c @@ -1,16 +1,15 @@ /* - silcschedule.c + silcschedule.c Author: Pekka Riikonen - Copyright (C) 1998 - 2001 Pekka Riikonen + Copyright (C) 1998 - 2002 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - + the Free Software Foundation; version 2 of the License. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -35,7 +34,7 @@ int silc_select(SilcScheduleFd fds, SilcUInt32 fds_count, the wakeup mechanism of the scheduler. In multi-threaded environment 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 *context); /* Uninitializes the platform specific scheduler context. */ void silc_schedule_internal_uninit(void *context); @@ -70,6 +69,8 @@ void silc_schedule_internal_signals_unblock(void *context); /* Internal task management routines. */ +static void silc_schedule_dispatch_timeout(SilcSchedule schedule, + bool dispatch_all); static void silc_task_queue_alloc(SilcTaskQueue *queue); static void silc_task_queue_free(SilcTaskQueue queue); static SilcTask silc_task_find(SilcTaskQueue queue, SilcUInt32 fd); @@ -79,8 +80,6 @@ static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first); static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask, SilcTaskPriority priority); static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task); -static int silc_schedule_task_timeout_compare(struct timeval *smaller, - struct timeval *bigger); static void silc_task_del_by_context(SilcTaskQueue queue, void *context); static void silc_task_del_by_callback(SilcTaskQueue queue, SilcTaskCallback callback); @@ -208,6 +207,7 @@ struct SilcTaskQueueStruct { */ struct SilcScheduleStruct { + void *app_context; /* Application specific context */ SilcTaskQueue fd_queue; SilcTaskQueue timeout_queue; SilcTaskQueue generic_queue; @@ -225,9 +225,10 @@ struct SilcScheduleStruct { /* Initializes the scheduler. This returns the scheduler context that is given as arugment usually to all silc_schedule_* functions. The `max_tasks' indicates the number of maximum tasks that the - scheduler can handle. */ + scheduler can handle. The `app_context' is application specific + context that is delivered to task callbacks. */ -SilcSchedule silc_schedule_init(int max_tasks) +SilcSchedule silc_schedule_init(int max_tasks, void *app_context) { SilcSchedule schedule; @@ -249,12 +250,13 @@ SilcSchedule silc_schedule_init(int max_tasks) schedule->max_fd = max_tasks; schedule->timeout = NULL; schedule->valid = TRUE; + schedule->app_context = app_context; /* Allocate scheduler lock */ silc_mutex_alloc(&schedule->lock); /* Initialize the platform specific scheduler. */ - schedule->internal = silc_schedule_internal_init(schedule); + schedule->internal = silc_schedule_internal_init(schedule, app_context); return schedule; } @@ -271,6 +273,19 @@ bool silc_schedule_uninit(SilcSchedule schedule) if (schedule->valid == TRUE) return FALSE; + /* Dispatch all timeouts before going away */ + SILC_SCHEDULE_LOCK(schedule); + silc_mutex_lock(schedule->timeout_queue->lock); + silc_schedule_dispatch_timeout(schedule, TRUE); + silc_mutex_unlock(schedule->timeout_queue->lock); + SILC_SCHEDULE_UNLOCK(schedule); + + /* Deliver signals before going away */ + if (schedule->signal_tasks) { + silc_schedule_internal_signals_call(schedule->internal, schedule); + schedule->signal_tasks = FALSE; + } + /* Unregister all tasks */ silc_schedule_task_remove(schedule->fd_queue, SILC_ALL_TASKS); silc_schedule_task_remove(schedule->timeout_queue, SILC_ALL_TASKS); @@ -287,6 +302,7 @@ bool silc_schedule_uninit(SilcSchedule schedule) silc_schedule_internal_uninit(schedule->internal); silc_mutex_free(schedule->lock); + silc_free(schedule); return TRUE; } @@ -326,8 +342,8 @@ void silc_schedule_stop(SilcSchedule schedule) static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule) { SilcTask task; - int i, last_fd = schedule->last_fd; - SilcUInt32 fd; + int i; + SilcUInt32 fd, last_fd = schedule->last_fd; for (i = 0; i <= last_fd; i++) { if (schedule->fd_list[i].events == 0) @@ -350,7 +366,8 @@ static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule) if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) { silc_mutex_unlock(schedule->fd_queue->lock); SILC_SCHEDULE_UNLOCK(schedule); - task->callback(schedule, SILC_TASK_READ, task->fd, task->context); + task->callback(schedule, schedule->app_context, + SILC_TASK_READ, task->fd, task->context); SILC_SCHEDULE_LOCK(schedule); silc_mutex_lock(schedule->fd_queue->lock); } @@ -359,7 +376,8 @@ static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule) if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) { silc_mutex_unlock(schedule->fd_queue->lock); SILC_SCHEDULE_UNLOCK(schedule); - task->callback(schedule, SILC_TASK_WRITE, task->fd, task->context); + task->callback(schedule, schedule->app_context, + SILC_TASK_WRITE, task->fd, task->context); SILC_SCHEDULE_LOCK(schedule); silc_mutex_lock(schedule->fd_queue->lock); } @@ -389,7 +407,8 @@ static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule) if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) { silc_mutex_unlock(schedule->generic_queue->lock); SILC_SCHEDULE_UNLOCK(schedule); - task->callback(schedule, SILC_TASK_READ, fd, task->context); + task->callback(schedule, schedule->app_context, + SILC_TASK_READ, fd, task->context); SILC_SCHEDULE_LOCK(schedule); silc_mutex_lock(schedule->generic_queue->lock); } @@ -398,7 +417,8 @@ static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule) if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) { silc_mutex_unlock(schedule->generic_queue->lock); SILC_SCHEDULE_UNLOCK(schedule); - task->callback(schedule, SILC_TASK_WRITE, fd, task->context); + task->callback(schedule, schedule->app_context, + SILC_TASK_WRITE, fd, task->context); SILC_SCHEDULE_LOCK(schedule); silc_mutex_lock(schedule->generic_queue->lock); } @@ -436,7 +456,8 @@ static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule) phase. */ /* This holds the schedule->lock and the schedule->timeout_queue->lock */ -static void silc_schedule_dispatch_timeout(SilcSchedule schedule) +static void silc_schedule_dispatch_timeout(SilcSchedule schedule, + bool dispatch_all) { SilcTaskQueue queue = schedule->timeout_queue; SilcTask task; @@ -454,11 +475,13 @@ static void silc_schedule_dispatch_timeout(SilcSchedule schedule) the expired tasks. */ while(1) { /* Execute the task if the timeout has expired */ - if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) { + if (dispatch_all || + silc_compare_timeval(&task->timeout, &curtime)) { if (task->valid) { silc_mutex_unlock(queue->lock); SILC_SCHEDULE_UNLOCK(schedule); - task->callback(schedule, SILC_TASK_EXPIRE, task->fd, task->context); + task->callback(schedule, schedule->app_context, + SILC_TASK_EXPIRE, task->fd, task->context); SILC_SCHEDULE_LOCK(schedule); silc_mutex_lock(queue->lock); } @@ -507,9 +530,9 @@ static void silc_schedule_select_timeout(SilcSchedule schedule) if (task && task->valid == TRUE) { /* If the timeout is in past, we will run the task and all other timeout tasks from the past. */ - if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) { - silc_schedule_dispatch_timeout(schedule); - + if (silc_compare_timeval(&task->timeout, &curtime)) { + silc_schedule_dispatch_timeout(schedule, FALSE); + /* The task(s) has expired and doesn't exist on the task queue anymore. We continue with new timeout. */ queue = schedule->timeout_queue; @@ -614,7 +637,7 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs) case 0: /* Timeout */ silc_mutex_lock(schedule->timeout_queue->lock); - silc_schedule_dispatch_timeout(schedule); + silc_schedule_dispatch_timeout(schedule, FALSE); silc_mutex_unlock(schedule->timeout_queue->lock); break; default: @@ -670,6 +693,16 @@ void silc_schedule_wakeup(SilcSchedule schedule) #endif } +/* 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 + may be used to get it as well if needed. */ + +void *silc_schedule_get_context(SilcSchedule schedule) +{ + return schedule->app_context; +} + /* Add new task to the scheduler */ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, @@ -682,6 +715,9 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, SilcTaskQueue queue; int timeout = FALSE; + if (!schedule->valid) + return NULL; + SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", fd, type, priority)); @@ -703,7 +739,7 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, /* Add the fd to be listened, the task found now applies to this fd as well. */ - silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ); + silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ, FALSE); return task; } @@ -742,7 +778,7 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, /* If the task is non-timeout task we have to tell the scheduler that we would like to have these tasks scheduled at some odd distant future. */ if (type != SILC_TASK_TIMEOUT) - silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ); + silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ, FALSE); silc_mutex_lock(queue->lock); @@ -843,21 +879,28 @@ void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context) call this directly if wanted. This can be called multiple times for one file descriptor to set different iomasks. */ -void silc_schedule_set_listen_fd(SilcSchedule schedule, - SilcUInt32 fd, SilcTaskEvent iomask) +void silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd, + SilcTaskEvent mask, bool send_events) { int i; bool found = FALSE; + if (!schedule->valid) + return; + SILC_SCHEDULE_LOCK(schedule); for (i = 0; i < schedule->max_fd; i++) if (schedule->fd_list[i].fd == fd) { schedule->fd_list[i].fd = fd; - schedule->fd_list[i].events = iomask; + schedule->fd_list[i].events = mask; if (i > schedule->last_fd) schedule->last_fd = i; found = TRUE; + if (send_events) { + schedule->fd_list[i].revents = mask; + silc_schedule_dispatch_nontimeout(schedule); + } break; } @@ -865,9 +908,13 @@ void silc_schedule_set_listen_fd(SilcSchedule schedule, for (i = 0; i < schedule->max_fd; i++) if (schedule->fd_list[i].events == 0) { schedule->fd_list[i].fd = fd; - schedule->fd_list[i].events = iomask; + schedule->fd_list[i].events = mask; if (i > schedule->last_fd) schedule->last_fd = i; + if (send_events) { + schedule->fd_list[i].revents = mask; + silc_schedule_dispatch_nontimeout(schedule); + } break; } @@ -936,6 +983,7 @@ static void silc_task_queue_alloc(SilcTaskQueue *queue) static void silc_task_queue_free(SilcTaskQueue queue) { silc_mutex_free(queue->lock); + memset(queue, 'F', sizeof(*queue)); silc_free(queue); } @@ -1035,7 +1083,7 @@ static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first) if (first == prev) break; - if (silc_schedule_task_timeout_compare(&prev->timeout, &task->timeout)) + if (silc_compare_timeval(&prev->timeout, &task->timeout)) task = prev; prev = prev->prev; @@ -1068,13 +1116,11 @@ static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask, /* If we have longer timeout than with the task head of us we have found our spot. */ - if (silc_schedule_task_timeout_compare(&prev->timeout, - &newtask->timeout)) + if (silc_compare_timeval(&prev->timeout, &newtask->timeout)) break; /* If we are equal size of timeout we will be after it. */ - if (!silc_schedule_task_timeout_compare(&newtask->timeout, - &prev->timeout)) + if (!silc_compare_timeval(&newtask->timeout, &prev->timeout)) break; /* We have shorter timeout, compare to next one. */ @@ -1089,11 +1135,9 @@ static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask, if (prev == task) { /* Check if we are going to be the first task in the queue */ - if (silc_schedule_task_timeout_compare(&prev->timeout, - &newtask->timeout)) + if (silc_compare_timeval(&prev->timeout, &newtask->timeout)) break; - if (!silc_schedule_task_timeout_compare(&newtask->timeout, - &prev->timeout)) + if (!silc_compare_timeval(&newtask->timeout, &prev->timeout)) break; /* We are now the first task in queue */ @@ -1107,13 +1151,11 @@ static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask, /* If we have longer timeout than with the task head of us we have found our spot. */ - if (silc_schedule_task_timeout_compare(&prev->timeout, - &newtask->timeout)) + if (silc_compare_timeval(&prev->timeout, &newtask->timeout)) break; /* If we are equal size of timeout, priority kicks in place. */ - if (!silc_schedule_task_timeout_compare(&newtask->timeout, - &prev->timeout)) + if (!silc_compare_timeval(&newtask->timeout, &prev->timeout)) if (prev->priority >= SILC_TASK_PRI_NORMAL) break; @@ -1129,11 +1171,9 @@ static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask, if (prev == task) { /* Check if we are going to be the first task in the queue */ - if (silc_schedule_task_timeout_compare(&prev->timeout, - &newtask->timeout)) + if (silc_compare_timeval(&prev->timeout, &newtask->timeout)) break; - if (!silc_schedule_task_timeout_compare(&newtask->timeout, - &prev->timeout)) + if (!silc_compare_timeval(&newtask->timeout, &prev->timeout)) if (prev->priority >= SILC_TASK_PRI_NORMAL) break; @@ -1172,10 +1212,11 @@ static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task) next = first; while(1) { - next = next->next; - silc_free(next->prev); - if (next == first) + old = next->next; + silc_free(next); + if (old == first) break; + next = old; } queue->task = NULL; @@ -1211,20 +1252,6 @@ static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task) } } -/* Compare two time values. If the first argument is smaller than the - second this function returns TRUE. */ - -static int silc_schedule_task_timeout_compare(struct timeval *smaller, - struct timeval *bigger) -{ - if ((smaller->tv_sec < bigger->tv_sec) || - ((smaller->tv_sec == bigger->tv_sec) && - (smaller->tv_usec < bigger->tv_usec))) - return TRUE; - - return FALSE; -} - static void silc_task_del_by_fd(SilcTaskQueue queue, SilcUInt32 fd) { SilcTask next;