+ while (silc_schedule_one(schedule, -1))
+ ;
+
+ SILC_SCHEDULE_UNLOCK(schedule);
+}
+
+/* Wakes up the scheduler. This is used only in multi-threaded
+ environments where threads may add new tasks or remove old tasks
+ from task queues. This is called to wake up the scheduler in the
+ main thread so that it detects the changes in the task queues.
+ If threads support is not compiled in this function has no effect.
+ Implementation of this function is platform specific. */
+
+void silc_schedule_wakeup(SilcSchedule schedule)
+{
+#ifdef SILC_THREADS
+ SILC_LOG_DEBUG(("Wakeup scheduler"));
+ SILC_SCHEDULE_LOCK(schedule);
+ schedule_ops.wakeup(schedule, schedule->internal);
+ SILC_SCHEDULE_UNLOCK(schedule);
+#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,
+ SilcTaskCallback callback, void *context,
+ long seconds, long useconds,
+ SilcTaskType type)
+{
+ SilcTask task = NULL;
+
+ if (!schedule->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;
+
+ ttask->header.type = 1;
+ ttask->header.callback = callback;
+ ttask->header.context = context;
+ ttask->header.valid = TRUE;
+
+ /* Add 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) {
+ ttask->timeout.tv_sec += 1;
+ ttask->timeout.tv_usec -= 1000000L;
+ }
+ }
+
+ SILC_LOG_DEBUG(("New timeout task %p: sec=%d, usec=%d", ttask,
+ seconds, useconds));
+
+ /* Add task to correct spot so that the first task in the list has
+ the earliest timeout. */
+ silc_list_start(schedule->timeout_queue);
+ prev = NULL;
+ while ((tmp = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
+ /* If we have shorter timeout, we have found our spot */
+ if (silc_compare_timeval(&ttask->timeout, &tmp->timeout)) {
+ silc_list_insert(schedule->timeout_queue, prev, ttask);
+ break;
+ }
+ prev = tmp;
+ }
+ if (!tmp)
+ silc_list_add(schedule->timeout_queue, ttask);
+
+ task = (SilcTask)ttask;
+ } else {
+ /* Check if fd is already added */
+ if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
+ NULL, (void **)&task))
+ goto out;
+
+ /* Check max tasks */
+ if (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"));
+ goto out;
+ }
+
+ SilcTaskFd ftask = silc_calloc(1, sizeof(*ftask));
+ if (!ftask)
+ goto out;
+
+ SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd));
+
+ ftask->header.type = 0;
+ ftask->header.callback = callback;
+ ftask->header.context = context;
+ ftask->header.valid = TRUE;
+ ftask->events = SILC_TASK_READ;
+ ftask->fd = fd;
+
+ /* Add task */
+ silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask);
+
+ task = (SilcTask)ftask;
+ }
+
+ out:
+ SILC_SCHEDULE_UNLOCK(schedule);
+ return task;
+}
+
+/* Invalidates task */
+
+void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
+{
+ if (task == SILC_ALL_TASKS) {
+ SilcTask task;
+ SilcHashTableList htl;
+
+ SILC_LOG_DEBUG(("Unregister all tasks"));
+
+ 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))
+ task->valid = FALSE;
+ 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)
+ task->valid = FALSE;
+
+ SILC_SCHEDULE_UNLOCK(schedule);
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Unregistering task %p", task));
+ SILC_SCHEDULE_LOCK(schedule);
+ task->valid = FALSE;
+ SILC_SCHEDULE_UNLOCK(schedule);
+}
+
+/* Invalidate task by fd */
+
+void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
+{
+ SilcTask task;
+
+ SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
+
+ 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))
+ task->valid = FALSE;
+
+ SILC_SCHEDULE_UNLOCK(schedule);
+}
+
+/* Invalidate task by task callback. */
+
+void silc_schedule_task_del_by_callback(SilcSchedule schedule,
+ SilcTaskCallback callback)
+{
+ SilcTask task;
+ SilcHashTableList htl;
+
+ SILC_LOG_DEBUG(("Unregister task by callback"));
+
+ 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)
+ task->valid = FALSE;
+ }
+ 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)
+ task->valid = FALSE;
+ }
+
+ SILC_SCHEDULE_UNLOCK(schedule);
+}
+
+/* Invalidate task by context. */
+
+void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
+{
+ SilcTask task;
+ SilcHashTableList htl;
+
+ SILC_LOG_DEBUG(("Unregister task by context"));
+
+ 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)
+ task->valid = FALSE;
+ }
+ 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)
+ task->valid = FALSE;
+ }
+
+ SILC_SCHEDULE_UNLOCK(schedule);
+}
+
+/* Invalidate task by all */
+
+void silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
+ SilcTaskCallback callback, void *context)
+{
+ SilcTask task;
+
+ 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);
+
+ 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)
+ task->valid = FALSE;
+ }
+
+ SILC_SCHEDULE_UNLOCK(schedule);
+}
+
+/* Removes task from the scheduler. This must be called with scheduler
+ locked. */
+
+static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
+{
+ SilcTaskFd ftask;
+ SilcTaskTimeout ttask;
+
+ if (task == SILC_ALL_TASKS) {
+ SilcTask task;
+ SilcHashTableList htl;
+ SilcUInt32 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));
+ 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) {
+ silc_list_del(schedule->timeout_queue, task);
+ silc_free(task);
+ }
+
+ 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;
+ }
+ }
+
+ return;
+ }
+
+ /* Delete from fd queue */
+ ftask = (SilcTaskFd)task;
+ silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
+}
+
+/* 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)
+{
+ SilcTaskFd task;
+
+ if (!schedule->valid)
+ return;
+
+ SILC_SCHEDULE_LOCK(schedule);
+
+ if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
+ NULL, (void **)&task)) {
+ task->events = mask;
+ if (send_events) {
+ task->revents = mask;
+ silc_schedule_dispatch_fd(schedule);
+ }
+ }
+
+ SILC_SCHEDULE_UNLOCK(schedule);
+}
+
+/* Removes a file descriptor from listen list. */
+
+void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
+{
+ silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
+}
+
+/* Register a new signal */
+
+void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal,
+ SilcTaskCallback callback, void *context)
+{
+ schedule_ops.signal_register(schedule, schedule->internal, signal,
+ callback, context);
+}
+
+/* Unregister a new signal */
+
+void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal,
+ SilcTaskCallback callback, void *context)
+{
+ schedule_ops.signal_unregister(schedule, schedule->internal, signal,
+ callback, context);
+}
+
+/* Call signal indicated by `signal'. */
+
+void silc_schedule_signal_call(SilcSchedule schedule, SilcUInt32 signal)
+{
+ /* Mark that signals needs to be delivered later. */
+ schedule_ops.signal_call(schedule, schedule->internal, signal);
+ schedule->signal_tasks = TRUE;