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 silc_mutex_lock(schedule->lock);
261 schedule->valid = FALSE;
262 silc_mutex_unlock(schedule->lock);
265 /* Executes nontimeout tasks. It then checks whether any of ther fd tasks
266 was signaled by the silc_select. If some task was not signaled then
267 all generic tasks are executed for that task. The generic tasks are
268 never executed for task that has explicit fd task set. */
269 /* This holds the schedule->lock and the queue locks. */
271 static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule)
274 int i, last_fd = schedule->last_fd;
277 for (i = 0; i <= last_fd; i++) {
278 if (schedule->fd_list[i].events == 0)
281 fd = schedule->fd_list[i].fd;
283 /* First check whether this fd has task in the fd queue */
284 silc_mutex_lock(schedule->fd_queue->lock);
285 task = silc_task_find(schedule->fd_queue, fd);
287 /* If the task was found then execute its callbacks. If not then
288 execute all generic tasks for that fd. */
290 /* Validity of the task is checked always before and after
291 execution beacuse the task might have been unregistered
292 in the callback function, ie. it is not valid anymore. */
294 /* Is the task ready for reading */
295 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
296 silc_mutex_unlock(schedule->fd_queue->lock);
297 silc_mutex_unlock(schedule->lock);
298 task->callback(schedule, SILC_TASK_READ, task->fd, task->context);
299 silc_mutex_lock(schedule->lock);
300 silc_mutex_lock(schedule->fd_queue->lock);
303 /* Is the task ready for writing */
304 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
305 silc_mutex_unlock(schedule->fd_queue->lock);
306 silc_mutex_unlock(schedule->lock);
307 task->callback(schedule, SILC_TASK_WRITE, task->fd, task->context);
308 silc_mutex_lock(schedule->lock);
309 silc_mutex_lock(schedule->fd_queue->lock);
313 silc_schedule_task_remove(schedule->fd_queue, task);
315 silc_mutex_unlock(schedule->fd_queue->lock);
317 /* Run generic tasks for this fd. */
319 silc_mutex_unlock(schedule->fd_queue->lock);
321 silc_mutex_lock(schedule->generic_queue->lock);
322 if (!schedule->generic_queue->task) {
323 silc_mutex_unlock(schedule->generic_queue->lock);
327 task = schedule->generic_queue->task;
329 /* Validity of the task is checked always before and after
330 execution beacuse the task might have been unregistered
331 in the callback function, ie. it is not valid anymore. */
333 /* Is the task ready for reading */
334 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
335 silc_mutex_unlock(schedule->generic_queue->lock);
336 silc_mutex_unlock(schedule->lock);
337 task->callback(schedule, SILC_TASK_READ, fd, task->context);
338 silc_mutex_lock(schedule->lock);
339 silc_mutex_lock(schedule->generic_queue->lock);
342 /* Is the task ready for writing */
343 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
344 silc_mutex_unlock(schedule->generic_queue->lock);
345 silc_mutex_unlock(schedule->lock);
346 task->callback(schedule, SILC_TASK_WRITE, fd, task->context);
347 silc_mutex_lock(schedule->lock);
348 silc_mutex_lock(schedule->generic_queue->lock);
352 /* Invalid (unregistered) tasks are removed from the
354 if (schedule->generic_queue->task == task->next) {
355 silc_schedule_task_remove(schedule->generic_queue, task);
356 silc_mutex_unlock(schedule->generic_queue->lock);
361 silc_schedule_task_remove(schedule->generic_queue, task);
365 /* Break if there isn't more tasks in the queue */
366 if (schedule->generic_queue->task == task->next)
372 silc_mutex_unlock(schedule->generic_queue->lock);
377 /* Executes all tasks whose timeout has expired. The task is removed from
378 the task queue after the callback function has returned. Also, invalid
379 tasks are removed here. We don't have to care about priorities because
380 tasks are already sorted in their priority order at the registration
382 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
384 static void silc_schedule_dispatch_timeout(SilcSchedule schedule)
386 SilcTaskQueue queue = schedule->timeout_queue;
388 struct timeval curtime;
390 SILC_LOG_DEBUG(("Running timeout tasks"));
392 silc_gettimeofday(&curtime);
394 queue = schedule->timeout_queue;
395 if (queue && queue->task) {
398 /* Walk thorugh all tasks in the particular task queue and run all
399 the expired tasks. */
401 /* Execute the task if the timeout has expired */
402 if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
404 silc_mutex_unlock(queue->lock);
405 silc_mutex_unlock(schedule->lock);
406 task->callback(schedule, SILC_TASK_EXPIRE, task->fd, task->context);
407 silc_mutex_lock(schedule->lock);
408 silc_mutex_lock(queue->lock);
411 /* Break if there isn't more tasks in the queue */
412 if (queue->task == task->next) {
413 silc_schedule_task_remove(queue, task);
419 /* Remove the task from queue */
420 silc_schedule_task_remove(queue, task->prev);
422 /* The timeout hasn't expired, check for next one */
424 /* Break if there isn't more tasks in the queue */
425 if (queue->task == task->next)
434 /* Calculates next timeout for select(). This is the timeout value
435 when at earliest some of the timeout tasks expire. If this is in the
436 past, they will be run now. */
437 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
439 static void silc_schedule_select_timeout(SilcSchedule schedule)
441 SilcTaskQueue queue = schedule->timeout_queue;
443 struct timeval curtime;
445 /* Get the current time */
446 silc_gettimeofday(&curtime);
447 schedule->timeout = NULL;
449 /* First task in the task queue has always the smallest timeout. */
452 if (task && task->valid == TRUE) {
453 /* If the timeout is in past, we will run the task and all other
454 timeout tasks from the past. */
455 if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
456 silc_schedule_dispatch_timeout(schedule);
458 /* The task(s) has expired and doesn't exist on the task queue
459 anymore. We continue with new timeout. */
460 queue = schedule->timeout_queue;
462 if (task == NULL || task->valid == FALSE)
466 /* Calculate the next timeout for select() */
467 queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
468 queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
469 if (queue->timeout.tv_sec < 0)
470 queue->timeout.tv_sec = 0;
472 /* We wouldn't want to go under zero, check for it. */
473 if (queue->timeout.tv_usec < 0) {
474 queue->timeout.tv_sec -= 1;
475 if (queue->timeout.tv_sec < 0)
476 queue->timeout.tv_sec = 0;
477 queue->timeout.tv_usec += 1000000L;
480 /* We've got the timeout value */
483 /* Task is not valid, remove it and try next one. */
484 silc_schedule_task_remove(queue, task);
486 if (queue->task == NULL)
491 /* Save the timeout */
493 schedule->timeout = &queue->timeout;
494 SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout->tv_sec,
495 schedule->timeout->tv_usec));
499 /* Runs the scheduler once and then returns. */
501 bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
503 struct timeval timeout;
506 SILC_LOG_DEBUG(("In scheduler loop"));
508 if (!schedule->is_locked)
509 silc_mutex_lock(schedule->lock);
511 /* If the task queues aren't initialized or we aren't valid anymore
513 if ((!schedule->fd_queue && !schedule->timeout_queue
514 && !schedule->generic_queue) || schedule->valid == FALSE) {
515 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
516 if (!schedule->is_locked)
517 silc_mutex_unlock(schedule->lock);
521 /* Calculate next timeout for silc_select(). This is the timeout value
522 when at earliest some of the timeout tasks expire. */
523 silc_mutex_lock(schedule->timeout_queue->lock);
524 silc_schedule_select_timeout(schedule);
525 silc_mutex_unlock(schedule->timeout_queue->lock);
527 if (timeout_usecs >= 0) {
529 timeout.tv_usec = timeout_usecs;
530 schedule->timeout = &timeout;
533 silc_mutex_unlock(schedule->lock);
535 /* This is the main select(). The program blocks here until some
536 of the selected file descriptors change status or the selected
538 SILC_LOG_DEBUG(("Select"));
539 ret = silc_select(schedule->fd_list, schedule->last_fd + 1,
542 silc_mutex_lock(schedule->lock);
549 SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
553 silc_mutex_lock(schedule->timeout_queue->lock);
554 silc_schedule_dispatch_timeout(schedule);
555 silc_mutex_unlock(schedule->timeout_queue->lock);
558 /* There is some data available now */
559 SILC_LOG_DEBUG(("Running non-timeout tasks"));
560 silc_schedule_dispatch_nontimeout(schedule);
564 if (!schedule->is_locked)
565 silc_mutex_unlock(schedule->lock);
570 /* The SILC scheduler. This is actually the main routine in SILC programs.
571 When this returns the program is to be ended. Before this function can
572 be called, one must call silc_schedule_init function. */
574 void silc_schedule(SilcSchedule schedule)
576 SILC_LOG_DEBUG(("Running scheduler"));
578 if (schedule->valid == FALSE) {
579 SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
583 silc_mutex_lock(schedule->lock);
584 schedule->is_locked = TRUE;
586 /* Start the scheduler loop */
587 while (silc_schedule_one(schedule, -1))
590 silc_mutex_unlock(schedule->lock);
593 /* Wakes up the scheduler. This is used only in multi-threaded
594 environments where threads may add new tasks or remove old tasks
595 from task queues. This is called to wake up the scheduler in the
596 main thread so that it detects the changes in the task queues.
597 If threads support is not compiled in this function has no effect.
598 Implementation of this function is platform specific. */
600 void silc_schedule_wakeup(SilcSchedule schedule)
603 SILC_LOG_DEBUG(("Wakeup scheduler"));
604 silc_mutex_lock(schedule->lock);
605 silc_schedule_wakeup_internal(schedule->wakeup);
606 silc_mutex_unlock(schedule->lock);
610 /* Add new task to the scheduler */
612 SilcTask silc_schedule_task_add(SilcSchedule schedule, uint32 fd,
613 SilcTaskCallback callback, void *context,
614 long seconds, long useconds,
616 SilcTaskPriority priority)
622 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", fd,
625 queue = SILC_SCHEDULE_GET_QUEUE(type);
627 /* If the task is generic task, we check whether this task has already
628 been registered. Generic tasks are registered only once and after that
629 the same task applies to all file descriptors to be registered. */
630 if (type == SILC_TASK_GENERIC) {
631 silc_mutex_lock(queue->lock);
634 SilcTask task = queue->task;
636 if ((task->callback == callback) && (task->context == context)) {
637 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
639 silc_mutex_unlock(queue->lock);
641 /* Add the fd to be listened, the task found now applies to this
643 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
647 if (queue->task == task->next)
654 silc_mutex_unlock(queue->lock);
657 newtask = silc_calloc(1, sizeof(*newtask));
659 newtask->context = context;
660 newtask->callback = callback;
661 newtask->valid = TRUE;
662 newtask->priority = priority;
663 newtask->type = type;
664 newtask->next = newtask;
665 newtask->prev = newtask;
667 /* Create timeout if marked to be timeout task */
668 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
669 silc_gettimeofday(&newtask->timeout);
670 newtask->timeout.tv_sec += seconds + (useconds / 1000000L);
671 newtask->timeout.tv_usec += (useconds % 1000000L);
672 if (newtask->timeout.tv_usec > 999999L) {
673 newtask->timeout.tv_sec += 1;
674 newtask->timeout.tv_usec -= 1000000L;
679 /* If the task is non-timeout task we have to tell the scheduler that we
680 would like to have these tasks scheduled at some odd distant future. */
681 if (type != SILC_TASK_TIMEOUT)
682 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
684 silc_mutex_lock(queue->lock);
686 /* Is this first task of the queue? */
687 if (queue->task == NULL) {
688 queue->task = newtask;
689 silc_mutex_unlock(queue->lock);
694 newtask = silc_task_add_timeout(queue, newtask, priority);
696 newtask = silc_task_add(queue, newtask, priority);
698 silc_mutex_unlock(queue->lock);
703 /* Removes a task from the scheduler */
705 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
707 SilcTaskQueue queue = SILC_SCHEDULE_GET_QUEUE(task->type);
709 /* Unregister all tasks */
710 if (task == SILC_ALL_TASKS) {
712 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
714 silc_mutex_lock(queue->lock);
717 silc_mutex_unlock(queue->lock);
726 if (queue->task == next->next)
731 silc_mutex_unlock(queue->lock);
735 SILC_LOG_DEBUG(("Unregistering task"));
737 silc_mutex_lock(queue->lock);
739 /* Unregister the specific task */
743 silc_mutex_unlock(queue->lock);
746 /* Remove task by fd */
748 void silc_schedule_task_del_by_fd(SilcSchedule schedule, uint32 fd)
750 SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
752 silc_task_del_by_fd(schedule->timeout_queue, fd);
753 silc_task_del_by_fd(schedule->fd_queue, fd);
756 /* Remove task by task callback. */
758 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
759 SilcTaskCallback callback)
761 SILC_LOG_DEBUG(("Unregister task by callback"));
763 silc_task_del_by_callback(schedule->timeout_queue, callback);
764 silc_task_del_by_callback(schedule->fd_queue, callback);
765 silc_task_del_by_callback(schedule->generic_queue, callback);
768 /* Remove task by context. */
770 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
772 SILC_LOG_DEBUG(("Unregister task by context"));
774 silc_task_del_by_context(schedule->timeout_queue, context);
775 silc_task_del_by_context(schedule->fd_queue, context);
776 silc_task_del_by_context(schedule->generic_queue, context);
779 /* Sets a file descriptor to be listened by select() in scheduler. One can
780 call this directly if wanted. This can be called multiple times for
781 one file descriptor to set different iomasks. */
783 void silc_schedule_set_listen_fd(SilcSchedule schedule,
784 uint32 fd, SilcTaskEvent iomask)
789 silc_mutex_lock(schedule->lock);
791 for (i = 0; i < schedule->max_fd; i++)
792 if (schedule->fd_list[i].fd == fd) {
793 schedule->fd_list[i].fd = fd;
794 schedule->fd_list[i].events = iomask;
795 if (i > schedule->last_fd)
796 schedule->last_fd = i;
802 for (i = 0; i < schedule->max_fd; i++)
803 if (schedule->fd_list[i].events == 0) {
804 schedule->fd_list[i].fd = fd;
805 schedule->fd_list[i].events = iomask;
806 if (i > schedule->last_fd)
807 schedule->last_fd = i;
811 silc_mutex_unlock(schedule->lock);
814 /* Removes a file descriptor from listen list. */
816 void silc_schedule_unset_listen_fd(SilcSchedule schedule, uint32 fd)
820 silc_mutex_lock(schedule->lock);
822 SILC_LOG_DEBUG(("Unset listen fd %d", fd));
824 for (i = 0; i < schedule->max_fd; i++)
825 if (schedule->fd_list[i].fd == fd) {
826 schedule->fd_list[i].fd = 0;
827 schedule->fd_list[i].events = 0;
828 if (schedule->last_fd == i)
829 schedule->last_fd = schedule->max_fd - 1;
833 silc_mutex_unlock(schedule->lock);
836 /* Allocates a newtask task queue into the scheduler */
838 static void silc_task_queue_alloc(SilcTaskQueue *queue)
840 *queue = silc_calloc(1, sizeof(**queue));
841 silc_mutex_alloc(&(*queue)->lock);
844 /* Free's a task queue. */
846 static void silc_task_queue_free(SilcTaskQueue queue)
848 silc_mutex_free(queue->lock);
852 /* Return task by its fd. */
854 static SilcTask silc_task_find(SilcTaskQueue queue, uint32 fd)
866 if (queue->task == next->next)
874 /* Adds a non-timeout task into the task queue. This function is used
875 by silc_task_register function. Returns a pointer to the registered
878 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
879 SilcTaskPriority priority)
881 SilcTask task, next, prev;
883 /* Take the first task in the queue */
887 case SILC_TASK_PRI_LOW:
888 /* Lowest priority. The task is added at the end of the list. */
890 newtask->prev = prev;
891 newtask->next = task;
892 prev->next = newtask;
893 task->prev = newtask;
895 case SILC_TASK_PRI_NORMAL:
896 /* Normal priority. The task is added before lower priority tasks
897 but after tasks with higher priority. */
899 while(prev != task) {
900 if (prev->priority > SILC_TASK_PRI_LOW)
905 /* There are only lower priorities in the list, we will
906 sit before them and become the first task in the queue. */
908 newtask->prev = prev;
909 newtask->next = task;
910 task->prev = newtask;
911 prev->next = newtask;
913 /* We are now the first task in queue */
914 queue->task = newtask;
916 /* Found a spot from the list, add the task to the list. */
918 newtask->prev = prev;
919 newtask->next = next;
920 prev->next = newtask;
921 next->prev = newtask;
932 /* Return the timeout task with smallest timeout. */
934 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
948 if (silc_schedule_task_timeout_compare(&prev->timeout, &task->timeout))
957 /* Adds a timeout task into the task queue. This function is used by
958 silc_task_register function. Returns a pointer to the registered
959 task. Timeout tasks are sorted by their timeout value in ascending
960 order. The priority matters if there are more than one task with
963 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
964 SilcTaskPriority priority)
966 SilcTask task, prev, next;
968 /* Take the first task in the queue */
971 /* Take last task from the list */
975 case SILC_TASK_PRI_LOW:
976 /* Lowest priority. The task is added at the end of the list. */
977 while(prev != task) {
979 /* If we have longer timeout than with the task head of us
980 we have found our spot. */
981 if (silc_schedule_task_timeout_compare(&prev->timeout,
985 /* If we are equal size of timeout we will be after it. */
986 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
990 /* We have shorter timeout, compare to next one. */
993 /* Found a spot from the list, add the task to the list. */
995 newtask->prev = prev;
996 newtask->next = next;
997 prev->next = newtask;
998 next->prev = newtask;
1001 /* Check if we are going to be the first task in the queue */
1002 if (silc_schedule_task_timeout_compare(&prev->timeout,
1005 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1009 /* We are now the first task in queue */
1010 queue->task = newtask;
1013 case SILC_TASK_PRI_NORMAL:
1014 /* Normal priority. The task is added before lower priority tasks
1015 but after tasks with higher priority. */
1016 while(prev != task) {
1018 /* If we have longer timeout than with the task head of us
1019 we have found our spot. */
1020 if (silc_schedule_task_timeout_compare(&prev->timeout,
1024 /* If we are equal size of timeout, priority kicks in place. */
1025 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1027 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1030 /* We have shorter timeout or higher priority, compare to next one. */
1033 /* Found a spot from the list, add the task to the list. */
1035 newtask->prev = prev;
1036 newtask->next = next;
1037 prev->next = newtask;
1038 next->prev = newtask;
1041 /* Check if we are going to be the first task in the queue */
1042 if (silc_schedule_task_timeout_compare(&prev->timeout,
1045 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1047 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1050 /* We are now the first task in queue */
1051 queue->task = newtask;
1062 /* Removes (unregisters) a task from particular task queue. This function
1063 is used internally by scheduler. This must be called holding the
1066 static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task)
1068 SilcTask first, old, next;
1070 if (!queue || !task)
1077 first = queue->task;
1079 /* Unregister all tasks in queue */
1080 if (task == SILC_ALL_TASKS) {
1081 SILC_LOG_DEBUG(("Removing all tasks at once"));
1086 silc_free(next->prev);
1095 SILC_LOG_DEBUG(("Removing task"));
1097 /* Unregister the task */
1101 SilcTask prev, next;
1108 if (prev == old && next == old)
1110 if (queue->task == old)
1111 queue->task = silc_task_get_first(queue, next);
1124 /* Compare two time values. If the first argument is smaller than the
1125 second this function returns TRUE. */
1127 static int silc_schedule_task_timeout_compare(struct timeval *smaller,
1128 struct timeval *bigger)
1130 if ((smaller->tv_sec < bigger->tv_sec) ||
1131 ((smaller->tv_sec == bigger->tv_sec) &&
1132 (smaller->tv_usec < bigger->tv_usec)))
1138 static void silc_task_del_by_fd(SilcTaskQueue queue, uint32 fd)
1142 silc_mutex_lock(queue->lock);
1145 silc_mutex_unlock(queue->lock);
1153 next->valid = FALSE;
1154 if (queue->task == next->next)
1159 silc_mutex_unlock(queue->lock);
1162 static void silc_task_del_by_callback(SilcTaskQueue queue,
1163 SilcTaskCallback callback)
1167 silc_mutex_lock(queue->lock);
1170 silc_mutex_unlock(queue->lock);
1177 if (next->callback == callback)
1178 next->valid = FALSE;
1179 if (queue->task == next->next)
1184 silc_mutex_unlock(queue->lock);
1187 static void silc_task_del_by_context(SilcTaskQueue queue, void *context)
1191 silc_mutex_lock(queue->lock);
1194 silc_mutex_unlock(queue->lock);
1201 if (next->context == context)
1202 next->valid = FALSE;
1203 if (queue->task == next->next)
1208 silc_mutex_unlock(queue->lock);