+ silc_mutex_unlock(schedule->lock);
+}
+
+/* Allocates a newtask task queue into the scheduler */
+
+static void silc_task_queue_alloc(SilcTaskQueue *queue)
+{
+ *queue = silc_calloc(1, sizeof(**queue));
+ silc_mutex_alloc(&(*queue)->lock);
+}
+
+/* Free's a task queue. */
+
+static void silc_task_queue_free(SilcTaskQueue queue)
+{
+ silc_mutex_free(queue->lock);
+ silc_free(queue);
+}
+
+/* Return task by its fd. */
+
+static SilcTask silc_task_find(SilcTaskQueue queue, uint32 fd)
+{
+ SilcTask next;
+
+ if (!queue->task)
+ return NULL;
+
+ next = queue->task;
+
+ while (1) {
+ if (next->fd == fd)
+ return next;
+ if (queue->task == next->next)
+ return NULL;
+ next = next->next;
+ }
+
+ return NULL;
+}
+
+/* Adds a non-timeout task into the task queue. This function is used
+ by silc_task_register function. Returns a pointer to the registered
+ task. */
+
+static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
+ SilcTaskPriority priority)
+{
+ SilcTask task, next, prev;
+
+ /* Take the first task in the queue */
+ task = queue->task;
+
+ switch(priority) {
+ case SILC_TASK_PRI_LOW:
+ /* Lowest priority. The task is added at the end of the list. */
+ prev = task->prev;
+ newtask->prev = prev;
+ newtask->next = task;
+ prev->next = newtask;
+ task->prev = newtask;
+ break;
+ case SILC_TASK_PRI_NORMAL:
+ /* Normal priority. The task is added before lower priority tasks
+ but after tasks with higher priority. */
+ prev = task->prev;
+ while(prev != task) {
+ if (prev->priority > SILC_TASK_PRI_LOW)
+ break;
+ prev = prev->prev;
+ }
+ if (prev == task) {
+ /* There are only lower priorities in the list, we will
+ sit before them and become the first task in the queue. */
+ prev = task->prev;
+ newtask->prev = prev;
+ newtask->next = task;
+ task->prev = newtask;
+ prev->next = newtask;
+
+ /* We are now the first task in queue */
+ queue->task = newtask;
+ } else {
+ /* Found a spot from the list, add the task to the list. */
+ next = prev->next;
+ newtask->prev = prev;
+ newtask->next = next;
+ prev->next = newtask;
+ next->prev = newtask;
+ }
+ break;
+ default:
+ silc_free(newtask);
+ return NULL;
+ }
+
+ return newtask;
+}
+
+/* Return the timeout task with smallest timeout. */
+
+static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
+{
+ SilcTask prev, task;
+
+ prev = first->prev;
+
+ if (first == prev)
+ return first;
+
+ task = first;
+ while (1) {
+ if (first == prev)
+ break;
+
+ if (silc_schedule_task_timeout_compare(&prev->timeout, &task->timeout))
+ task = prev;
+
+ prev = prev->prev;
+ }
+
+ return task;
+}
+
+/* Adds a timeout task into the task queue. This function is used by
+ silc_task_register function. Returns a pointer to the registered
+ task. Timeout tasks are sorted by their timeout value in ascending
+ order. The priority matters if there are more than one task with
+ same timeout. */
+
+static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
+ SilcTaskPriority priority)
+{
+ SilcTask task, prev, next;
+
+ /* Take the first task in the queue */
+ task = queue->task;
+
+ /* Take last task from the list */
+ prev = task->prev;
+
+ switch(priority) {
+ case SILC_TASK_PRI_LOW:
+ /* Lowest priority. The task is added at the end of the list. */
+ while(prev != task) {
+
+ /* 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))
+ break;
+
+ /* If we are equal size of timeout we will be after it. */
+ if (!silc_schedule_task_timeout_compare(&newtask->timeout,
+ &prev->timeout))
+ break;
+
+ /* We have shorter timeout, compare to next one. */
+ prev = prev->prev;
+ }
+ /* Found a spot from the list, add the task to the list. */
+ next = prev->next;
+ newtask->prev = prev;
+ newtask->next = next;
+ prev->next = newtask;
+ next->prev = newtask;
+
+ 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))
+ break;
+ if (!silc_schedule_task_timeout_compare(&newtask->timeout,
+ &prev->timeout))
+ break;
+
+ /* We are now the first task in queue */
+ queue->task = newtask;
+ }
+ break;
+ case SILC_TASK_PRI_NORMAL:
+ /* Normal priority. The task is added before lower priority tasks
+ but after tasks with higher priority. */
+ while(prev != task) {
+
+ /* 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))
+ break;
+
+ /* If we are equal size of timeout, priority kicks in place. */
+ if (!silc_schedule_task_timeout_compare(&newtask->timeout,
+ &prev->timeout))
+ if (prev->priority >= SILC_TASK_PRI_NORMAL)
+ break;
+
+ /* We have shorter timeout or higher priority, compare to next one. */
+ prev = prev->prev;
+ }
+ /* Found a spot from the list, add the task to the list. */
+ next = prev->next;
+ newtask->prev = prev;
+ newtask->next = next;
+ prev->next = newtask;
+ next->prev = newtask;
+
+ 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))
+ break;
+ if (!silc_schedule_task_timeout_compare(&newtask->timeout,
+ &prev->timeout))
+ if (prev->priority >= SILC_TASK_PRI_NORMAL)
+ break;
+
+ /* We are now the first task in queue */
+ queue->task = newtask;
+ }
+ break;
+ default:
+ silc_free(newtask);
+ return NULL;
+ }
+
+ return newtask;
+}
+
+/* Removes (unregisters) a task from particular task queue. This function
+ is used internally by scheduler. This must be called holding the
+ queue->lock. */
+
+static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task)
+{
+ SilcTask first, old, next;
+
+ if (!queue || !task)
+ return FALSE;
+
+ if (!queue->task) {
+ return FALSE;
+ }
+
+ first = queue->task;
+
+ /* Unregister all tasks in queue */
+ if (task == SILC_ALL_TASKS) {
+ SILC_LOG_DEBUG(("Removing all tasks at once"));
+ next = first;
+
+ while(1) {
+ next = next->next;
+ silc_free(next->prev);
+ if (next == first)
+ break;
+ }
+
+ queue->task = NULL;
+ return TRUE;
+ }
+
+ SILC_LOG_DEBUG(("Removing task"));
+
+ /* Unregister the task */
+ old = first;
+ while(1) {
+ if (old == task) {
+ SilcTask prev, next;
+
+ prev = old->prev;
+ next = old->next;
+ prev->next = next;
+ next->prev = prev;
+
+ if (prev == old && next == old)
+ queue->task = NULL;
+ if (queue->task == old)
+ queue->task = silc_task_get_first(queue, next);
+
+ silc_free(old);
+ return TRUE;