Author: Pekka Riikonen <priikone@silcnet.org>
- 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
/************************** Types and definitions ***************************/
+/* Connected event context */
+typedef struct SilcScheduleEventConnectionStruct {
+ SilcSchedule schedule;
+ SilcTaskEventCallback callback;
+ void *context;
+ struct SilcScheduleEventConnectionStruct *next;
+} *SilcScheduleEventConnection;
+
/* Platform specific implementation */
extern const SilcScheduleOps schedule_ops;
static void silc_schedule_dispatch_fd(SilcSchedule schedule);
static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
SilcBool dispatch_all);
-
+SILC_TASK_CALLBACK(silc_schedule_event_del_timeout);
/************************ Static utility functions **************************/
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
/* 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
/* 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 */
curtime.tv_sec = 0;
curtime.tv_usec += 1000000L;
}
-
break;
- }
+ } while ((task = silc_list_get(schedule->timeout_queue)));
/* Save the timeout */
if (task) {
static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
{
- SilcTaskFd ftask;
- SilcTaskTimeout ttask;
+ SilcSchedule parent;
- if (task == SILC_ALL_TASKS) {
+ if (silc_unlikely(task == SILC_ALL_TASKS)) {
SilcTask task;
+ SilcEventTask etask;
SilcHashTableList htl;
- SilcUInt32 fd;
+ void *fd;
/* Delete from fd queue */
silc_hash_table_list(schedule->fd_queue, &htl);
- while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task))
- silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd));
+ while (silc_hash_table_get(&htl, &fd, (void *)&task))
+ silc_hash_table_del(schedule->fd_queue, fd);
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 = silc_list_get(schedule->timeout_queue))) {
silc_list_del(schedule->timeout_queue, task);
silc_free(task);
}
+ /* Delete even tasks */
+ parent = silc_schedule_get_parent(schedule);
+ silc_hash_table_list(parent->events, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&etask)) {
+ silc_hash_table_del_by_context(parent->events, etask->event, etask);
+ silc_free(etask->event);
+ silc_free(etask);
+ }
+ silc_hash_table_list_reset(&htl);
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);
- silc_free(ttask);
- break;
- }
+ switch (task->type) {
+ case SILC_TASK_FD:
+ {
+ /* Delete from fd queue */
+ SilcTaskFd ftask = (SilcTaskFd)task;
+ silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
}
+ break;
+
+ case SILC_TASK_TIMEOUT:
+ {
+ /* Delete from timeout queue */
+ silc_list_del(schedule->timeout_queue, task);
+
+ /* Put to free list */
+ silc_list_add(schedule->free_tasks, task);
+ }
+ break;
+ case SILC_TASK_EVENT:
+ {
+ SilcEventTask etask = (SilcEventTask)task;
+ SilcScheduleEventConnection conn;
+
+ parent = silc_schedule_get_parent(schedule);
+
+ /* Delete event */
+ silc_hash_table_del_by_context(parent->events, etask->event, etask);
+
+ /* Remove all connections */
+ silc_list_start(etask->connections);
+ while ((conn = silc_list_get(etask->connections)))
+ silc_free(conn);
+
+ silc_free(etask->event);
+ silc_free(etask);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Timeout freelist garbage collection */
+
+SILC_TASK_CALLBACK(silc_schedule_timeout_gc)
+{
+ SilcTaskTimeout t;
+ int c;
+
+ if (!schedule->valid)
+ return;
+
+ SILC_LOG_DEBUG(("Timeout freelist garbage collection"));
+
+ SILC_SCHEDULE_LOCK(schedule);
+
+ if (silc_list_count(schedule->free_tasks) <= 10) {
+ SILC_SCHEDULE_UNLOCK(schedule);
+ silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
+ schedule, 3600, 0);
+ return;
+ }
+ if (silc_list_count(schedule->timeout_queue) >
+ silc_list_count(schedule->free_tasks)) {
+ SILC_SCHEDULE_UNLOCK(schedule);
+ silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
+ schedule, 3600, 0);
return;
}
- /* Delete from fd queue */
- ftask = (SilcTaskFd)task;
- silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
+ c = silc_list_count(schedule->free_tasks) / 2;
+ if (c > silc_list_count(schedule->timeout_queue))
+ c = (silc_list_count(schedule->free_tasks) -
+ silc_list_count(schedule->timeout_queue));
+ if (silc_list_count(schedule->free_tasks) - c < 10)
+ c -= (10 - (silc_list_count(schedule->free_tasks) - c));
+
+ SILC_LOG_DEBUG(("Freeing %d unused tasks, leaving %d", c,
+ silc_list_count(schedule->free_tasks) - c));
+
+ silc_list_start(schedule->free_tasks);
+ while ((t = silc_list_get(schedule->free_tasks)) && c-- > 0) {
+ silc_list_del(schedule->free_tasks, t);
+ silc_free(t);
+ }
+ silc_list_start(schedule->free_tasks);
+
+ SILC_SCHEDULE_UNLOCK(schedule);
+
+ silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
+ schedule, 3600, 0);
}
+#ifdef SILC_DIST_INPLACE
+/* Print schedule statistics to stdout */
+
+void silc_schedule_stats(SilcSchedule schedule)
+{
+ SilcTaskFd ftask;
+ fprintf(stdout, "Schedule %p statistics:\n\n", schedule);
+ fprintf(stdout, "Num FD tasks : %d (%lu bytes allocated)\n",
+ silc_hash_table_count(schedule->fd_queue),
+ sizeof(*ftask) * silc_hash_table_count(schedule->fd_queue));
+ fprintf(stdout, "Num Timeout tasks : %d (%lu bytes allocated)\n",
+ silc_list_count(schedule->timeout_queue),
+ sizeof(struct SilcTaskTimeoutStruct) *
+ silc_list_count(schedule->timeout_queue));
+ fprintf(stdout, "Num Timeout freelist : %d (%lu bytes allocated)\n",
+ silc_list_count(schedule->free_tasks),
+ sizeof(struct SilcTaskTimeoutStruct) *
+ silc_list_count(schedule->free_tasks));
+}
+#endif /* SILC_DIST_INPLACE */
/****************************** Public API **********************************/
scheduler can handle. The `app_context' is application specific
context that is delivered to task callbacks. */
-SilcSchedule silc_schedule_init(int max_tasks, void *app_context)
+SilcSchedule silc_schedule_init(int max_tasks, void *app_context,
+ SilcStack stack, SilcSchedule parent)
{
SilcSchedule schedule;
- SILC_LOG_DEBUG(("Initializing scheduler"));
+ /* Initialize Tls, in case it hasn't been done yet */
+ silc_thread_tls_init();
- schedule = silc_calloc(1, sizeof(*schedule));
+ stack = silc_stack_alloc(0, stack);
+ if (!stack)
+ return NULL;
+
+ /* Allocate scheduler from the stack */
+ schedule = silc_scalloc(stack, 1, sizeof(*schedule));
if (!schedule)
return NULL;
+ SILC_LOG_DEBUG(("Initializing scheduler %p", schedule));
+
+ /* Allocate Fd task hash table dynamically */
schedule->fd_queue =
- silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
+ silc_hash_table_alloc(NULL, 0, silc_hash_uint, NULL, NULL, NULL,
silc_schedule_fd_destructor, NULL, TRUE);
- if (!schedule->fd_queue)
+ if (!schedule->fd_queue) {
+ silc_stack_free(stack);
return NULL;
+ }
+
+ silc_list_init(schedule->timeout_queue, struct SilcTaskStruct, next);
+ silc_list_init(schedule->free_tasks, struct SilcTaskStruct, next);
- silc_list_init(schedule->timeout_queue, struct SilcTaskTimeoutStruct, next);
+ /* Get the parent */
+ if (parent && parent->parent)
+ parent = parent->parent;
+ schedule->stack = stack;
schedule->app_context = app_context;
schedule->valid = TRUE;
schedule->max_tasks = max_tasks;
+ schedule->parent = parent;
/* Allocate scheduler lock */
silc_mutex_alloc(&schedule->lock);
/* 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_stack_free(stack);
+ return NULL;
+ }
+
+ /* Timeout freelist garbage collection */
+ silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
+ schedule, 3600, 0);
return schedule;
}
SilcBool silc_schedule_uninit(SilcSchedule schedule)
{
- SILC_LOG_DEBUG(("Uninitializing scheduler"));
+ SilcTask task;
+
+ SILC_VERIFY(schedule);
+
+ SILC_LOG_DEBUG(("Uninitializing scheduler %p", schedule));
if (schedule->valid == TRUE)
return FALSE;
}
/* Unregister all tasks */
+ silc_schedule_task_del(schedule, SILC_ALL_TASKS);
silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
- silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
+
+ /* Delete timeout task freelist */
+ silc_list_start(schedule->free_tasks);
+ while ((task = silc_list_get(schedule->free_tasks)))
+ silc_free(task);
/* Unregister all task queues */
silc_hash_table_free(schedule->fd_queue);
schedule_ops.uninit(schedule, schedule->internal);
silc_mutex_free(schedule->lock);
- silc_free(schedule);
+ silc_stack_free(schedule->stack);
return TRUE;
}
void silc_schedule_stop(SilcSchedule schedule)
{
SILC_LOG_DEBUG(("Stopping scheduler"));
+ SILC_VERIFY(schedule);
SILC_SCHEDULE_LOCK(schedule);
schedule->valid = FALSE;
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);
+ } else if (silc_likely(ret > 0)) {
+ /* There is some data available now */
+ SILC_LOG_DEBUG(("Running fd tasks"));
+ silc_schedule_dispatch_fd(schedule);
- 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;
- }
+ /* 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;
- 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
#endif
}
+/* Returns parent scheduler */
+
+SilcSchedule silc_schedule_get_parent(SilcSchedule schedule)
+{
+ return schedule->parent ? schedule->parent : schedule;
+}
+
/* 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
return schedule->app_context;
}
+/* Return the stack of the scheduler */
+
+SilcStack silc_schedule_get_stack(SilcSchedule schedule)
+{
+ return schedule->stack;
+}
+
+/* Set notify callback */
+
+void silc_schedule_set_notify(SilcSchedule schedule,
+ SilcTaskNotifyCb notify, void *context)
+{
+ schedule->notify = notify;
+ schedule->notify_context = context;
+}
+
+/* Set global scheduler */
+
+void silc_schedule_set_global(SilcSchedule schedule)
+{
+ SilcTls tls = silc_thread_get_tls();
+
+ if (!tls) {
+ /* Try to initialize Tls */
+ tls = silc_thread_tls_init();
+ SILC_VERIFY(tls);
+ if (!tls)
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Setting global scheduler %p", schedule));
+
+ tls->schedule = schedule;
+}
+
+/* Return global scheduler */
+
+SilcSchedule silc_schedule_get_global(void)
+{
+ SilcTls tls = silc_thread_get_tls();
+
+ if (!tls)
+ return NULL;
+
+ SILC_LOG_DEBUG(("Return global scheduler %p", tls->schedule));
+
+ return tls->schedule;
+}
+
/* Add new task to the scheduler */
SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
{
SilcTask task = NULL;
- if (!schedule->valid)
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return NULL;
+ }
+ }
+
+ if (silc_unlikely(!schedule->valid)) {
+ silc_set_errno(SILC_ERR_NOT_VALID);
return NULL;
+ }
SILC_SCHEDULE_LOCK(schedule);
- if (type == SILC_TASK_TIMEOUT) {
- SilcTaskTimeout tmp, prev, ttask = silc_calloc(1, sizeof(*ttask));
- if (!ttask)
- goto out;
+ 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 (silc_unlikely(!ttask)) {
+ ttask = silc_calloc(1, sizeof(*ttask));
+ if (silc_unlikely(!ttask))
+ goto out;
+ } else
+ silc_list_del(schedule->free_tasks, ttask);
ttask->header.type = 1;
ttask->header.callback = callback;
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) {
/* 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;
}
silc_list_add(schedule->timeout_queue, ttask);
task = (SilcTask)ttask;
- } else {
+
+ /* 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_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;
+ silc_set_errno(SILC_ERR_LIMIT);
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));
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;
+
+ /* 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,
+ 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) {
- SilcTask task;
+ SilcSchedule parent;
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
+
+ if (silc_unlikely(task == SILC_ALL_TASKS)) {
SilcHashTableList htl;
SILC_LOG_DEBUG(("Unregister all tasks"));
/* 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);
+ }
+
+ /* Delete even tasks */
+ parent = silc_schedule_get_parent(schedule);
+ silc_hash_table_list(parent->events, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&task))
task->valid = FALSE;
+ silc_hash_table_list_reset(&htl);
SILC_SCHEDULE_UNLOCK(schedule);
- return;
+ return TRUE;
}
- SILC_LOG_DEBUG(("Unregistering task %p", task));
+ SILC_LOG_DEBUG(("Unregistering task %p, type %d", task, task->type));
SILC_SCHEDULE_LOCK(schedule);
task->valid = FALSE;
+
+ /* Call notify callback */
+ if (schedule->notify && task->type != SILC_TASK_EVENT)
+ schedule->notify(schedule, FALSE, task, task->type == SILC_TASK_FD,
+ 0, 0, 0, 0, schedule->notify_context);
SILC_SCHEDULE_UNLOCK(schedule);
+
+ if (task->type == SILC_TASK_EVENT) {
+ /* Schedule removal of deleted event task */
+ parent = silc_schedule_get_parent(schedule);
+ silc_schedule_task_add_timeout(parent, silc_schedule_event_del_timeout,
+ task, 0, 1);
+ }
+
+ 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;
+ SilcTask task = NULL;
+ SilcBool ret = FALSE;
SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
+
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;
+ /* 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)) {
+ schedule_ops.signal_unregister(schedule, schedule->internal, fd);
+ ret = TRUE;
+ }
+
+ if (ret == FALSE)
+ silc_set_errno(SILC_ERR_NOT_FOUND);
+
+ 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"));
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
+
SILC_SCHEDULE_LOCK(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);
/* 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;
+
+ /* 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);
+
+ if (ret == FALSE)
+ silc_set_errno(SILC_ERR_NOT_FOUND);
+
+ 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"));
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
+
SILC_SCHEDULE_LOCK(schedule);
/* 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);
/* 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) {
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);
+
+ if (ret == FALSE)
+ silc_set_errno(SILC_ERR_NOT_FOUND);
+
+ 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);
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
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;
+
+ /* 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);
+
+ if (ret == FALSE)
+ silc_set_errno(SILC_ERR_NOT_FOUND);
+
+ 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 (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
+
+ if (silc_unlikely(!schedule->valid)) {
+ silc_set_errno(SILC_ERR_NOT_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);
}
+
+ /* 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 descriptor's current requested event mask. */
+
+SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule,
+ SilcUInt32 fd)
+{
+ SilcTaskFd task;
+ SilcTaskEvent event = 0;
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return 0;
+ }
+ }
+
+ if (silc_unlikely(!schedule->valid)) {
+ silc_set_errno(SILC_ERR_NOT_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. */
silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
}
-/* Register a new signal */
+/*************************** Asynchronous Events ****************************/
-void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal,
- SilcTaskCallback callback, void *context)
+/* Add event */
+
+SilcTask silc_schedule_task_add_event(SilcSchedule schedule,
+ const char *event, ...)
{
- schedule_ops.signal_register(schedule, schedule->internal, signal,
- callback, context);
+ SilcEventTask task;
+ SilcSchedule parent;
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return NULL;
+ }
+ }
+
+ /* Get parent scheduler */
+ parent = silc_schedule_get_parent(schedule);
+
+ SILC_LOG_DEBUG(("Adding event '%s' to scheduler %p", event, parent));
+
+ SILC_SCHEDULE_LOCK(parent);
+
+ /* Create events hash table if not already done */
+ if (!parent->events) {
+ parent->events = silc_hash_table_alloc(NULL, 3,
+ silc_hash_string, NULL,
+ silc_hash_string_compare, NULL,
+ NULL, NULL, FALSE);
+ if (!parent->events) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ return NULL;
+ }
+ }
+
+ /* Check if this event is added already */
+ if (silc_hash_table_find(parent->events, (void *)event, NULL, NULL)) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ return NULL;
+ }
+
+ /* Add new event */
+ task = silc_calloc(1, sizeof(*task));
+ if (!task) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ return NULL;
+ }
+
+ task->header.type = SILC_TASK_EVENT;
+ task->header.valid = TRUE;
+ task->event = silc_strdup(event);
+ if (!task->event) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ silc_free(task);
+ return NULL;
+ }
+ silc_list_init(task->connections, struct SilcScheduleEventConnectionStruct,
+ next);
+
+ if (!silc_hash_table_add(parent->events, task->event, task)) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ silc_free(task->event);
+ silc_free(task);
+ return NULL;
+ }
+
+ SILC_SCHEDULE_UNLOCK(parent);
+
+ return (SilcTask)task;
}
-/* Unregister a new signal */
+/* Connect to event task */
-void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal,
- SilcTaskCallback callback, void *context)
+SilcBool silc_schedule_event_connect(SilcSchedule schedule,
+ const char *event, SilcTask task,
+ SilcTaskEventCallback callback,
+ void *context)
{
- schedule_ops.signal_unregister(schedule, schedule->internal, signal,
- callback, context);
+ SilcSchedule parent;
+ SilcScheduleEventConnection conn;
+ SilcEventTask etask;
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
+
+ if (!event && !task) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+
+ if (task && task->type != SILC_TASK_EVENT) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+
+ /* Get parent scheduler */
+ parent = silc_schedule_get_parent(schedule);
+
+ SILC_SCHEDULE_LOCK(parent);
+
+ if (!task) {
+ /* Get the event task */
+ if (!silc_hash_table_find(parent->events, (void *)event, NULL,
+ (void *)&task)) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ return FALSE;
+ }
+ }
+ etask = (SilcEventTask)task;
+
+ /* See if task is deleted */
+ if (task->valid == FALSE) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ silc_set_errno(SILC_ERR_NOT_VALID);
+ return FALSE;
+ }
+
+ SILC_LOG_DEBUG(("Connect callback %p with context %p to event '%s'",
+ callback, context, etask->event));
+
+ /* See if already connected */
+ silc_list_start(etask->connections);
+ while ((conn = silc_list_get(etask->connections))) {
+ if (conn->callback == callback && conn->context == context) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ silc_set_errno(SILC_ERR_ALREADY_EXISTS);
+ return FALSE;
+ }
+ }
+
+ conn = silc_calloc(1, sizeof(*conn));
+ if (!conn) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ return FALSE;
+ }
+
+ /* Connect to the event */
+ conn->schedule = schedule;
+ conn->callback = callback;
+ conn->context = context;
+ silc_list_add(etask->connections, conn);
+
+ SILC_SCHEDULE_UNLOCK(parent);
+
+ return TRUE;
+}
+
+/* Disconnect from event */
+
+SilcBool silc_schedule_event_disconnect(SilcSchedule schedule,
+ const char *event, SilcTask task,
+ SilcTaskEventCallback callback,
+ void *context)
+{
+ SilcSchedule parent;
+ SilcScheduleEventConnection conn;
+ SilcEventTask etask;
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
+
+ if (!event && !task) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+
+ if (task && task->type != SILC_TASK_EVENT) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+
+ /* Get parent scheduler */
+ parent = silc_schedule_get_parent(schedule);
+
+ SILC_SCHEDULE_LOCK(parent);
+
+ if (!task) {
+ /* Get the event task */
+ if (!silc_hash_table_find(parent->events, (void *)event, NULL,
+ (void *)&task)) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ return FALSE;
+ }
+ }
+ etask = (SilcEventTask)task;
+
+ /* See if task is deleted */
+ if (task->valid == FALSE) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ silc_set_errno(SILC_ERR_NOT_VALID);
+ return FALSE;
+ }
+
+ SILC_LOG_DEBUG(("Disconnect callback %p with context %p from event '%s'",
+ callback, context, etask->event));
+
+ /* Disconnect */
+ silc_list_start(etask->connections);
+ while ((conn = silc_list_get(etask->connections))) {
+ if (conn->callback == callback && conn->context == context) {
+ silc_list_del(etask->connections, conn);
+ silc_free(conn);
+ SILC_SCHEDULE_UNLOCK(parent);
+ return TRUE;
+ }
+ }
+
+ SILC_SCHEDULE_UNLOCK(parent);
+ silc_set_errno(SILC_ERR_NOT_FOUND);
+ return FALSE;
+}
+
+/* Signal event */
+
+SilcBool silc_schedule_event_signal(SilcSchedule schedule, const char *event,
+ SilcTask task, ...)
+{
+ SilcSchedule parent;
+ SilcScheduleEventConnection conn;
+ SilcEventTask etask;
+ SilcBool stop;
+ va_list ap, cp;
+
+ if (silc_unlikely(!schedule)) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
+
+ if (silc_unlikely(!event && !task)) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+
+ if (silc_unlikely(task && task->type != SILC_TASK_EVENT)) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+
+ /* Get parent scheduler */
+ parent = silc_schedule_get_parent(schedule);
+
+ SILC_SCHEDULE_LOCK(parent);
+
+ if (!task) {
+ /* Get the event task */
+ if (!silc_hash_table_find(parent->events, (void *)event, NULL,
+ (void *)&task)) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ return FALSE;
+ }
+ }
+ etask = (SilcEventTask)task;
+
+ /* See if task is deleted */
+ if (task->valid == FALSE) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ silc_set_errno(SILC_ERR_NOT_VALID);
+ return FALSE;
+ }
+
+ SILC_LOG_DEBUG(("Signal event '%s'", etask->event));
+
+ va_start(ap, task);
+
+ /* Deliver the signal */
+ silc_list_start(etask->connections);
+ while ((conn = silc_list_get(etask->connections))) {
+ SILC_SCHEDULE_UNLOCK(parent);
+
+ silc_va_copy(cp, ap);
+ stop = conn->callback(conn->schedule, conn->schedule->app_context,
+ task, conn->context, cp);
+ va_end(cp);
+
+ SILC_SCHEDULE_LOCK(parent);
+
+ /* Stop signal if wanted or if the task was deleted */
+ if (!stop || !task->valid)
+ break;
+ }
+
+ va_end(ap);
+
+ SILC_SCHEDULE_UNLOCK(parent);
+
+ return TRUE;
+}
+
+/* Delete event */
+
+SilcBool silc_schedule_task_del_event(SilcSchedule schedule, const char *event)
+{
+ SilcSchedule parent;
+ SilcTask task;
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ SILC_VERIFY(schedule);
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+ }
+
+ if (!event) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return FALSE;
+ }
+
+ /* Get parent scheduler */
+ parent = silc_schedule_get_parent(schedule);
+
+ SILC_SCHEDULE_LOCK(parent);
+
+ /* Get the event task */
+ if (!silc_hash_table_find(parent->events, (void *)event, NULL,
+ (void *)&task)) {
+ SILC_SCHEDULE_UNLOCK(parent);
+ return FALSE;
+ }
+
+ /* See if already deleted */
+ if (task->valid == FALSE)
+ return TRUE;
+
+ SILC_LOG_DEBUG(("Delete event '%s'", ((SilcEventTask)task)->event));
+
+ SILC_SCHEDULE_UNLOCK(parent);
+
+ silc_schedule_task_del(parent, task);
+
+ return TRUE;
}
-/* Call signal indicated by `signal'. */
+/* Timeout to remove deleted event task */
-void silc_schedule_signal_call(SilcSchedule schedule, SilcUInt32 signal)
+SILC_TASK_CALLBACK(silc_schedule_event_del_timeout)
{
- /* Mark that signals needs to be delivered later. */
- schedule_ops.signal_call(schedule, schedule->internal, signal);
- schedule->signal_tasks = TRUE;
+ SILC_SCHEDULE_LOCK(schedule);
+ silc_schedule_task_remove(schedule, context);
+ SILC_SCHEDULE_UNLOCK(schedule);
}