}
/* 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;
/* 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;
if (silc_unlikely(task == SILC_ALL_TASKS)) {
SilcTask task;
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 */
{
SilcTaskFd ftask;
fprintf(stdout, "Schedule %p statistics:\n\n", schedule);
- fprintf(stdout, "Num FD tasks : %lu (%lu bytes allocated)\n",
+ 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 (%d bytes allocated)\n",
+ 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 (%d bytes allocated)\n",
+ 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));
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 schedule;
- SILC_LOG_DEBUG(("Initializing scheduler"));
+ /* Initialize Tls, in case it hasn't been done yet */
+ silc_thread_tls_init();
+
+ stack = silc_stack_alloc(0, stack);
+ if (!stack)
+ return NULL;
- schedule = silc_calloc(1, sizeof(*schedule));
+ /* 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);
+ schedule->stack = stack;
schedule->app_context = app_context;
schedule->valid = TRUE;
schedule->max_tasks = max_tasks;
/* 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,
{
SilcTask task;
- SILC_LOG_DEBUG(("Uninitializing scheduler"));
+ SILC_VERIFY(schedule);
+
+ SILC_LOG_DEBUG(("Uninitializing scheduler %p", schedule));
if (schedule->valid == TRUE)
return FALSE;
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);
/* 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;
}
/* 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"));
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
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 (silc_unlikely(!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);
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;
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;
}
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;
schedule->max_tasks)) {
SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task"));
task = NULL;
+ silc_set_errno(SILC_ERR_LIMIT);
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,
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;
}
SilcBool silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
{
+ 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;
/* 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 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;
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 */
(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;
}
ret = TRUE;
}
+ if (ret == FALSE)
+ silc_set_errno(SILC_ERR_NOT_FOUND);
+
return ret;
}
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 */
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;
}
}
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;
}
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 */
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_list_start(list);
while ((task = (SilcTask)silc_list_get(list))) {
if (task->context == context) {
- ret = TRUE;
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;
}
if (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 */
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;
}
{
SilcTaskFd task;
- if (silc_unlikely(!schedule->valid))
+ 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);
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);
SilcTaskFd task;
SilcTaskEvent event = 0;
- if (silc_unlikely(!schedule->valid))
+ 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),