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
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_likely(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 (silc_unlikely(!t->valid)) {
- silc_schedule_task_remove(schedule, t);
- continue;
- }
- if (!task->revents || !task->events)
- 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 (silc_unlikely(!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
if (!schedule->fd_queue)
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;
of the selected file descriptors change status or the selected
timeout expires. */
SILC_LOG_DEBUG(("Select"));
- ret = schedule_ops.select(schedule, schedule->internal);
+ ret = schedule_ops.schedule(schedule, schedule->internal);
if (silc_likely(ret == 0)) {
/* Timeout */
task = (SilcTask)ttask;
} 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) {
+ task = NULL;
+ 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;
}
- SilcTaskFd ftask = silc_calloc(1, sizeof(*ftask));
- if (silc_unlikely(!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;
/* 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;
+ }
SILC_SCHEDULE_UNLOCK(schedule);
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)) {
+ if (!schedule_ops.schedule_fd(schedule, schedule->internal, task, mask)) {
+ SILC_SCHEDULE_UNLOCK(schedule);
+ return FALSE;
+ }
task->events = mask;
- if (silc_unlikely(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. */