X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcutil%2Fsilcschedule.c;h=53cb0aa7426f22eb883c7fc5ee73fc427a9db48f;hb=52e57c880aba9c5e89f59d962eb9af75670b76e0;hp=aa648e3908eb5bb58be946ef27ae4d846fddf1ff;hpb=0162cc800a7ca6f21bffe5f4c66f3c8ee357b564;p=silc.git diff --git a/lib/silcutil/silcschedule.c b/lib/silcutil/silcschedule.c index aa648e39..53cb0aa7 100644 --- a/lib/silcutil/silcschedule.c +++ b/lib/silcutil/silcschedule.c @@ -106,7 +106,7 @@ static void silc_schedule_dispatch_timeout(SilcSchedule schedule, } /* Execute the task if the timeout has expired */ - if (!silc_compare_timeval(&task->timeout, &curtime) && !dispatch_all) + if (silc_compare_timeval(&task->timeout, &curtime) > 0 && !dispatch_all) break; t->valid = FALSE; @@ -155,7 +155,7 @@ static void silc_schedule_select_timeout(SilcSchedule schedule) /* 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 (silc_unlikely(!schedule->valid)) return; @@ -205,7 +205,7 @@ static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task) /* 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); @@ -325,8 +325,10 @@ 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 SilcTaskStruct, next); silc_list_init(schedule->free_tasks, struct SilcTaskStruct, next); @@ -340,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, @@ -465,12 +473,20 @@ static SilcBool silc_schedule_iterate(SilcSchedule schedule, int timeout_usecs) /* There is some data available now */ SILC_LOG_DEBUG(("Running fd tasks")); silc_schedule_dispatch_fd(schedule); + + /* If timeout was very short, dispatch also timeout tasks */ + if (schedule->has_timeout && schedule->timeout.tv_sec == 0 && + schedule->timeout.tv_usec < 50000) + silc_schedule_dispatch_timeout(schedule, FALSE); continue; } else { - /* Error */ - if (silc_likely(errno == EINTR)) + /* Error or special case handling */ + if (errno == EINTR) continue; + if (ret == -2) + break; + SILC_LOG_ERROR(("Error in select()/poll(): %s", strerror(errno))); continue; } @@ -493,6 +509,7 @@ SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs) /* Runs the scheduler and blocks here. When this returns the scheduler has ended. */ +#ifndef SILC_SYMBIAN void silc_schedule(SilcSchedule schedule) { SILC_LOG_DEBUG(("Running scheduler")); @@ -502,6 +519,7 @@ void silc_schedule(SilcSchedule 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 @@ -530,6 +548,15 @@ void *silc_schedule_get_context(SilcSchedule schedule) return schedule->app_context; } +/* Set notify callback */ + +void silc_schedule_set_notify(SilcSchedule schedule, + SilcTaskNotifyCb notify, void *context) +{ + schedule->notify = notify; + schedule->notify_context = context; +} + /* Add new task to the scheduler */ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, @@ -554,8 +581,8 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, ttask = silc_calloc(1, sizeof(*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; @@ -583,7 +610,7 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, prev = NULL; 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; } @@ -594,26 +621,39 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, task = (SilcTask)ttask; + /* Call notify callback */ + if (schedule->notify) + schedule->notify(schedule, TRUE, task, FALSE, 0, 0, seconds, useconds, + schedule->notify_context); + } else if (silc_likely(type == SILC_TASK_FD)) { SilcTaskFd ftask; /* Check if fd is already added */ if (silc_unlikely(silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd), - NULL, (void **)&task))) - goto out; + 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 (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; } ftask = silc_calloc(1, sizeof(*ftask)); - if (silc_unlikely(!ftask)) + if (silc_unlikely(!ftask)) { + task = NULL; goto out; + } SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd)); @@ -625,12 +665,25 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, ftask->fd = fd; /* Add task and schedule it */ - silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask); - schedule_ops.schedule_fd(schedule, schedule->internal, ftask, - ftask->events); + 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; + /* Call notify callback */ + if (schedule->notify) + schedule->notify(schedule, TRUE, task, TRUE, ftask->fd, + SILC_TASK_READ, 0, 0, schedule->notify_context); + } else if (silc_unlikely(type == SILC_TASK_SIGNAL)) { SILC_SCHEDULE_UNLOCK(schedule); schedule_ops.signal_register(schedule, schedule->internal, fd, @@ -640,12 +693,21 @@ SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd, 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 (silc_unlikely(task == SILC_ALL_TASKS)) { SilcHashTableList htl; @@ -656,31 +718,51 @@ 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; + + /* Call notify callback */ + if (schedule->notify) + schedule->notify(schedule, FALSE, task, TRUE, + ((SilcTaskFd)task)->fd, 0, 0, 0, + schedule->notify_context); + } 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) + while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))) { task->valid = FALSE; + /* Call notify callback */ + if (schedule->notify) + schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0, + schedule->notify_context); + } + SILC_SCHEDULE_UNLOCK(schedule); - return; + return TRUE; } SILC_LOG_DEBUG(("Unregistering task %p", task)); SILC_SCHEDULE_LOCK(schedule); task->valid = FALSE; + + /* Call notify callback */ + if (schedule->notify) + schedule->notify(schedule, FALSE, task, !task->type, 0, 0, 0, 0, + schedule->notify_context); 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)); @@ -689,24 +771,37 @@ void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd) /* fd is unique, so there is only one task with this fd in the table */ if (silc_likely(silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd), NULL, - (void **)&task))) + (void *)&task))) { + SILC_LOG_DEBUG(("Deleting task %p", task)); task->valid = FALSE; + /* Call notify callback */ + if (schedule->notify) + schedule->notify(schedule, FALSE, task, TRUE, fd, 0, 0, 0, + schedule->notify_context); + ret = TRUE; + } + SILC_SCHEDULE_UNLOCK(schedule); /* If it is signal, remove it */ - if (silc_unlikely(!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")); @@ -714,9 +809,17 @@ 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; + + /* Call notify callback */ + if (schedule->notify) + schedule->notify(schedule, FALSE, task, TRUE, + ((SilcTaskFd)task)->fd, 0, 0, 0, + schedule->notify_context); + ret = TRUE; + } } silc_hash_table_list_reset(&htl); @@ -724,20 +827,31 @@ void silc_schedule_task_del_by_callback(SilcSchedule schedule, list = schedule->timeout_queue; silc_list_start(list); while ((task = (SilcTask)silc_list_get(list))) { - if (task->callback == callback) + if (task->callback == callback) { task->valid = FALSE; + + /* Call notify callback */ + if (schedule->notify) + schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0, + schedule->notify_context); + 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")); @@ -745,9 +859,17 @@ 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; + + /* Call notify callback */ + if (schedule->notify) + schedule->notify(schedule, FALSE, task, TRUE, + ((SilcTaskFd)task)->fd, 0, 0, 0, + schedule->notify_context); + ret = TRUE; + } } silc_hash_table_list_reset(&htl); @@ -755,26 +877,37 @@ void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context) list = schedule->timeout_queue; silc_list_start(list); while ((task = (SilcTask)silc_list_get(list))) { - if (task->context == context) + if (task->context == context) { task->valid = FALSE; + + /* Call notify callback */ + if (schedule->notify) + schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0, + schedule->notify_context); + ret = TRUE; + } } 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); @@ -782,41 +915,61 @@ void silc_schedule_task_del_by_all(SilcSchedule schedule, int fd, list = schedule->timeout_queue; silc_list_start(list); while ((task = (SilcTask)silc_list_get(list))) { - if (task->callback == callback && task->context == context) + if (task->callback == callback && task->context == context) { task->valid = FALSE; + + /* Call notify callback */ + if (schedule->notify) + schedule->notify(schedule, FALSE, task, FALSE, 0, 0, 0, 0, + schedule->notify_context); + 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 (silc_unlikely(!schedule->valid)) - return; + 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; - schedule_ops.schedule_fd(schedule, schedule->internal, task, mask); if (silc_unlikely(send_events) && mask) { task->revents = mask; silc_schedule_dispatch_fd(schedule); } + + /* Call notify callback */ + if (schedule->notify) + schedule->notify(schedule, TRUE, (SilcTask)task, + TRUE, task->fd, mask, 0, 0, + schedule->notify_context); } SILC_SCHEDULE_UNLOCK(schedule); + + return TRUE; } -/* Returns the file descriptors current requested event mask. */ +/* Returns the file descriptor's current requested event mask. */ SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule, SilcUInt32 fd) @@ -829,7 +982,7 @@ SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule, SILC_SCHEDULE_LOCK(schedule); if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd), - NULL, (void **)&task)) + NULL, (void *)&task)) event = task->events; SILC_SCHEDULE_UNLOCK(schedule);