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)
271 int i, last_fd = schedule->last_fd;
273 for (i = 0; i <= 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);
750 /* Remove task by task callback. */
752 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
753 SilcTaskCallback callback)
755 silc_task_del_by_callback(schedule->timeout_queue, callback);
756 silc_task_del_by_callback(schedule->fd_queue, callback);
757 silc_task_del_by_callback(schedule->generic_queue, callback);
760 /* Remove task by context. */
762 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
764 silc_task_del_by_context(schedule->timeout_queue, context);
765 silc_task_del_by_context(schedule->fd_queue, context);
766 silc_task_del_by_context(schedule->generic_queue, context);
769 /* Sets a file descriptor to be listened by select() in scheduler. One can
770 call this directly if wanted. This can be called multiple times for
771 one file descriptor to set different iomasks. */
773 void silc_schedule_set_listen_fd(SilcSchedule schedule,
774 uint32 fd, SilcTaskEvent iomask)
779 silc_mutex_lock(schedule->lock);
781 for (i = 0; i < schedule->max_fd; i++)
782 if (schedule->fd_list[i].fd == fd) {
783 schedule->fd_list[i].fd = fd;
784 schedule->fd_list[i].events = iomask;
785 if (i > schedule->last_fd)
786 schedule->last_fd = i;
792 for (i = 0; i < schedule->max_fd; i++)
793 if (schedule->fd_list[i].events == 0) {
794 schedule->fd_list[i].fd = fd;
795 schedule->fd_list[i].events = iomask;
796 if (i > schedule->last_fd)
797 schedule->last_fd = i;
801 silc_mutex_unlock(schedule->lock);
804 /* Removes a file descriptor from listen list. */
806 void silc_schedule_unset_listen_fd(SilcSchedule schedule, uint32 fd)
810 silc_mutex_lock(schedule->lock);
812 for (i = 0; i < schedule->max_fd; i++)
813 if (schedule->fd_list[i].fd == fd) {
814 schedule->fd_list[i].fd = 0;
815 schedule->fd_list[i].events = 0;
816 if (schedule->last_fd == i)
817 schedule->last_fd = schedule->max_fd - 1;
821 silc_mutex_unlock(schedule->lock);
824 /* Allocates a newtask task queue into the scheduler */
826 static void silc_task_queue_alloc(SilcTaskQueue *queue)
828 *queue = silc_calloc(1, sizeof(**queue));
829 silc_mutex_alloc(&(*queue)->lock);
832 /* Free's a task queue. */
834 static void silc_task_queue_free(SilcTaskQueue queue)
836 silc_mutex_free(queue->lock);
840 /* Return task by its fd. */
842 static SilcTask silc_task_find(SilcTaskQueue queue, uint32 fd)
854 if (queue->task == next->next)
862 /* Adds a non-timeout task into the task queue. This function is used
863 by silc_task_register function. Returns a pointer to the registered
866 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
867 SilcTaskPriority priority)
869 SilcTask task, next, prev;
871 /* Take the first task in the queue */
875 case SILC_TASK_PRI_LOW:
876 /* Lowest priority. The task is added at the end of the list. */
878 newtask->prev = prev;
879 newtask->next = task;
880 prev->next = newtask;
881 task->prev = newtask;
883 case SILC_TASK_PRI_NORMAL:
884 /* Normal priority. The task is added before lower priority tasks
885 but after tasks with higher priority. */
887 while(prev != task) {
888 if (prev->priority > SILC_TASK_PRI_LOW)
893 /* There are only lower priorities in the list, we will
894 sit before them and become the first task in the queue. */
896 newtask->prev = prev;
897 newtask->next = task;
898 task->prev = newtask;
899 prev->next = newtask;
901 /* We are now the first task in queue */
902 queue->task = newtask;
904 /* Found a spot from the list, add the task to the list. */
906 newtask->prev = prev;
907 newtask->next = next;
908 prev->next = newtask;
909 next->prev = newtask;
920 /* Return the timeout task with smallest timeout. */
922 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
936 if (silc_schedule_task_timeout_compare(&prev->timeout, &task->timeout))
945 /* Adds a timeout task into the task queue. This function is used by
946 silc_task_register function. Returns a pointer to the registered
947 task. Timeout tasks are sorted by their timeout value in ascending
948 order. The priority matters if there are more than one task with
951 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
952 SilcTaskPriority priority)
954 SilcTask task, prev, next;
956 /* Take the first task in the queue */
959 /* Take last task from the list */
963 case SILC_TASK_PRI_LOW:
964 /* Lowest priority. The task is added at the end of the list. */
965 while(prev != task) {
967 /* If we have longer timeout than with the task head of us
968 we have found our spot. */
969 if (silc_schedule_task_timeout_compare(&prev->timeout,
973 /* If we are equal size of timeout we will be after it. */
974 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
978 /* We have shorter timeout, compare to next one. */
981 /* Found a spot from the list, add the task to the list. */
983 newtask->prev = prev;
984 newtask->next = next;
985 prev->next = newtask;
986 next->prev = newtask;
989 /* Check if we are going to be the first task in the queue */
990 if (silc_schedule_task_timeout_compare(&prev->timeout,
993 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
997 /* We are now the first task in queue */
998 queue->task = newtask;
1001 case SILC_TASK_PRI_NORMAL:
1002 /* Normal priority. The task is added before lower priority tasks
1003 but after tasks with higher priority. */
1004 while(prev != task) {
1006 /* If we have longer timeout than with the task head of us
1007 we have found our spot. */
1008 if (silc_schedule_task_timeout_compare(&prev->timeout,
1012 /* If we are equal size of timeout, priority kicks in place. */
1013 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1015 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1018 /* We have shorter timeout or higher priority, compare to next one. */
1021 /* Found a spot from the list, add the task to the list. */
1023 newtask->prev = prev;
1024 newtask->next = next;
1025 prev->next = newtask;
1026 next->prev = newtask;
1029 /* Check if we are going to be the first task in the queue */
1030 if (silc_schedule_task_timeout_compare(&prev->timeout,
1033 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1035 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1038 /* We are now the first task in queue */
1039 queue->task = newtask;
1050 /* Removes (unregisters) a task from particular task queue. This function
1051 is used internally by scheduler. This must be called holding the
1054 static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task)
1056 SilcTask first, old, next;
1058 if (!queue || !task)
1065 first = queue->task;
1067 /* Unregister all tasks in queue */
1068 if (task == SILC_ALL_TASKS) {
1069 SILC_LOG_DEBUG(("Removing all tasks at once"));
1074 silc_free(next->prev);
1083 SILC_LOG_DEBUG(("Removing task"));
1085 /* Unregister the task */
1089 SilcTask prev, next;
1096 if (prev == old && next == old)
1098 if (queue->task == old)
1099 queue->task = silc_task_get_first(queue, next);
1112 /* Compare two time values. If the first argument is smaller than the
1113 second this function returns TRUE. */
1115 static int silc_schedule_task_timeout_compare(struct timeval *smaller,
1116 struct timeval *bigger)
1118 if ((smaller->tv_sec < bigger->tv_sec) ||
1119 ((smaller->tv_sec == bigger->tv_sec) &&
1120 (smaller->tv_usec < bigger->tv_usec)))
1126 static void silc_task_del_by_fd(SilcTaskQueue queue, uint32 fd)
1130 SILC_LOG_DEBUG(("Unregister task by fd"));
1132 silc_mutex_lock(queue->lock);
1135 silc_mutex_unlock(queue->lock);
1143 next->valid = FALSE;
1144 if (queue->task == next->next)
1149 silc_mutex_unlock(queue->lock);
1152 static void silc_task_del_by_callback(SilcTaskQueue queue,
1153 SilcTaskCallback callback)
1157 SILC_LOG_DEBUG(("Unregister task by callback"));
1159 silc_mutex_lock(queue->lock);
1162 silc_mutex_unlock(queue->lock);
1169 if (next->callback == callback)
1170 next->valid = FALSE;
1171 if (queue->task == next->next)
1176 silc_mutex_unlock(queue->lock);
1179 static void silc_task_del_by_context(SilcTaskQueue queue, void *context)
1183 SILC_LOG_DEBUG(("Unregister task by context"));
1185 silc_mutex_lock(queue->lock);
1188 silc_mutex_unlock(queue->lock);
1195 if (next->context == context)
1196 next->valid = FALSE;
1197 if (queue->task == next->next)
1202 silc_mutex_unlock(queue->lock);