5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1998 - 2001 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
22 #include "silcincludes.h"
23 #include "silcschedule_i.h"
25 /* Forward declarations */
26 typedef struct SilcTaskQueueStruct *SilcTaskQueue;
28 /* System specific routines. Implemented under unix/ and win32/. */
30 /* System specific select(). Returns same values as normal select(). */
31 int silc_select(SilcScheduleFd fds, uint32 fds_count, struct timeval *timeout);
33 /* Initializes the wakeup of the scheduler. In multi-threaded environment
34 the scheduler needs to be wakenup when tasks are added or removed from
35 the task queues. This will initialize the wakeup for the scheduler.
36 Any tasks that needs to be registered must be registered to the `queue'.
37 It is guaranteed that the scheduler will automatically free any
38 registered tasks in this queue. This is system specific routine. */
39 void *silc_schedule_wakeup_init(SilcSchedule schedule);
41 /* Uninitializes the system specific wakeup. */
42 void silc_schedule_wakeup_uninit(void *context);
44 /* Wakes up the scheduler. This is platform specific routine */
45 void silc_schedule_wakeup_internal(void *context);
48 /* Internal task management routines. */
50 static void silc_task_queue_alloc(SilcTaskQueue *queue);
51 static void silc_task_queue_free(SilcTaskQueue queue);
52 static SilcTask silc_task_find(SilcTaskQueue queue, uint32 fd);
53 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
54 SilcTaskPriority priority);
55 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first);
56 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
57 SilcTaskPriority priority);
58 static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task);
59 static int silc_schedule_task_timeout_compare(struct timeval *smaller,
60 struct timeval *bigger);
61 static void silc_task_del_by_context(SilcTaskQueue queue, void *context);
62 static void silc_task_del_by_callback(SilcTaskQueue queue,
63 SilcTaskCallback callback);
64 static void silc_task_del_by_fd(SilcTaskQueue queue, uint32 fd);
66 /* Returns the task queue by task type */
67 #define SILC_SCHEDULE_GET_QUEUE(type) \
68 (type == SILC_TASK_FD ? schedule->fd_queue : \
69 type == SILC_TASK_TIMEOUT ? schedule->timeout_queue : \
70 schedule->generic_queue)
72 /* SILC Task object. Represents one task in the scheduler. */
73 struct SilcTaskStruct {
75 struct timeval timeout;
76 SilcTaskCallback callback;
79 SilcTaskPriority priority;
82 /* Pointers forming doubly linked circular list */
83 struct SilcTaskStruct *next;
84 struct SilcTaskStruct *prev;
87 /* SILC Task Queue object. The queue holds all the tasks in the scheduler.
88 There are always three task queues in the scheduler. One for non-timeout
89 tasks (fd tasks performing tasks over specified file descriptor),
90 one for timeout tasks and one for generic tasks. */
91 struct SilcTaskQueueStruct {
92 SilcTask task; /* Pointer to all tasks */
93 struct timeval timeout; /* Current timeout */
94 SILC_MUTEX_DEFINE(lock); /* Queue's lock */
98 SILC Scheduler structure.
100 This is the actual schedule object in SILC. Both SILC client and server
101 uses this same scheduler. Actually, this scheduler could be used by any
102 program needing scheduling.
104 Following short description of the fields:
106 SilcTaskQueue fd_queue
108 Task queue hook for non-timeout tasks. Usually this means that these
109 tasks perform different kind of I/O on file descriptors. File
110 descriptors are usually network sockets but they actually can be
111 any file descriptors. This hook is initialized in silc_schedule_init
112 function. Timeout tasks should not be added to this queue because
113 they will never expire.
115 SilcTaskQueue timeout_queue
117 Task queue hook for timeout tasks. This hook is reserved specificly
118 for tasks with timeout. Non-timeout tasks should not be added to this
119 queue because they will never get scheduled. This hook is also
120 initialized in silc_schedule_init function.
122 SilcTaskQueue generic_queue
124 Task queue hook for generic tasks. This hook is reserved specificly
125 for generic tasks, tasks that apply to all file descriptors, except
126 to those that have specificly registered a non-timeout task. This hook
127 is also initialized in silc_schedule_init function.
129 SilcScheduleFd fd_list
131 List of file descriptors the scheduler is supposed to be listenning.
132 This is updated internally.
137 Size of the fd_list list. There can be `max_fd' many tasks in
138 the scheduler at once. The `last_fd' is the last valid entry
141 struct timeval *timeout;
143 Pointer to the schedules next timeout. Value of this timeout is
144 automatically updated in the silc_schedule function.
148 Marks validity of the scheduler. This is a boolean value. When this
149 is false the scheduler is terminated and the program will end. This
150 set to true when the scheduler is initialized with silc_schedule_init
156 File descriptor sets for select(). These are automatically managed
157 by the scheduler and should not be touched otherwise.
161 System specific wakeup context. On multi-threaded environments the
162 scheduler needs to be wakenup (in the thread) when tasks are added
163 or removed. This is initialized by silc_schedule_wakeup_init.
165 SILC_MUTEX_DEFINE(lock)
170 struct SilcScheduleStruct {
171 SilcTaskQueue fd_queue;
172 SilcTaskQueue timeout_queue;
173 SilcTaskQueue generic_queue;
174 SilcScheduleFd fd_list;
177 struct timeval *timeout;
180 SILC_MUTEX_DEFINE(lock);
184 /* Initializes the scheduler. This returns the scheduler context that
185 is given as arugment usually to all silc_schedule_* functions.
186 The `max_tasks' indicates the number of maximum tasks that the
187 scheduler can handle. */
189 SilcSchedule silc_schedule_init(int max_tasks)
191 SilcSchedule schedule;
193 SILC_LOG_DEBUG(("Initializing scheduler"));
195 schedule = silc_calloc(1, sizeof(*schedule));
197 /* Allocate three task queues, one for file descriptor based tasks,
198 one for timeout tasks and one for generic tasks. */
199 silc_task_queue_alloc(&schedule->fd_queue);
200 silc_task_queue_alloc(&schedule->timeout_queue);
201 silc_task_queue_alloc(&schedule->generic_queue);
203 /* Initialize the scheduler */
204 schedule->fd_list = silc_calloc(max_tasks, sizeof(*schedule->fd_list));
205 schedule->max_fd = max_tasks;
206 schedule->timeout = NULL;
207 schedule->valid = TRUE;
209 /* Allocate scheduler lock */
210 silc_mutex_alloc(&schedule->lock);
212 /* Initialize the wakeup, for multi-threads support */
213 schedule->wakeup = silc_schedule_wakeup_init(schedule);
218 /* Uninitializes the schedule. This is called when the program is ready
219 to end. This removes all tasks and task queues. Returns FALSE if the
220 scheduler could not be uninitialized. This happens when the scheduler
221 is still valid and silc_schedule_stop has not been called. */
223 bool silc_schedule_uninit(SilcSchedule schedule)
225 SILC_LOG_DEBUG(("Uninitializing scheduler"));
227 if (schedule->valid == TRUE)
230 /* Unregister all tasks */
231 silc_schedule_task_remove(schedule->fd_queue, SILC_ALL_TASKS);
232 silc_schedule_task_remove(schedule->timeout_queue, SILC_ALL_TASKS);
233 silc_schedule_task_remove(schedule->generic_queue, SILC_ALL_TASKS);
235 /* Unregister all task queues */
236 silc_task_queue_free(schedule->fd_queue);
237 silc_task_queue_free(schedule->timeout_queue);
238 silc_task_queue_free(schedule->generic_queue);
240 silc_free(schedule->fd_list);
242 /* Uninit the wakeup */
243 silc_schedule_wakeup_uninit(schedule->wakeup);
245 silc_mutex_free(schedule->lock);
250 /* Stops the schedule even if it is not supposed to be stopped yet.
251 After calling this, one should call silc_schedule_uninit (after the
252 silc_schedule has returned). */
254 void silc_schedule_stop(SilcSchedule schedule)
256 SILC_LOG_DEBUG(("Stopping scheduler"));
257 silc_mutex_lock(schedule->lock);
258 schedule->valid = FALSE;
259 silc_mutex_unlock(schedule->lock);
262 /* Executes nontimeout tasks. It then checks whether any of ther fd tasks
263 was signaled by the silc_select. If some task was not signaled then
264 all generic tasks are executed for that task. The generic tasks are
265 never executed for task that has explicit fd task set. */
266 /* This holds the schedule->lock and the queue locks. */
268 static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule)
273 for (i = 0; i <= schedule->last_fd; i++) {
274 if (schedule->fd_list[i].events == 0)
277 /* First check whether this fd has task in the fd queue */
278 silc_mutex_lock(schedule->fd_queue->lock);
279 task = silc_task_find(schedule->fd_queue, schedule->fd_list[i].fd);
280 silc_mutex_unlock(schedule->fd_queue->lock);
282 /* If the task was found then execute its callbacks. If not then
283 execute all generic tasks for that fd. */
285 /* Validity of the task is checked always before and after
286 execution beacuse the task might have been unregistered
287 in the callback function, ie. it is not valid anymore. */
288 silc_mutex_lock(schedule->fd_queue->lock);
290 /* Is the task ready for reading */
291 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
292 silc_mutex_unlock(schedule->fd_queue->lock);
293 silc_mutex_unlock(schedule->lock);
294 task->callback(schedule, SILC_TASK_READ, task->fd, task->context);
295 silc_mutex_lock(schedule->lock);
296 silc_mutex_lock(schedule->fd_queue->lock);
299 /* Is the task ready for writing */
300 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
301 silc_mutex_unlock(schedule->fd_queue->lock);
302 silc_mutex_unlock(schedule->lock);
303 task->callback(schedule, SILC_TASK_WRITE, task->fd, task->context);
304 silc_mutex_lock(schedule->lock);
305 silc_mutex_lock(schedule->fd_queue->lock);
309 silc_schedule_task_remove(schedule->fd_queue, task);
311 silc_mutex_unlock(schedule->fd_queue->lock);
313 /* Run generic tasks for this fd. */
315 silc_mutex_lock(schedule->generic_queue->lock);
316 if (!schedule->generic_queue->task) {
317 silc_mutex_unlock(schedule->generic_queue->lock);
321 task = schedule->generic_queue->task;
323 /* Validity of the task is checked always before and after
324 execution beacuse the task might have been unregistered
325 in the callback function, ie. it is not valid anymore. */
327 /* Is the task ready for reading */
328 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
329 silc_mutex_unlock(schedule->generic_queue->lock);
330 silc_mutex_unlock(schedule->lock);
331 task->callback(schedule, SILC_TASK_READ, schedule->fd_list[i].fd,
333 silc_mutex_lock(schedule->lock);
334 silc_mutex_lock(schedule->generic_queue->lock);
337 /* Is the task ready for writing */
338 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
339 silc_mutex_unlock(schedule->generic_queue->lock);
340 silc_mutex_unlock(schedule->lock);
341 task->callback(schedule, SILC_TASK_WRITE, schedule->fd_list[i].fd,
343 silc_mutex_lock(schedule->lock);
344 silc_mutex_lock(schedule->generic_queue->lock);
348 /* Invalid (unregistered) tasks are removed from the
350 if (schedule->generic_queue->task == task->next) {
351 silc_schedule_task_remove(schedule->generic_queue, task);
352 silc_mutex_unlock(schedule->generic_queue->lock);
357 silc_schedule_task_remove(schedule->generic_queue, task);
361 /* Break if there isn't more tasks in the queue */
362 if (schedule->generic_queue->task == task->next)
368 silc_mutex_unlock(schedule->generic_queue->lock);
373 /* Executes all tasks whose timeout has expired. The task is removed from
374 the task queue after the callback function has returned. Also, invalid
375 tasks are removed here. We don't have to care about priorities because
376 tasks are already sorted in their priority order at the registration
378 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
380 static void silc_schedule_dispatch_timeout(SilcSchedule schedule)
382 SilcTaskQueue queue = schedule->timeout_queue;
384 struct timeval curtime;
386 SILC_LOG_DEBUG(("Running timeout tasks"));
388 silc_gettimeofday(&curtime);
390 queue = schedule->timeout_queue;
391 if (queue && queue->task) {
394 /* Walk thorugh all tasks in the particular task queue and run all
395 the expired tasks. */
397 /* Execute the task if the timeout has expired */
398 if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
400 silc_mutex_unlock(queue->lock);
401 silc_mutex_unlock(schedule->lock);
402 task->callback(schedule, SILC_TASK_EXPIRE, task->fd, task->context);
403 silc_mutex_lock(schedule->lock);
404 silc_mutex_lock(queue->lock);
407 /* Break if there isn't more tasks in the queue */
408 if (queue->task == task->next) {
409 silc_schedule_task_remove(queue, task);
415 /* Remove the task from queue */
416 silc_schedule_task_remove(queue, task->prev);
418 /* The timeout hasn't expired, check for next one */
420 /* Break if there isn't more tasks in the queue */
421 if (queue->task == task->next)
430 /* Calculates next timeout for select(). This is the timeout value
431 when at earliest some of the timeout tasks expire. If this is in the
432 past, they will be run now. */
433 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
435 static void silc_schedule_select_timeout(SilcSchedule schedule)
437 SilcTaskQueue queue = schedule->timeout_queue;
439 struct timeval curtime;
441 /* Get the current time */
442 silc_gettimeofday(&curtime);
443 schedule->timeout = NULL;
445 /* First task in the task queue has always the smallest timeout. */
448 if (task && task->valid == TRUE) {
449 /* If the timeout is in past, we will run the task and all other
450 timeout tasks from the past. */
451 if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
452 silc_schedule_dispatch_timeout(schedule);
454 /* The task(s) has expired and doesn't exist on the task queue
455 anymore. We continue with new timeout. */
456 queue = schedule->timeout_queue;
458 if (task == NULL || task->valid == FALSE)
462 /* Calculate the next timeout for select() */
463 queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
464 queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
465 if (queue->timeout.tv_sec < 0)
466 queue->timeout.tv_sec = 0;
468 /* We wouldn't want to go under zero, check for it. */
469 if (queue->timeout.tv_usec < 0) {
470 queue->timeout.tv_sec -= 1;
471 if (queue->timeout.tv_sec < 0)
472 queue->timeout.tv_sec = 0;
473 queue->timeout.tv_usec += 1000000L;
476 /* We've got the timeout value */
479 /* Task is not valid, remove it and try next one. */
480 silc_schedule_task_remove(queue, task);
482 if (queue->task == NULL)
487 /* Save the timeout */
489 schedule->timeout = &queue->timeout;
490 SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout->tv_sec,
491 schedule->timeout->tv_usec));
495 /* Runs the scheduler once and then returns. */
497 bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
499 struct timeval timeout;
502 SILC_LOG_DEBUG(("In scheduler loop"));
504 if (!schedule->is_locked)
505 silc_mutex_lock(schedule->lock);
507 /* If the task queues aren't initialized or we aren't valid anymore
509 if ((!schedule->fd_queue && !schedule->timeout_queue
510 && !schedule->generic_queue) || schedule->valid == FALSE) {
511 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
512 if (!schedule->is_locked)
513 silc_mutex_unlock(schedule->lock);
517 /* Calculate next timeout for silc_select(). This is the timeout value
518 when at earliest some of the timeout tasks expire. */
519 silc_mutex_lock(schedule->timeout_queue->lock);
520 silc_schedule_select_timeout(schedule);
521 silc_mutex_unlock(schedule->timeout_queue->lock);
523 if (timeout_usecs >= 0) {
525 timeout.tv_usec = timeout_usecs;
526 schedule->timeout = &timeout;
529 silc_mutex_unlock(schedule->lock);
531 /* This is the main select(). The program blocks here until some
532 of the selected file descriptors change status or the selected
534 SILC_LOG_DEBUG(("Select"));
535 ret = silc_select(schedule->fd_list, schedule->last_fd + 1,
538 silc_mutex_lock(schedule->lock);
545 SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
549 silc_mutex_lock(schedule->timeout_queue->lock);
550 silc_schedule_dispatch_timeout(schedule);
551 silc_mutex_unlock(schedule->timeout_queue->lock);
554 /* There is some data available now */
555 SILC_LOG_DEBUG(("Running non-timeout tasks"));
556 silc_schedule_dispatch_nontimeout(schedule);
560 if (!schedule->is_locked)
561 silc_mutex_unlock(schedule->lock);
566 /* The SILC scheduler. This is actually the main routine in SILC programs.
567 When this returns the program is to be ended. Before this function can
568 be called, one must call silc_schedule_init function. */
570 void silc_schedule(SilcSchedule schedule)
572 SILC_LOG_DEBUG(("Running scheduler"));
574 if (schedule->valid == FALSE) {
575 SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
579 silc_mutex_lock(schedule->lock);
580 schedule->is_locked = TRUE;
582 /* Start the scheduler loop */
583 while (silc_schedule_one(schedule, -1))
586 silc_mutex_unlock(schedule->lock);
589 /* Wakes up the scheduler. This is used only in multi-threaded
590 environments where threads may add new tasks or remove old tasks
591 from task queues. This is called to wake up the scheduler in the
592 main thread so that it detects the changes in the task queues.
593 If threads support is not compiled in this function has no effect.
594 Implementation of this function is platform specific. */
596 void silc_schedule_wakeup(SilcSchedule schedule)
599 SILC_LOG_DEBUG(("Wakeup scheduler"));
600 silc_mutex_lock(schedule->lock);
601 silc_schedule_wakeup_internal(schedule->wakeup);
602 silc_mutex_unlock(schedule->lock);
606 /* Add new task to the scheduler */
608 SilcTask silc_schedule_task_add(SilcSchedule schedule, uint32 fd,
609 SilcTaskCallback callback, void *context,
610 long seconds, long useconds,
612 SilcTaskPriority priority)
618 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", fd,
621 queue = SILC_SCHEDULE_GET_QUEUE(type);
623 /* If the task is generic task, we check whether this task has already
624 been registered. Generic tasks are registered only once and after that
625 the same task applies to all file descriptors to be registered. */
626 if (type == SILC_TASK_GENERIC) {
627 silc_mutex_lock(queue->lock);
630 SilcTask task = queue->task;
632 if ((task->callback == callback) && (task->context == context)) {
633 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
635 silc_mutex_unlock(queue->lock);
637 /* Add the fd to be listened, the task found now applies to this
639 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
643 if (queue->task == task->next)
650 silc_mutex_unlock(queue->lock);
653 newtask = silc_calloc(1, sizeof(*newtask));
655 newtask->context = context;
656 newtask->callback = callback;
657 newtask->valid = TRUE;
658 newtask->priority = priority;
659 newtask->type = type;
660 newtask->next = newtask;
661 newtask->prev = newtask;
663 /* Create timeout if marked to be timeout task */
664 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
665 silc_gettimeofday(&newtask->timeout);
666 newtask->timeout.tv_sec += seconds + (useconds / 1000000L);
667 newtask->timeout.tv_usec += (useconds % 1000000L);
668 if (newtask->timeout.tv_usec > 999999L) {
669 newtask->timeout.tv_sec += 1;
670 newtask->timeout.tv_usec -= 1000000L;
675 /* If the task is non-timeout task we have to tell the scheduler that we
676 would like to have these tasks scheduled at some odd distant future. */
677 if (type != SILC_TASK_TIMEOUT)
678 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
680 silc_mutex_lock(queue->lock);
682 /* Is this first task of the queue? */
683 if (queue->task == NULL) {
684 queue->task = newtask;
685 silc_mutex_unlock(queue->lock);
690 newtask = silc_task_add_timeout(queue, newtask, priority);
692 newtask = silc_task_add(queue, newtask, priority);
694 silc_mutex_unlock(queue->lock);
699 /* Removes a task from the scheduler */
701 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
703 SilcTaskQueue queue = SILC_SCHEDULE_GET_QUEUE(task->type);
705 /* Unregister all tasks */
706 if (task == SILC_ALL_TASKS) {
708 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
710 silc_mutex_lock(queue->lock);
713 silc_mutex_unlock(queue->lock);
722 if (queue->task == next->next)
727 silc_mutex_unlock(queue->lock);
731 SILC_LOG_DEBUG(("Unregistering task"));
733 silc_mutex_lock(queue->lock);
735 /* Unregister the specific task */
739 silc_mutex_unlock(queue->lock);
742 /* Remove task by fd */
744 void silc_schedule_task_del_by_fd(SilcSchedule schedule, uint32 fd)
746 silc_task_del_by_fd(schedule->timeout_queue, fd);
747 silc_task_del_by_fd(schedule->fd_queue, fd);
748 silc_task_del_by_fd(schedule->generic_queue, fd);
751 /* Remove task by task callback. */
753 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
754 SilcTaskCallback callback)
756 silc_task_del_by_callback(schedule->timeout_queue, callback);
757 silc_task_del_by_callback(schedule->fd_queue, callback);
758 silc_task_del_by_callback(schedule->generic_queue, callback);
761 /* Remove task by context. */
763 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
765 silc_task_del_by_context(schedule->timeout_queue, context);
766 silc_task_del_by_context(schedule->fd_queue, context);
767 silc_task_del_by_context(schedule->generic_queue, context);
770 /* Sets a file descriptor to be listened by select() in scheduler. One can
771 call this directly if wanted. This can be called multiple times for
772 one file descriptor to set different iomasks. */
774 void silc_schedule_set_listen_fd(SilcSchedule schedule,
775 uint32 fd, SilcTaskEvent iomask)
780 silc_mutex_lock(schedule->lock);
782 for (i = 0; i < schedule->max_fd; i++)
783 if (schedule->fd_list[i].fd == fd) {
784 schedule->fd_list[i].fd = fd;
785 schedule->fd_list[i].events = iomask;
786 if (i > schedule->last_fd)
787 schedule->last_fd = i;
793 for (i = 0; i < schedule->max_fd; i++)
794 if (schedule->fd_list[i].events == 0) {
795 schedule->fd_list[i].fd = fd;
796 schedule->fd_list[i].events = iomask;
797 if (i > schedule->last_fd)
798 schedule->last_fd = i;
802 silc_mutex_unlock(schedule->lock);
805 /* Removes a file descriptor from listen list. */
807 void silc_schedule_unset_listen_fd(SilcSchedule schedule, uint32 fd)
811 silc_mutex_lock(schedule->lock);
813 for (i = 0; i < schedule->max_fd; i++)
814 if (schedule->fd_list[i].fd == fd) {
815 schedule->fd_list[i].fd = 0;
816 schedule->fd_list[i].events = 0;
817 if (schedule->last_fd == i)
818 schedule->last_fd = schedule->max_fd - 1;
822 silc_mutex_unlock(schedule->lock);
825 /* Allocates a newtask task queue into the scheduler */
827 static void silc_task_queue_alloc(SilcTaskQueue *queue)
829 *queue = silc_calloc(1, sizeof(**queue));
830 silc_mutex_alloc(&(*queue)->lock);
833 /* Free's a task queue. */
835 static void silc_task_queue_free(SilcTaskQueue queue)
837 silc_mutex_free(queue->lock);
841 /* Return task by its fd. */
843 static SilcTask silc_task_find(SilcTaskQueue queue, uint32 fd)
855 if (queue->task == next->next)
863 /* Adds a non-timeout task into the task queue. This function is used
864 by silc_task_register function. Returns a pointer to the registered
867 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
868 SilcTaskPriority priority)
870 SilcTask task, next, prev;
872 /* Take the first task in the queue */
876 case SILC_TASK_PRI_LOW:
877 /* Lowest priority. The task is added at the end of the list. */
879 newtask->prev = prev;
880 newtask->next = task;
881 prev->next = newtask;
882 task->prev = newtask;
884 case SILC_TASK_PRI_NORMAL:
885 /* Normal priority. The task is added before lower priority tasks
886 but after tasks with higher priority. */
888 while(prev != task) {
889 if (prev->priority > SILC_TASK_PRI_LOW)
894 /* There are only lower priorities in the list, we will
895 sit before them and become the first task in the queue. */
897 newtask->prev = prev;
898 newtask->next = task;
899 task->prev = newtask;
900 prev->next = newtask;
902 /* We are now the first task in queue */
903 queue->task = newtask;
905 /* Found a spot from the list, add the task to the list. */
907 newtask->prev = prev;
908 newtask->next = next;
909 prev->next = newtask;
910 next->prev = newtask;
921 /* Return the timeout task with smallest timeout. */
923 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
937 if (silc_schedule_task_timeout_compare(&prev->timeout, &task->timeout))
946 /* Adds a timeout task into the task queue. This function is used by
947 silc_task_register function. Returns a pointer to the registered
948 task. Timeout tasks are sorted by their timeout value in ascending
949 order. The priority matters if there are more than one task with
952 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
953 SilcTaskPriority priority)
955 SilcTask task, prev, next;
957 /* Take the first task in the queue */
960 /* Take last task from the list */
964 case SILC_TASK_PRI_LOW:
965 /* Lowest priority. The task is added at the end of the list. */
966 while(prev != task) {
968 /* If we have longer timeout than with the task head of us
969 we have found our spot. */
970 if (silc_schedule_task_timeout_compare(&prev->timeout,
974 /* If we are equal size of timeout we will be after it. */
975 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
979 /* We have shorter timeout, compare to next one. */
982 /* Found a spot from the list, add the task to the list. */
984 newtask->prev = prev;
985 newtask->next = next;
986 prev->next = newtask;
987 next->prev = newtask;
990 /* Check if we are going to be the first task in the queue */
991 if (silc_schedule_task_timeout_compare(&prev->timeout,
994 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
998 /* We are now the first task in queue */
999 queue->task = newtask;
1002 case SILC_TASK_PRI_NORMAL:
1003 /* Normal priority. The task is added before lower priority tasks
1004 but after tasks with higher priority. */
1005 while(prev != task) {
1007 /* If we have longer timeout than with the task head of us
1008 we have found our spot. */
1009 if (silc_schedule_task_timeout_compare(&prev->timeout,
1013 /* If we are equal size of timeout, priority kicks in place. */
1014 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1016 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1019 /* We have shorter timeout or higher priority, compare to next one. */
1022 /* Found a spot from the list, add the task to the list. */
1024 newtask->prev = prev;
1025 newtask->next = next;
1026 prev->next = newtask;
1027 next->prev = newtask;
1030 /* Check if we are going to be the first task in the queue */
1031 if (silc_schedule_task_timeout_compare(&prev->timeout,
1034 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1036 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1039 /* We are now the first task in queue */
1040 queue->task = newtask;
1051 /* Removes (unregisters) a task from particular task queue. This function
1052 is used internally by scheduler. This must be called holding the
1055 static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task)
1057 SilcTask first, old, next;
1066 first = queue->task;
1068 /* Unregister all tasks in queue */
1069 if (task == SILC_ALL_TASKS) {
1070 SILC_LOG_DEBUG(("Removing all tasks at once"));
1075 silc_free(next->prev);
1084 SILC_LOG_DEBUG(("Removing task"));
1086 /* Unregister the task */
1090 SilcTask prev, next;
1097 if (prev == old && next == old)
1099 if (queue->task == old)
1100 queue->task = silc_task_get_first(queue, next);
1113 /* Compare two time values. If the first argument is smaller than the
1114 second this function returns TRUE. */
1116 static int silc_schedule_task_timeout_compare(struct timeval *smaller,
1117 struct timeval *bigger)
1119 if ((smaller->tv_sec < bigger->tv_sec) ||
1120 ((smaller->tv_sec == bigger->tv_sec) &&
1121 (smaller->tv_usec < bigger->tv_usec)))
1127 static void silc_task_del_by_fd(SilcTaskQueue queue, uint32 fd)
1131 SILC_LOG_DEBUG(("Unregister task by fd"));
1133 silc_mutex_lock(queue->lock);
1136 silc_mutex_unlock(queue->lock);
1144 next->valid = FALSE;
1145 if (queue->task == next->next)
1150 silc_mutex_unlock(queue->lock);
1153 static void silc_task_del_by_callback(SilcTaskQueue queue,
1154 SilcTaskCallback callback)
1158 SILC_LOG_DEBUG(("Unregister task by callback"));
1160 silc_mutex_lock(queue->lock);
1163 silc_mutex_unlock(queue->lock);
1170 if (next->callback == callback)
1171 next->valid = FALSE;
1172 if (queue->task == next->next)
1177 silc_mutex_unlock(queue->lock);
1180 static void silc_task_del_by_context(SilcTaskQueue queue, void *context)
1184 SILC_LOG_DEBUG(("Unregister task by context"));
1186 silc_mutex_lock(queue->lock);
1189 silc_mutex_unlock(queue->lock);
1196 if (next->context == context)
1197 next->valid = FALSE;
1198 if (queue->task == next->next)
1203 silc_mutex_unlock(queue->lock);