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);
206 /* Initialize the scheduler */
207 schedule->fd_list = silc_calloc(max_tasks, sizeof(*schedule->fd_list));
208 schedule->max_fd = max_tasks;
209 schedule->timeout = NULL;
210 schedule->valid = TRUE;
212 /* Allocate scheduler lock */
213 silc_mutex_alloc(&schedule->lock);
215 /* Initialize the wakeup, for multi-threads support */
216 schedule->wakeup = silc_schedule_wakeup_init(schedule);
221 /* Uninitializes the schedule. This is called when the program is ready
222 to end. This removes all tasks and task queues. Returns FALSE if the
223 scheduler could not be uninitialized. This happens when the scheduler
224 is still valid and silc_schedule_stop has not been called. */
226 bool silc_schedule_uninit(SilcSchedule schedule)
228 SILC_LOG_DEBUG(("Uninitializing scheduler"));
230 if (schedule->valid == TRUE)
233 /* Unregister all tasks */
234 silc_schedule_task_remove(schedule->fd_queue, SILC_ALL_TASKS);
235 silc_schedule_task_remove(schedule->timeout_queue, SILC_ALL_TASKS);
236 silc_schedule_task_remove(schedule->generic_queue, SILC_ALL_TASKS);
238 /* Unregister all task queues */
239 silc_task_queue_free(schedule->fd_queue);
240 silc_task_queue_free(schedule->timeout_queue);
241 silc_task_queue_free(schedule->generic_queue);
243 silc_free(schedule->fd_list);
245 /* Uninit the wakeup */
246 silc_schedule_wakeup_uninit(schedule->wakeup);
248 silc_mutex_free(schedule->lock);
253 /* Stops the schedule even if it is not supposed to be stopped yet.
254 After calling this, one should call silc_schedule_uninit (after the
255 silc_schedule has returned). */
257 void silc_schedule_stop(SilcSchedule schedule)
259 SILC_LOG_DEBUG(("Stopping scheduler"));
260 schedule->valid = FALSE;
263 /* Executes nontimeout tasks. It then checks whether any of ther fd tasks
264 was signaled by the silc_select. If some task was not signaled then
265 all generic tasks are executed for that task. The generic tasks are
266 never executed for task that has explicit fd task set. */
267 /* This holds the schedule->lock and the queue locks. */
269 static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule)
272 int i, last_fd = schedule->last_fd;
275 for (i = 0; i <= last_fd; i++) {
276 if (schedule->fd_list[i].events == 0)
279 fd = schedule->fd_list[i].fd;
281 /* First check whether this fd has task in the fd queue */
282 silc_mutex_lock(schedule->fd_queue->lock);
283 task = silc_task_find(schedule->fd_queue, fd);
285 /* If the task was found then execute its callbacks. If not then
286 execute all generic tasks for that fd. */
288 /* Validity of the task is checked always before and after
289 execution beacuse the task might have been unregistered
290 in the callback function, ie. it is not valid anymore. */
292 /* Is the task ready for reading */
293 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
294 silc_mutex_unlock(schedule->fd_queue->lock);
295 silc_mutex_unlock(schedule->lock);
296 task->callback(schedule, SILC_TASK_READ, task->fd, task->context);
297 silc_mutex_lock(schedule->lock);
298 silc_mutex_lock(schedule->fd_queue->lock);
301 /* Is the task ready for writing */
302 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
303 silc_mutex_unlock(schedule->fd_queue->lock);
304 silc_mutex_unlock(schedule->lock);
305 task->callback(schedule, SILC_TASK_WRITE, task->fd, task->context);
306 silc_mutex_lock(schedule->lock);
307 silc_mutex_lock(schedule->fd_queue->lock);
311 silc_schedule_task_remove(schedule->fd_queue, task);
313 silc_mutex_unlock(schedule->fd_queue->lock);
315 /* Run generic tasks for this fd. */
317 silc_mutex_unlock(schedule->fd_queue->lock);
319 silc_mutex_lock(schedule->generic_queue->lock);
320 if (!schedule->generic_queue->task) {
321 silc_mutex_unlock(schedule->generic_queue->lock);
325 task = schedule->generic_queue->task;
327 /* Validity of the task is checked always before and after
328 execution beacuse the task might have been unregistered
329 in the callback function, ie. it is not valid anymore. */
331 /* Is the task ready for reading */
332 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
333 silc_mutex_unlock(schedule->generic_queue->lock);
334 silc_mutex_unlock(schedule->lock);
335 task->callback(schedule, SILC_TASK_READ, fd, task->context);
336 silc_mutex_lock(schedule->lock);
337 silc_mutex_lock(schedule->generic_queue->lock);
340 /* Is the task ready for writing */
341 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
342 silc_mutex_unlock(schedule->generic_queue->lock);
343 silc_mutex_unlock(schedule->lock);
344 task->callback(schedule, SILC_TASK_WRITE, fd, task->context);
345 silc_mutex_lock(schedule->lock);
346 silc_mutex_lock(schedule->generic_queue->lock);
350 /* Invalid (unregistered) tasks are removed from the
352 if (schedule->generic_queue->task == task->next) {
353 silc_schedule_task_remove(schedule->generic_queue, task);
354 silc_mutex_unlock(schedule->generic_queue->lock);
359 silc_schedule_task_remove(schedule->generic_queue, task);
363 /* Break if there isn't more tasks in the queue */
364 if (schedule->generic_queue->task == task->next)
370 silc_mutex_unlock(schedule->generic_queue->lock);
375 /* Executes all tasks whose timeout has expired. The task is removed from
376 the task queue after the callback function has returned. Also, invalid
377 tasks are removed here. We don't have to care about priorities because
378 tasks are already sorted in their priority order at the registration
380 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
382 static void silc_schedule_dispatch_timeout(SilcSchedule schedule)
384 SilcTaskQueue queue = schedule->timeout_queue;
386 struct timeval curtime;
388 SILC_LOG_DEBUG(("Running timeout tasks"));
390 silc_gettimeofday(&curtime);
392 queue = schedule->timeout_queue;
393 if (queue && queue->task) {
396 /* Walk thorugh all tasks in the particular task queue and run all
397 the expired tasks. */
399 /* Execute the task if the timeout has expired */
400 if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
402 silc_mutex_unlock(queue->lock);
403 silc_mutex_unlock(schedule->lock);
404 task->callback(schedule, SILC_TASK_EXPIRE, task->fd, task->context);
405 silc_mutex_lock(schedule->lock);
406 silc_mutex_lock(queue->lock);
409 /* Break if there isn't more tasks in the queue */
410 if (queue->task == task->next) {
411 silc_schedule_task_remove(queue, task);
417 /* Remove the task from queue */
418 silc_schedule_task_remove(queue, task->prev);
420 /* The timeout hasn't expired, check for next one */
422 /* Break if there isn't more tasks in the queue */
423 if (queue->task == task->next)
432 /* Calculates next timeout for select(). This is the timeout value
433 when at earliest some of the timeout tasks expire. If this is in the
434 past, they will be run now. */
435 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
437 static void silc_schedule_select_timeout(SilcSchedule schedule)
439 SilcTaskQueue queue = schedule->timeout_queue;
441 struct timeval curtime;
443 /* Get the current time */
444 silc_gettimeofday(&curtime);
445 schedule->timeout = NULL;
447 /* First task in the task queue has always the smallest timeout. */
450 if (task && task->valid == TRUE) {
451 /* If the timeout is in past, we will run the task and all other
452 timeout tasks from the past. */
453 if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
454 silc_schedule_dispatch_timeout(schedule);
456 /* The task(s) has expired and doesn't exist on the task queue
457 anymore. We continue with new timeout. */
458 queue = schedule->timeout_queue;
460 if (task == NULL || task->valid == FALSE)
464 /* Calculate the next timeout for select() */
465 queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
466 queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
467 if (queue->timeout.tv_sec < 0)
468 queue->timeout.tv_sec = 0;
470 /* We wouldn't want to go under zero, check for it. */
471 if (queue->timeout.tv_usec < 0) {
472 queue->timeout.tv_sec -= 1;
473 if (queue->timeout.tv_sec < 0)
474 queue->timeout.tv_sec = 0;
475 queue->timeout.tv_usec += 1000000L;
478 /* We've got the timeout value */
481 /* Task is not valid, remove it and try next one. */
482 silc_schedule_task_remove(queue, task);
484 if (queue->task == NULL)
489 /* Save the timeout */
491 schedule->timeout = &queue->timeout;
492 SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout->tv_sec,
493 schedule->timeout->tv_usec));
497 /* Runs the scheduler once and then returns. */
499 bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
501 struct timeval timeout;
504 SILC_LOG_DEBUG(("In scheduler loop"));
506 if (!schedule->is_locked)
507 silc_mutex_lock(schedule->lock);
509 /* If the task queues aren't initialized or we aren't valid anymore
511 if ((!schedule->fd_queue && !schedule->timeout_queue
512 && !schedule->generic_queue) || schedule->valid == FALSE) {
513 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
514 if (!schedule->is_locked)
515 silc_mutex_unlock(schedule->lock);
519 /* Calculate next timeout for silc_select(). This is the timeout value
520 when at earliest some of the timeout tasks expire. */
521 silc_mutex_lock(schedule->timeout_queue->lock);
522 silc_schedule_select_timeout(schedule);
523 silc_mutex_unlock(schedule->timeout_queue->lock);
525 if (timeout_usecs >= 0) {
527 timeout.tv_usec = timeout_usecs;
528 schedule->timeout = &timeout;
531 silc_mutex_unlock(schedule->lock);
533 /* This is the main select(). The program blocks here until some
534 of the selected file descriptors change status or the selected
536 SILC_LOG_DEBUG(("Select"));
537 ret = silc_select(schedule->fd_list, schedule->last_fd + 1,
540 silc_mutex_lock(schedule->lock);
547 SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
551 silc_mutex_lock(schedule->timeout_queue->lock);
552 silc_schedule_dispatch_timeout(schedule);
553 silc_mutex_unlock(schedule->timeout_queue->lock);
556 /* There is some data available now */
557 SILC_LOG_DEBUG(("Running non-timeout tasks"));
558 silc_schedule_dispatch_nontimeout(schedule);
562 if (!schedule->is_locked)
563 silc_mutex_unlock(schedule->lock);
568 /* The SILC scheduler. This is actually the main routine in SILC programs.
569 When this returns the program is to be ended. Before this function can
570 be called, one must call silc_schedule_init function. */
572 void silc_schedule(SilcSchedule schedule)
574 SILC_LOG_DEBUG(("Running scheduler"));
576 if (schedule->valid == FALSE) {
577 SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
581 silc_mutex_lock(schedule->lock);
582 schedule->is_locked = TRUE;
584 /* Start the scheduler loop */
585 while (silc_schedule_one(schedule, -1))
588 silc_mutex_unlock(schedule->lock);
591 /* Wakes up the scheduler. This is used only in multi-threaded
592 environments where threads may add new tasks or remove old tasks
593 from task queues. This is called to wake up the scheduler in the
594 main thread so that it detects the changes in the task queues.
595 If threads support is not compiled in this function has no effect.
596 Implementation of this function is platform specific. */
598 void silc_schedule_wakeup(SilcSchedule schedule)
601 SILC_LOG_DEBUG(("Wakeup scheduler"));
602 silc_mutex_lock(schedule->lock);
603 silc_schedule_wakeup_internal(schedule->wakeup);
604 silc_mutex_unlock(schedule->lock);
608 /* Add new task to the scheduler */
610 SilcTask silc_schedule_task_add(SilcSchedule schedule, uint32 fd,
611 SilcTaskCallback callback, void *context,
612 long seconds, long useconds,
614 SilcTaskPriority priority)
620 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", fd,
623 queue = SILC_SCHEDULE_GET_QUEUE(type);
625 /* If the task is generic task, we check whether this task has already
626 been registered. Generic tasks are registered only once and after that
627 the same task applies to all file descriptors to be registered. */
628 if (type == SILC_TASK_GENERIC) {
629 silc_mutex_lock(queue->lock);
632 SilcTask task = queue->task;
634 if ((task->callback == callback) && (task->context == context)) {
635 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
637 silc_mutex_unlock(queue->lock);
639 /* Add the fd to be listened, the task found now applies to this
641 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
645 if (queue->task == task->next)
652 silc_mutex_unlock(queue->lock);
655 newtask = silc_calloc(1, sizeof(*newtask));
657 newtask->context = context;
658 newtask->callback = callback;
659 newtask->valid = TRUE;
660 newtask->priority = priority;
661 newtask->type = type;
662 newtask->next = newtask;
663 newtask->prev = newtask;
665 /* Create timeout if marked to be timeout task */
666 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
667 silc_gettimeofday(&newtask->timeout);
668 newtask->timeout.tv_sec += seconds + (useconds / 1000000L);
669 newtask->timeout.tv_usec += (useconds % 1000000L);
670 if (newtask->timeout.tv_usec > 999999L) {
671 newtask->timeout.tv_sec += 1;
672 newtask->timeout.tv_usec -= 1000000L;
677 /* If the task is non-timeout task we have to tell the scheduler that we
678 would like to have these tasks scheduled at some odd distant future. */
679 if (type != SILC_TASK_TIMEOUT)
680 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
682 silc_mutex_lock(queue->lock);
684 /* Is this first task of the queue? */
685 if (queue->task == NULL) {
686 queue->task = newtask;
687 silc_mutex_unlock(queue->lock);
692 newtask = silc_task_add_timeout(queue, newtask, priority);
694 newtask = silc_task_add(queue, newtask, priority);
696 silc_mutex_unlock(queue->lock);
701 /* Removes a task from the scheduler */
703 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
705 SilcTaskQueue queue = SILC_SCHEDULE_GET_QUEUE(task->type);
707 /* Unregister all tasks */
708 if (task == SILC_ALL_TASKS) {
710 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
712 silc_mutex_lock(queue->lock);
715 silc_mutex_unlock(queue->lock);
724 if (queue->task == next->next)
729 silc_mutex_unlock(queue->lock);
733 SILC_LOG_DEBUG(("Unregistering task"));
735 silc_mutex_lock(queue->lock);
737 /* Unregister the specific task */
741 silc_mutex_unlock(queue->lock);
744 /* Remove task by fd */
746 void silc_schedule_task_del_by_fd(SilcSchedule schedule, uint32 fd)
748 SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
750 silc_task_del_by_fd(schedule->timeout_queue, fd);
751 silc_task_del_by_fd(schedule->fd_queue, fd);
754 /* Remove task by task callback. */
756 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
757 SilcTaskCallback callback)
759 SILC_LOG_DEBUG(("Unregister task by callback"));
761 silc_task_del_by_callback(schedule->timeout_queue, callback);
762 silc_task_del_by_callback(schedule->fd_queue, callback);
763 silc_task_del_by_callback(schedule->generic_queue, callback);
766 /* Remove task by context. */
768 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
770 SILC_LOG_DEBUG(("Unregister task by context"));
772 silc_task_del_by_context(schedule->timeout_queue, context);
773 silc_task_del_by_context(schedule->fd_queue, context);
774 silc_task_del_by_context(schedule->generic_queue, context);
777 /* Sets a file descriptor to be listened by select() in scheduler. One can
778 call this directly if wanted. This can be called multiple times for
779 one file descriptor to set different iomasks. */
781 void silc_schedule_set_listen_fd(SilcSchedule schedule,
782 uint32 fd, SilcTaskEvent iomask)
787 silc_mutex_lock(schedule->lock);
789 for (i = 0; i < schedule->max_fd; i++)
790 if (schedule->fd_list[i].fd == fd) {
791 schedule->fd_list[i].fd = fd;
792 schedule->fd_list[i].events = iomask;
793 if (i > schedule->last_fd)
794 schedule->last_fd = i;
800 for (i = 0; i < schedule->max_fd; i++)
801 if (schedule->fd_list[i].events == 0) {
802 schedule->fd_list[i].fd = fd;
803 schedule->fd_list[i].events = iomask;
804 if (i > schedule->last_fd)
805 schedule->last_fd = i;
809 silc_mutex_unlock(schedule->lock);
812 /* Removes a file descriptor from listen list. */
814 void silc_schedule_unset_listen_fd(SilcSchedule schedule, uint32 fd)
818 silc_mutex_lock(schedule->lock);
820 SILC_LOG_DEBUG(("Unset listen fd %d", fd));
822 for (i = 0; i < schedule->max_fd; i++)
823 if (schedule->fd_list[i].fd == fd) {
824 schedule->fd_list[i].fd = 0;
825 schedule->fd_list[i].events = 0;
826 if (schedule->last_fd == i)
827 schedule->last_fd = schedule->max_fd - 1;
831 silc_mutex_unlock(schedule->lock);
834 /* Allocates a newtask task queue into the scheduler */
836 static void silc_task_queue_alloc(SilcTaskQueue *queue)
838 *queue = silc_calloc(1, sizeof(**queue));
839 silc_mutex_alloc(&(*queue)->lock);
842 /* Free's a task queue. */
844 static void silc_task_queue_free(SilcTaskQueue queue)
846 silc_mutex_free(queue->lock);
850 /* Return task by its fd. */
852 static SilcTask silc_task_find(SilcTaskQueue queue, uint32 fd)
864 if (queue->task == next->next)
872 /* Adds a non-timeout task into the task queue. This function is used
873 by silc_task_register function. Returns a pointer to the registered
876 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
877 SilcTaskPriority priority)
879 SilcTask task, next, prev;
881 /* Take the first task in the queue */
885 case SILC_TASK_PRI_LOW:
886 /* Lowest priority. The task is added at the end of the list. */
888 newtask->prev = prev;
889 newtask->next = task;
890 prev->next = newtask;
891 task->prev = newtask;
893 case SILC_TASK_PRI_NORMAL:
894 /* Normal priority. The task is added before lower priority tasks
895 but after tasks with higher priority. */
897 while(prev != task) {
898 if (prev->priority > SILC_TASK_PRI_LOW)
903 /* There are only lower priorities in the list, we will
904 sit before them and become the first task in the queue. */
906 newtask->prev = prev;
907 newtask->next = task;
908 task->prev = newtask;
909 prev->next = newtask;
911 /* We are now the first task in queue */
912 queue->task = newtask;
914 /* Found a spot from the list, add the task to the list. */
916 newtask->prev = prev;
917 newtask->next = next;
918 prev->next = newtask;
919 next->prev = newtask;
930 /* Return the timeout task with smallest timeout. */
932 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
946 if (silc_schedule_task_timeout_compare(&prev->timeout, &task->timeout))
955 /* Adds a timeout task into the task queue. This function is used by
956 silc_task_register function. Returns a pointer to the registered
957 task. Timeout tasks are sorted by their timeout value in ascending
958 order. The priority matters if there are more than one task with
961 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
962 SilcTaskPriority priority)
964 SilcTask task, prev, next;
966 /* Take the first task in the queue */
969 /* Take last task from the list */
973 case SILC_TASK_PRI_LOW:
974 /* Lowest priority. The task is added at the end of the list. */
975 while(prev != task) {
977 /* If we have longer timeout than with the task head of us
978 we have found our spot. */
979 if (silc_schedule_task_timeout_compare(&prev->timeout,
983 /* If we are equal size of timeout we will be after it. */
984 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
988 /* We have shorter timeout, compare to next one. */
991 /* Found a spot from the list, add the task to the list. */
993 newtask->prev = prev;
994 newtask->next = next;
995 prev->next = newtask;
996 next->prev = newtask;
999 /* Check if we are going to be the first task in the queue */
1000 if (silc_schedule_task_timeout_compare(&prev->timeout,
1003 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1007 /* We are now the first task in queue */
1008 queue->task = newtask;
1011 case SILC_TASK_PRI_NORMAL:
1012 /* Normal priority. The task is added before lower priority tasks
1013 but after tasks with higher priority. */
1014 while(prev != task) {
1016 /* If we have longer timeout than with the task head of us
1017 we have found our spot. */
1018 if (silc_schedule_task_timeout_compare(&prev->timeout,
1022 /* If we are equal size of timeout, priority kicks in place. */
1023 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1025 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1028 /* We have shorter timeout or higher priority, compare to next one. */
1031 /* Found a spot from the list, add the task to the list. */
1033 newtask->prev = prev;
1034 newtask->next = next;
1035 prev->next = newtask;
1036 next->prev = newtask;
1039 /* Check if we are going to be the first task in the queue */
1040 if (silc_schedule_task_timeout_compare(&prev->timeout,
1043 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1045 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1048 /* We are now the first task in queue */
1049 queue->task = newtask;
1060 /* Removes (unregisters) a task from particular task queue. This function
1061 is used internally by scheduler. This must be called holding the
1064 static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task)
1066 SilcTask first, old, next;
1068 if (!queue || !task)
1075 first = queue->task;
1077 /* Unregister all tasks in queue */
1078 if (task == SILC_ALL_TASKS) {
1079 SILC_LOG_DEBUG(("Removing all tasks at once"));
1084 silc_free(next->prev);
1093 SILC_LOG_DEBUG(("Removing task"));
1095 /* Unregister the task */
1099 SilcTask prev, next;
1106 if (prev == old && next == old)
1108 if (queue->task == old)
1109 queue->task = silc_task_get_first(queue, next);
1122 /* Compare two time values. If the first argument is smaller than the
1123 second this function returns TRUE. */
1125 static int silc_schedule_task_timeout_compare(struct timeval *smaller,
1126 struct timeval *bigger)
1128 if ((smaller->tv_sec < bigger->tv_sec) ||
1129 ((smaller->tv_sec == bigger->tv_sec) &&
1130 (smaller->tv_usec < bigger->tv_usec)))
1136 static void silc_task_del_by_fd(SilcTaskQueue queue, uint32 fd)
1140 silc_mutex_lock(queue->lock);
1143 silc_mutex_unlock(queue->lock);
1151 next->valid = FALSE;
1152 if (queue->task == next->next)
1157 silc_mutex_unlock(queue->lock);
1160 static void silc_task_del_by_callback(SilcTaskQueue queue,
1161 SilcTaskCallback callback)
1165 silc_mutex_lock(queue->lock);
1168 silc_mutex_unlock(queue->lock);
1175 if (next->callback == callback)
1176 next->valid = FALSE;
1177 if (queue->task == next->next)
1182 silc_mutex_unlock(queue->lock);
1185 static void silc_task_del_by_context(SilcTaskQueue queue, void *context)
1189 silc_mutex_lock(queue->lock);
1192 silc_mutex_unlock(queue->lock);
1199 if (next->context == context)
1200 next->valid = FALSE;
1201 if (queue->task == next->next)
1206 silc_mutex_unlock(queue->lock);