X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcutil%2Fsilcschedule.c;h=5ca563035b4aa6b3008d82a48e04da47628873ac;hb=413da0f8686910f5e627393157566ae729ca99c4;hp=cb4a9693c5f23d829b6fc72a07fba25905a90dde;hpb=a2e242bd81d3cf90145036fcb96b3fd002801961;p=silc.git diff --git a/lib/silcutil/silcschedule.c b/lib/silcutil/silcschedule.c index cb4a9693..5ca56303 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); @@ -45,11 +44,22 @@ void silc_schedule_internal_wakeup(void *context); /* Register signal */ void silc_schedule_internal_signal_register(void *context, - SilcUInt32 signal); + SilcUInt32 signal, + SilcTaskCallback callback, + void *callback_context); /* Unregister signal */ void silc_schedule_internal_signal_unregister(void *context, - SilcUInt32 signal); + SilcUInt32 signal, + SilcTaskCallback callback, + void *callback_context); + +/* Mark signal to be called later. */ +void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal); + +/* Call all signals */ +void silc_schedule_internal_signals_call(void *context, + SilcSchedule schedule); /* Block registered signals in scheduler. */ void silc_schedule_internal_signals_block(void *context); @@ -59,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); @@ -68,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); @@ -98,12 +108,12 @@ do { \ /* SILC Task object. Represents one task in the scheduler. */ struct SilcTaskStruct { SilcUInt32 fd; - struct timeval timeout; - SilcTaskCallback callback; - void *context; - bool valid; - SilcTaskPriority priority; - SilcTaskType type; + SilcTaskCallback callback; /* Task callback */ + void *context; /* Task callback context */ + struct timeval timeout; /* Set for timeout tasks */ + unsigned int valid : 1; /* Set when task is valid */ + unsigned int priority : 2; /* Priority of the task */ + unsigned int type : 5; /* Type of the task */ /* Pointers forming doubly linked circular list */ struct SilcTaskStruct *next; @@ -190,8 +200,14 @@ struct SilcTaskQueueStruct { Scheduler lock. + bool signal_tasks + + TRUE when tasks has been registered from signals. Next round in + scheduler will call the callbacks when this is TRUE. + */ struct SilcScheduleStruct { + void *app_context; /* Application specific context */ SilcTaskQueue fd_queue; SilcTaskQueue timeout_queue; SilcTaskQueue generic_queue; @@ -203,14 +219,16 @@ struct SilcScheduleStruct { void *internal; SILC_MUTEX_DEFINE(lock); bool is_locked; + bool signal_tasks; }; /* 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; @@ -232,15 +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); -#ifdef SILC_UNIX - silc_schedule_signal_register(schedule, SIGALRM); -#endif + schedule->internal = silc_schedule_internal_init(schedule, app_context); return schedule; } @@ -257,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); @@ -273,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; } @@ -312,18 +342,19 @@ 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; + SilcUInt16 revents; for (i = 0; i <= last_fd; i++) { if (schedule->fd_list[i].events == 0) continue; - fd = schedule->fd_list[i].fd; - /* First check whether this fd has task in the fd queue */ silc_mutex_lock(schedule->fd_queue->lock); + fd = schedule->fd_list[i].fd; task = silc_task_find(schedule->fd_queue, fd); + revents = schedule->fd_list[i].revents; /* If the task was found then execute its callbacks. If not then execute all generic tasks for that fd. */ @@ -333,19 +364,21 @@ static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule) in the callback function, ie. it is not valid anymore. */ /* Is the task ready for reading */ - if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) { + if (task->valid && 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); } /* Is the task ready for writing */ - if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) { + if (task->valid && 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); } @@ -367,24 +400,28 @@ static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule) task = schedule->generic_queue->task; while(1) { - /* Validity of the task is checked always before and after + /* Validity of the task and fd is checked always before and after execution beacuse the task might have been unregistered in the callback function, ie. it is not valid anymore. */ /* Is the task ready for reading */ - if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) { + if (task->valid && revents & SILC_TASK_READ && + fd == schedule->fd_list[i].fd) { 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); } /* Is the task ready for writing */ - if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) { + if (task->valid && revents & SILC_TASK_WRITE && + fd == schedule->fd_list[i].fd) { 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); } @@ -422,7 +459,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; @@ -440,11 +478,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); } @@ -493,9 +533,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; @@ -549,6 +589,14 @@ bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs) if (!schedule->is_locked) SILC_SCHEDULE_LOCK(schedule); + /* Deliver signals if any has been set to be called */ + if (schedule->signal_tasks) { + SILC_SCHEDULE_UNLOCK(schedule); + silc_schedule_internal_signals_call(schedule->internal, schedule); + schedule->signal_tasks = FALSE; + SILC_SCHEDULE_LOCK(schedule); + } + /* If the task queues aren't initialized or we aren't valid anymore we will return */ if ((!schedule->fd_queue && !schedule->timeout_queue @@ -592,7 +640,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: @@ -648,6 +696,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, @@ -660,8 +718,8 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, SilcTaskQueue queue; int timeout = FALSE; - SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", fd, - type, priority)); + if (!schedule->valid) + return NULL; queue = SILC_SCHEDULE_GET_QUEUE(type); @@ -671,6 +729,9 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, if (type == SILC_TASK_GENERIC) { silc_mutex_lock(queue->lock); + SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", fd, + type, priority)); + if (queue->task) { SilcTask task = queue->task; while(1) { @@ -681,7 +742,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; } @@ -696,6 +757,12 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, } newtask = silc_calloc(1, sizeof(*newtask)); + if (!newtask) + return NULL; + + SILC_LOG_DEBUG(("Registering new task %p, fd=%d type=%d priority=%d", + newtask, fd, type, priority)); + newtask->fd = fd; newtask->context = context; newtask->callback = callback; @@ -720,7 +787,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); @@ -821,21 +888,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; } @@ -843,9 +917,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; } @@ -876,16 +954,29 @@ void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd) /* Register a new signal */ -void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal) +void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal, + SilcTaskCallback callback, void *context) { - silc_schedule_internal_signal_register(schedule->internal, signal); + silc_schedule_internal_signal_register(schedule->internal, signal, + callback, context); } /* Unregister a new signal */ -void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal) +void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal, + SilcTaskCallback callback, void *context) +{ + silc_schedule_internal_signal_unregister(schedule->internal, signal, + callback, context); +} + +/* Call signal indicated by `signal'. */ + +void silc_schedule_signal_call(SilcSchedule schedule, SilcUInt32 signal) { - silc_schedule_internal_signal_unregister(schedule->internal, signal); + /* Mark that signals needs to be delivered later. */ + silc_schedule_internal_signal_call(schedule->internal, signal); + schedule->signal_tasks = TRUE; } /* Allocates a newtask task queue into the scheduler */ @@ -901,6 +992,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); } @@ -1000,7 +1092,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; @@ -1033,13 +1125,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. */ @@ -1054,11 +1144,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 */ @@ -1072,13 +1160,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; @@ -1094,11 +1180,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; @@ -1137,17 +1221,18 @@ 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; return TRUE; } - SILC_LOG_DEBUG(("Removing task")); + SILC_LOG_DEBUG(("Removing task %p", task)); /* Unregister the task */ old = first; @@ -1176,20 +1261,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;