X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcutil%2Fsilcschedule.c;h=6d3f09e1c3d3794f4776ea5211a4115c1492494c;hb=196824372226561334cd638d6471267cbffb354c;hp=012eca77a28e079603445dc827d70890fb2f5f68;hpb=ec1d42b30ec00fd48dc23d28dbe54374ae0d6a1d;p=silc.git diff --git a/lib/silcutil/silcschedule.c b/lib/silcutil/silcschedule.c index 012eca77..6d3f09e1 100644 --- a/lib/silcutil/silcschedule.c +++ b/lib/silcutil/silcschedule.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1998 - 2006 Pekka Riikonen + Copyright (C) 1998 - 2007 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 @@ -45,43 +45,34 @@ static void silc_schedule_fd_destructor(void *key, void *context, static void silc_schedule_dispatch_fd(SilcSchedule schedule) { - SilcHashTableList htl; - SilcTask t; SilcTaskFd task; - SilcUInt32 fd; + SilcTask t; - silc_hash_table_list(schedule->fd_queue, &htl); - while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) { + /* The dispatch list includes only valid tasks, and tasks that have + something to dispatch. Dispatching is atomic; no matter if another + thread invalidates a task when we unlock, we dispatch to completion. */ + SILC_SCHEDULE_UNLOCK(schedule); + silc_list_start(schedule->fd_dispatch); + while ((task = silc_list_get(schedule->fd_dispatch))) { t = (SilcTask)task; - if (!t->valid) { - silc_schedule_task_remove(schedule, t); - continue; - } - if (!task->events || !task->revents) - continue; - /* Is the task ready for reading */ - if (task->revents & SILC_TASK_READ) { - SILC_SCHEDULE_UNLOCK(schedule); + if (task->revents & SILC_TASK_READ) t->callback(schedule, schedule->app_context, SILC_TASK_READ, task->fd, t->context); - SILC_SCHEDULE_LOCK(schedule); - } /* Is the task ready for writing */ - if (t->valid && task->revents & SILC_TASK_WRITE) { - SILC_SCHEDULE_UNLOCK(schedule); + if (t->valid && task->revents & SILC_TASK_WRITE) t->callback(schedule, schedule->app_context, SILC_TASK_WRITE, task->fd, t->context); - SILC_SCHEDULE_LOCK(schedule); - } - - /* Remove if task was invalidated in the task callback */ - if (!t->valid) - silc_schedule_task_remove(schedule, t); } - silc_hash_table_list_reset(&htl); + SILC_SCHEDULE_LOCK(schedule); + + /* Remove invalidated tasks */ + silc_list_start(schedule->fd_dispatch); + while ((task = silc_list_get(schedule->fd_dispatch))) + if (silc_unlikely(!task->header.valid)) + silc_schedule_task_remove(schedule, (SilcTask)task); } /* Executes all tasks whose timeout has expired. The task is removed from @@ -102,31 +93,35 @@ static void silc_schedule_dispatch_timeout(SilcSchedule schedule, /* First task in the task queue has always the earliest timeout. */ silc_list_start(schedule->timeout_queue); - while ((task = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) { + task = silc_list_get(schedule->timeout_queue); + if (silc_unlikely(!task)) + return; + do { t = (SilcTask)task; /* Remove invalid task */ - if (!t->valid) { + if (silc_unlikely(!t->valid)) { silc_schedule_task_remove(schedule, t); continue; } /* Execute the task if the timeout has expired */ - if (dispatch_all || silc_compare_timeval(&task->timeout, &curtime)) { - t->valid = FALSE; - SILC_SCHEDULE_UNLOCK(schedule); - t->callback(schedule, schedule->app_context, SILC_TASK_EXPIRE, 0, - t->context); - SILC_SCHEDULE_LOCK(schedule); + if (silc_compare_timeval(&task->timeout, &curtime) > 0 && !dispatch_all) + break; - /* Remove the expired task */ - silc_schedule_task_remove(schedule, t); + t->valid = FALSE; + SILC_SCHEDULE_UNLOCK(schedule); + t->callback(schedule, schedule->app_context, SILC_TASK_EXPIRE, 0, + t->context); + SILC_SCHEDULE_LOCK(schedule); - /* Balance when we have lots of small timeouts */ - if ((++count) > 50) - break; - } - } + /* Remove the expired task */ + silc_schedule_task_remove(schedule, t); + + /* Balance when we have lots of small timeouts */ + if (silc_unlikely((++count) > 40)) + break; + } while (silc_likely((task = silc_list_get(schedule->timeout_queue)))); } /* Calculates next timeout. This is the timeout value when at earliest some @@ -146,20 +141,23 @@ static void silc_schedule_select_timeout(SilcSchedule schedule) /* First task in the task queue has always the earliest timeout. */ silc_list_start(schedule->timeout_queue); - while ((task = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) { + task = silc_list_get(schedule->timeout_queue); + if (silc_unlikely(!task)) + return; + do { t = (SilcTask)task; /* Remove invalid task */ - if (!t->valid) { + if (silc_unlikely(!t->valid)) { silc_schedule_task_remove(schedule, t); continue; } /* If the timeout is in past, we will run the task and all other timeout tasks from the past. */ - if (silc_compare_timeval(&task->timeout, &curtime) && dispatch) { + if (silc_compare_timeval(&task->timeout, &curtime) <= 0 && dispatch) { silc_schedule_dispatch_timeout(schedule, FALSE); - if (!schedule->valid) + if (silc_unlikely(!schedule->valid)) return; /* Start selecting new timeout again after dispatch */ @@ -181,13 +179,13 @@ static void silc_schedule_select_timeout(SilcSchedule schedule) curtime.tv_sec = 0; curtime.tv_usec += 1000000L; } - break; - } + } while ((task = silc_list_get(schedule->timeout_queue))); /* Save the timeout */ if (task) { schedule->timeout = curtime; + schedule->has_timeout = TRUE; SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout.tv_sec, schedule->timeout.tv_usec)); } @@ -199,16 +197,15 @@ static void silc_schedule_select_timeout(SilcSchedule schedule) static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task) { SilcTaskFd ftask; - SilcTaskTimeout ttask; - if (task == SILC_ALL_TASKS) { + if (silc_unlikely(task == SILC_ALL_TASKS)) { SilcTask task; SilcHashTableList htl; SilcUInt32 fd; /* Delete from fd queue */ silc_hash_table_list(schedule->fd_queue, &htl); - while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) + while (silc_hash_table_get(&htl, (void *)&fd, (void *)&task)) silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd)); silc_hash_table_list_reset(&htl); @@ -222,27 +219,17 @@ static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task) return; } - /* Delete from timeout queue */ - if (task->type == 1) { - silc_list_start(schedule->timeout_queue); - while ((ttask = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) { - if (ttask == (SilcTaskTimeout)task) { - silc_list_del(schedule->timeout_queue, ttask); - - /* Put to free list */ - silc_list_add(schedule->free_tasks, ttask); - if (silc_list_count(schedule->free_tasks) == 1) - silc_list_start(schedule->free_tasks); - break; - } - } + if (silc_likely(task->type == 1)) { + /* Delete from timeout queue */ + silc_list_del(schedule->timeout_queue, task); - return; + /* Put to free list */ + silc_list_add(schedule->free_tasks, task); + } else { + /* Delete from fd queue */ + ftask = (SilcTaskFd)task; + silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd)); } - - /* Delete from fd queue */ - ftask = (SilcTaskFd)task; - silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd)); } /* Timeout freelist garbage collection */ @@ -338,11 +325,13 @@ SilcSchedule silc_schedule_init(int max_tasks, void *app_context) schedule->fd_queue = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL, silc_schedule_fd_destructor, NULL, TRUE); - if (!schedule->fd_queue) + if (!schedule->fd_queue) { + silc_free(schedule); return NULL; + } - silc_list_init(schedule->timeout_queue, struct SilcTaskTimeoutStruct, next); - silc_list_init(schedule->free_tasks, struct SilcTaskTimeoutStruct, next); + silc_list_init(schedule->timeout_queue, struct SilcTaskStruct, next); + silc_list_init(schedule->free_tasks, struct SilcTaskStruct, next); schedule->app_context = app_context; schedule->valid = TRUE; @@ -353,6 +342,12 @@ SilcSchedule silc_schedule_init(int max_tasks, void *app_context) /* Initialize the platform specific scheduler. */ schedule->internal = schedule_ops.init(schedule, app_context); + if (!schedule->internal) { + silc_hash_table_free(schedule->fd_queue); + silc_mutex_free(schedule->lock); + silc_free(schedule); + return NULL; + } /* Timeout freelist garbage collection */ silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc, @@ -419,107 +414,107 @@ void silc_schedule_stop(SilcSchedule schedule) SILC_SCHEDULE_UNLOCK(schedule); } -/* Runs the scheduler once and then returns. */ +/* Runs the scheduler once and then returns. Must be called locked. */ -SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs) +static SilcBool silc_schedule_iterate(SilcSchedule schedule, int timeout_usecs) { struct timeval timeout; int ret; - SILC_LOG_DEBUG(("In scheduler loop")); + do { + SILC_LOG_DEBUG(("In scheduler loop")); - if (!schedule->is_locked) - SILC_SCHEDULE_LOCK(schedule); + /* Deliver signals if any has been set to be called */ + if (silc_unlikely(schedule->signal_tasks)) { + SILC_SCHEDULE_UNLOCK(schedule); + schedule_ops.signals_call(schedule, schedule->internal); + schedule->signal_tasks = FALSE; + SILC_SCHEDULE_LOCK(schedule); + } - /* Deliver signals if any has been set to be called */ - if (schedule->signal_tasks) { - SILC_SCHEDULE_UNLOCK(schedule); - schedule_ops.signals_call(schedule, schedule->internal); - schedule->signal_tasks = FALSE; - SILC_SCHEDULE_LOCK(schedule); - } + /* Check if scheduler is valid */ + if (silc_unlikely(schedule->valid == FALSE)) { + SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting")); + return FALSE; + } - /* Check if scheduler is valid */ - if (schedule->valid == FALSE) { - SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting")); - if (!schedule->is_locked) - SILC_SCHEDULE_UNLOCK(schedule); - return FALSE; - } + /* Calculate next timeout for silc_select(). This is the timeout value + when at earliest some of the timeout tasks expire. This may dispatch + already expired timeouts. */ + silc_schedule_select_timeout(schedule); - /* Calculate next timeout for silc_select(). This is the timeout value - when at earliest some of the timeout tasks expire. This may dispatch - already expired timeouts. */ - silc_schedule_select_timeout(schedule); + /* Check if scheduler is valid */ + if (silc_unlikely(schedule->valid == FALSE)) { + SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting")); + return FALSE; + } - /* Check if scheduler is valid */ - if (schedule->valid == FALSE) { - SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting")); - if (!schedule->is_locked) - SILC_SCHEDULE_UNLOCK(schedule); - return FALSE; - } + if (timeout_usecs >= 0) { + timeout.tv_sec = 0; + timeout.tv_usec = timeout_usecs; + schedule->timeout = timeout; + schedule->has_timeout = TRUE; + } - if (timeout_usecs >= 0) { - timeout.tv_sec = 0; - timeout.tv_usec = timeout_usecs; - schedule->timeout = timeout; - schedule->has_timeout = TRUE; - } + /* This is the main silc_select(). The program blocks here until some + of the selected file descriptors change status or the selected + timeout expires. */ + SILC_LOG_DEBUG(("Select")); + ret = schedule_ops.schedule(schedule, schedule->internal); + + if (silc_likely(ret == 0)) { + /* Timeout */ + SILC_LOG_DEBUG(("Running timeout tasks")); + if (silc_likely(silc_list_count(schedule->timeout_queue))) + silc_schedule_dispatch_timeout(schedule, FALSE); + continue; - /* This is the main silc_select(). The program blocks here until some - of the selected file descriptors change status or the selected - timeout expires. */ - SILC_LOG_DEBUG(("Select")); - ret = schedule_ops.select(schedule, schedule->internal); - - switch (ret) { - case 0: - /* Timeout */ - SILC_LOG_DEBUG(("Running timeout tasks")); - silc_schedule_dispatch_timeout(schedule, FALSE); - break; - case -1: - /* Error */ - if (errno == EINTR) - break; - SILC_LOG_ERROR(("Error in select(): %s", strerror(errno))); - break; - default: - /* There is some data available now */ - SILC_LOG_DEBUG(("Running fd tasks")); - silc_schedule_dispatch_fd(schedule); - break; - } + } else if (silc_likely(ret > 0)) { + /* There is some data available now */ + SILC_LOG_DEBUG(("Running fd tasks")); + silc_schedule_dispatch_fd(schedule); + continue; - if (!schedule->is_locked) - SILC_SCHEDULE_UNLOCK(schedule); + } else { + /* Error or special case handling */ + if (errno == EINTR) + continue; + if (ret == -2) + break; + + SILC_LOG_ERROR(("Error in select()/poll(): %s", strerror(errno))); + continue; + } + } while (timeout_usecs == -1); return TRUE; } -/* The SILC scheduler. This is actually the main routine in SILC programs. - When this returns the program is to be ended. Before this function can - be called, one must call silc_schedule_init function. */ +/* Runs the scheduler once and then returns. */ -void silc_schedule(SilcSchedule schedule) +SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs) { - SILC_LOG_DEBUG(("Running scheduler")); + SilcBool ret; + SILC_SCHEDULE_LOCK(schedule); + ret = silc_schedule_iterate(schedule, timeout_usecs); + SILC_SCHEDULE_UNLOCK(schedule); + return ret; +} - if (schedule->valid == FALSE) { - SILC_LOG_ERROR(("Scheduler is not valid, stopping")); - return; - } +/* Runs the scheduler and blocks here. When this returns the scheduler + has ended. */ - SILC_SCHEDULE_LOCK(schedule); - schedule->is_locked = TRUE; +#ifndef SILC_SYMBIAN +void silc_schedule(SilcSchedule schedule) +{ + SILC_LOG_DEBUG(("Running scheduler")); /* Start the scheduler loop */ - while (silc_schedule_one(schedule, -1)) - ; - + SILC_SCHEDULE_LOCK(schedule); + silc_schedule_iterate(schedule, -1); SILC_SCHEDULE_UNLOCK(schedule); } +#endif /* !SILC_SYMBIAN */ /* Wakes up the scheduler. This is used only in multi-threaded environments where threads may add new tasks or remove old tasks @@ -557,21 +552,23 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, { SilcTask task = NULL; - if (!schedule->valid) + if (silc_unlikely(!schedule->valid)) return NULL; SILC_SCHEDULE_LOCK(schedule); - if (type == SILC_TASK_TIMEOUT) { + if (silc_likely(type == SILC_TASK_TIMEOUT)) { SilcTaskTimeout tmp, prev, ttask; + SilcList list; + silc_list_start(schedule->free_tasks); ttask = silc_list_get(schedule->free_tasks); - if (!ttask) { + if (silc_unlikely(!ttask)) { ttask = silc_calloc(1, sizeof(*ttask)); - if (!ttask) + if (silc_unlikely(!ttask)) goto out; - } - silc_list_del(schedule->free_tasks, ttask); + } else + silc_list_del(schedule->free_tasks, ttask); ttask->header.type = 1; ttask->header.callback = callback; @@ -579,8 +576,8 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, ttask->header.valid = TRUE; /* Add timeout */ + silc_gettimeofday(&ttask->timeout); if ((seconds + useconds) > 0) { - silc_gettimeofday(&ttask->timeout); ttask->timeout.tv_sec += seconds + (useconds / 1000000L); ttask->timeout.tv_usec += (useconds % 1000000L); if (ttask->timeout.tv_usec >= 1000000L) { @@ -594,11 +591,12 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, /* Add task to correct spot so that the first task in the list has the earliest timeout. */ - silc_list_start(schedule->timeout_queue); + list = schedule->timeout_queue; + silc_list_start(list); prev = NULL; - while ((tmp = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) { + while ((tmp = silc_list_get(list)) != SILC_LIST_END) { /* If we have shorter timeout, we have found our spot */ - if (silc_compare_timeval(&ttask->timeout, &tmp->timeout)) { + if (silc_compare_timeval(&ttask->timeout, &tmp->timeout) < 0) { silc_list_insert(schedule->timeout_queue, prev, ttask); break; } @@ -609,22 +607,34 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, task = (SilcTask)ttask; - } else if (type == SILC_TASK_FD) { + } else if (silc_likely(type == SILC_TASK_FD)) { + SilcTaskFd ftask; + /* Check if fd is already added */ - if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd), - NULL, (void **)&task)) - goto out; + if (silc_unlikely(silc_hash_table_find(schedule->fd_queue, + SILC_32_TO_PTR(fd), + NULL, (void *)&task))) { + if (task->valid) + goto out; + + /* Remove invalid task. We must have unique fd key to hash table. */ + silc_schedule_task_remove(schedule, task); + } /* Check max tasks */ - if (schedule->max_tasks > 0 && - silc_hash_table_count(schedule->fd_queue) >= schedule->max_tasks) { + if (silc_unlikely(schedule->max_tasks > 0 && + silc_hash_table_count(schedule->fd_queue) >= + schedule->max_tasks)) { SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task")); + task = NULL; goto out; } - SilcTaskFd ftask = silc_calloc(1, sizeof(*ftask)); - if (!ftask) + ftask = silc_calloc(1, sizeof(*ftask)); + if (silc_unlikely(!ftask)) { + task = NULL; goto out; + } SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd)); @@ -635,29 +645,47 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, ftask->events = SILC_TASK_READ; ftask->fd = fd; - /* Add task */ - silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask); + /* Add task and schedule it */ + if (!silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask)) { + silc_free(ftask); + task = NULL; + goto out; + } + if (!schedule_ops.schedule_fd(schedule, schedule->internal, + ftask, ftask->events)) { + silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd)); + task = NULL; + goto out; + } task = (SilcTask)ftask; - } else if (type == SILC_TASK_SIGNAL) { + } else if (silc_unlikely(type == SILC_TASK_SIGNAL)) { SILC_SCHEDULE_UNLOCK(schedule); - schedule_ops.signal_register(schedule, schedule->internal, (int)fd, + schedule_ops.signal_register(schedule, schedule->internal, fd, callback, context); return NULL; } - out: SILC_SCHEDULE_UNLOCK(schedule); + +#ifdef SILC_SYMBIAN + /* On symbian we wakeup scheduler immediately after adding timeout task + in case the task is added outside the scheduler loop (in some active + object). */ + if (task && task->type == 1) + silc_schedule_wakeup(schedule); +#endif /* SILC_SYMBIAN */ + return task; } /* Invalidates task */ -void silc_schedule_task_del(SilcSchedule schedule, SilcTask task) +SilcBool silc_schedule_task_del(SilcSchedule schedule, SilcTask task) { - if (task == SILC_ALL_TASKS) { + if (silc_unlikely(task == SILC_ALL_TASKS)) { SilcHashTableList htl; SILC_LOG_DEBUG(("Unregister all tasks")); @@ -666,7 +694,7 @@ void silc_schedule_task_del(SilcSchedule schedule, SilcTask task) /* Delete from fd queue */ silc_hash_table_list(schedule->fd_queue, &htl); - while (silc_hash_table_get(&htl, NULL, (void **)&task)) + while (silc_hash_table_get(&htl, NULL, (void *)&task)) task->valid = FALSE; silc_hash_table_list_reset(&htl); @@ -677,44 +705,57 @@ void silc_schedule_task_del(SilcSchedule schedule, SilcTask task) task->valid = FALSE; SILC_SCHEDULE_UNLOCK(schedule); - return; + return TRUE; } SILC_LOG_DEBUG(("Unregistering task %p", task)); SILC_SCHEDULE_LOCK(schedule); task->valid = FALSE; SILC_SCHEDULE_UNLOCK(schedule); + + return TRUE; } /* Invalidate task by fd */ -void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd) +SilcBool silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd) { SilcTask task = NULL; + SilcBool ret = FALSE; SILC_LOG_DEBUG(("Unregister task by fd %d", fd)); SILC_SCHEDULE_LOCK(schedule); /* fd is unique, so there is only one task with this fd in the table */ - if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd), NULL, - (void **)&task)) + if (silc_likely(silc_hash_table_find(schedule->fd_queue, + SILC_32_TO_PTR(fd), NULL, + (void *)&task))) { + SILC_LOG_DEBUG(("Deleting task %p", task)); task->valid = FALSE; + ret = TRUE; + } SILC_SCHEDULE_UNLOCK(schedule); /* If it is signal, remove it */ - if (!task) + if (silc_unlikely(!task)) { schedule_ops.signal_unregister(schedule, schedule->internal, fd); + ret = TRUE; + } + + return ret; } /* Invalidate task by task callback. */ -void silc_schedule_task_del_by_callback(SilcSchedule schedule, - SilcTaskCallback callback) +SilcBool silc_schedule_task_del_by_callback(SilcSchedule schedule, + SilcTaskCallback callback) { SilcTask task; SilcHashTableList htl; + SilcList list; + SilcBool ret = FALSE; SILC_LOG_DEBUG(("Unregister task by callback")); @@ -722,29 +763,38 @@ void silc_schedule_task_del_by_callback(SilcSchedule schedule, /* Delete from fd queue */ silc_hash_table_list(schedule->fd_queue, &htl); - while (silc_hash_table_get(&htl, NULL, (void **)&task)) { - if (task->callback == callback) + while (silc_hash_table_get(&htl, NULL, (void *)&task)) { + if (task->callback == callback) { task->valid = FALSE; + ret = TRUE; + } } silc_hash_table_list_reset(&htl); /* Delete from timeout queue */ - silc_list_start(schedule->timeout_queue); - while ((task = (SilcTask)silc_list_get(schedule->timeout_queue)) - != SILC_LIST_END) { - if (task->callback == callback) + list = schedule->timeout_queue; + silc_list_start(list); + while ((task = (SilcTask)silc_list_get(list))) { + if (task->callback == callback) { task->valid = FALSE; + ret = TRUE; + } } SILC_SCHEDULE_UNLOCK(schedule); + + return ret; } /* Invalidate task by context. */ -void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context) +SilcBool silc_schedule_task_del_by_context(SilcSchedule schedule, + void *context) { SilcTask task; SilcHashTableList htl; + SilcList list; + SilcBool ret = FALSE; SILC_LOG_DEBUG(("Unregister task by context")); @@ -752,73 +802,112 @@ void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context) /* Delete from fd queue */ silc_hash_table_list(schedule->fd_queue, &htl); - while (silc_hash_table_get(&htl, NULL, (void **)&task)) { - if (task->context == context) + while (silc_hash_table_get(&htl, NULL, (void *)&task)) { + if (task->context == context) { task->valid = FALSE; + ret = TRUE; + } } silc_hash_table_list_reset(&htl); /* Delete from timeout queue */ - silc_list_start(schedule->timeout_queue); - while ((task = (SilcTask)silc_list_get(schedule->timeout_queue)) - != SILC_LIST_END) { - if (task->context == context) + list = schedule->timeout_queue; + silc_list_start(list); + while ((task = (SilcTask)silc_list_get(list))) { + if (task->context == context) { + ret = TRUE; task->valid = FALSE; + } } SILC_SCHEDULE_UNLOCK(schedule); + + return ret; } /* Invalidate task by all */ -void silc_schedule_task_del_by_all(SilcSchedule schedule, int fd, - SilcTaskCallback callback, void *context) +SilcBool silc_schedule_task_del_by_all(SilcSchedule schedule, int fd, + SilcTaskCallback callback, + void *context) { SilcTask task; + SilcList list; + SilcBool ret = FALSE; SILC_LOG_DEBUG(("Unregister task by fd, callback and context")); /* For fd task, callback and context is irrelevant as fd is unique */ if (fd) - silc_schedule_task_del_by_fd(schedule, fd); + return silc_schedule_task_del_by_fd(schedule, fd); SILC_SCHEDULE_LOCK(schedule); /* Delete from timeout queue */ - silc_list_start(schedule->timeout_queue); - while ((task = (SilcTask)silc_list_get(schedule->timeout_queue)) - != SILC_LIST_END) { - if (task->callback == callback && task->context == context) + list = schedule->timeout_queue; + silc_list_start(list); + while ((task = (SilcTask)silc_list_get(list))) { + if (task->callback == callback && task->context == context) { task->valid = FALSE; + ret = TRUE; + } } SILC_SCHEDULE_UNLOCK(schedule); + + return TRUE; } /* Sets a file descriptor to be listened by scheduler. One can 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 mask, SilcBool send_events) +SilcBool silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd, + SilcTaskEvent mask, SilcBool send_events) { SilcTaskFd task; - if (!schedule->valid) - return; + if (silc_unlikely(!schedule->valid)) + return FALSE; SILC_SCHEDULE_LOCK(schedule); if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd), - NULL, (void **)&task)) { + NULL, (void *)&task)) { + if (!schedule_ops.schedule_fd(schedule, schedule->internal, task, mask)) { + SILC_SCHEDULE_UNLOCK(schedule); + return FALSE; + } task->events = mask; - if (send_events) { + if (silc_unlikely(send_events) && mask) { task->revents = mask; silc_schedule_dispatch_fd(schedule); } } SILC_SCHEDULE_UNLOCK(schedule); + + return TRUE; +} + +/* Returns the file descriptor's current requested event mask. */ + +SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule, + SilcUInt32 fd) +{ + SilcTaskFd task; + SilcTaskEvent event = 0; + + if (silc_unlikely(!schedule->valid)) + return 0; + + SILC_SCHEDULE_LOCK(schedule); + if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd), + NULL, (void *)&task)) + event = task->events; + SILC_SCHEDULE_UNLOCK(schedule); + + return event; } /* Removes a file descriptor from listen list. */