/*
- silcschedule.c
+ silcschedule.c
Author: Pekka Riikonen <priikone@silcnet.org>
- 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
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);
/* 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);
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);
*/
struct SilcScheduleStruct {
+ void *app_context; /* Application specific context */
SilcTaskQueue fd_queue;
SilcTaskQueue timeout_queue;
SilcTaskQueue generic_queue;
/* 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;
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);
+ schedule->internal = silc_schedule_internal_init(schedule, app_context);
return 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);
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;
for (i = 0; i <= last_fd; i++) {
if (schedule->fd_list[i].events == 0)
if (task->valid && schedule->fd_list[i].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);
}
if (task->valid && schedule->fd_list[i].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);
}
if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
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);
}
if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
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);
}
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;
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);
}
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;
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:
#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,
SilcTaskQueue queue;
int timeout = FALSE;
+ if (!schedule->valid)
+ return NULL;
+
SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", fd,
type, priority));
/* 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;
}
/* 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);
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;
}
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;
}
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;
/* 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. */
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 */
/* 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;
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;
}
}
-/* 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;