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;
274 for (i = 0; i <= last_fd; i++) {
275 if (schedule->fd_list[i].events == 0)
278 fd = schedule->fd_list[i].fd;
280 /* First check whether this fd has task in the fd queue */
281 silc_mutex_lock(schedule->fd_queue->lock);
282 task = silc_task_find(schedule->fd_queue, fd);
284 /* If the task was found then execute its callbacks. If not then
285 execute all generic tasks for that fd. */
287 /* Validity of the task is checked always before and after
288 execution beacuse the task might have been unregistered
289 in the callback function, ie. it is not valid anymore. */
291 /* Is the task ready for reading */
292 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
293 silc_mutex_unlock(schedule->fd_queue->lock);
294 silc_mutex_unlock(schedule->lock);
295 task->callback(schedule, SILC_TASK_READ, task->fd, task->context);
296 silc_mutex_lock(schedule->lock);
297 silc_mutex_lock(schedule->fd_queue->lock);
300 /* Is the task ready for writing */
301 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
302 silc_mutex_unlock(schedule->fd_queue->lock);
303 silc_mutex_unlock(schedule->lock);
304 task->callback(schedule, SILC_TASK_WRITE, task->fd, task->context);
305 silc_mutex_lock(schedule->lock);
306 silc_mutex_lock(schedule->fd_queue->lock);
310 silc_schedule_task_remove(schedule->fd_queue, task);
312 silc_mutex_unlock(schedule->fd_queue->lock);
314 /* Run generic tasks for this fd. */
316 silc_mutex_unlock(schedule->fd_queue->lock);
318 silc_mutex_lock(schedule->generic_queue->lock);
319 if (!schedule->generic_queue->task) {
320 silc_mutex_unlock(schedule->generic_queue->lock);
324 task = schedule->generic_queue->task;
326 /* Validity of the task is checked always before and after
327 execution beacuse the task might have been unregistered
328 in the callback function, ie. it is not valid anymore. */
330 /* Is the task ready for reading */
331 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
332 silc_mutex_unlock(schedule->generic_queue->lock);
333 silc_mutex_unlock(schedule->lock);
334 task->callback(schedule, SILC_TASK_READ, fd, task->context);
335 silc_mutex_lock(schedule->lock);
336 silc_mutex_lock(schedule->generic_queue->lock);
339 /* Is the task ready for writing */
340 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
341 silc_mutex_unlock(schedule->generic_queue->lock);
342 silc_mutex_unlock(schedule->lock);
343 task->callback(schedule, SILC_TASK_WRITE, fd, task->context);
344 silc_mutex_lock(schedule->lock);
345 silc_mutex_lock(schedule->generic_queue->lock);
349 /* Invalid (unregistered) tasks are removed from the
351 if (schedule->generic_queue->task == task->next) {
352 silc_schedule_task_remove(schedule->generic_queue, task);
353 silc_mutex_unlock(schedule->generic_queue->lock);
358 silc_schedule_task_remove(schedule->generic_queue, task);
362 /* Break if there isn't more tasks in the queue */
363 if (schedule->generic_queue->task == task->next)
369 silc_mutex_unlock(schedule->generic_queue->lock);
374 /* Executes all tasks whose timeout has expired. The task is removed from
375 the task queue after the callback function has returned. Also, invalid
376 tasks are removed here. We don't have to care about priorities because
377 tasks are already sorted in their priority order at the registration
379 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
381 static void silc_schedule_dispatch_timeout(SilcSchedule schedule)
383 SilcTaskQueue queue = schedule->timeout_queue;
385 struct timeval curtime;
387 SILC_LOG_DEBUG(("Running timeout tasks"));
389 silc_gettimeofday(&curtime);
391 queue = schedule->timeout_queue;
392 if (queue && queue->task) {
395 /* Walk thorugh all tasks in the particular task queue and run all
396 the expired tasks. */
398 /* Execute the task if the timeout has expired */
399 if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
401 silc_mutex_unlock(queue->lock);
402 silc_mutex_unlock(schedule->lock);
403 task->callback(schedule, SILC_TASK_EXPIRE, task->fd, task->context);
404 silc_mutex_lock(schedule->lock);
405 silc_mutex_lock(queue->lock);
408 /* Break if there isn't more tasks in the queue */
409 if (queue->task == task->next) {
410 silc_schedule_task_remove(queue, task);
416 /* Remove the task from queue */
417 silc_schedule_task_remove(queue, task->prev);
419 /* The timeout hasn't expired, check for next one */
421 /* Break if there isn't more tasks in the queue */
422 if (queue->task == task->next)
431 /* Calculates next timeout for select(). This is the timeout value
432 when at earliest some of the timeout tasks expire. If this is in the
433 past, they will be run now. */
434 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
436 static void silc_schedule_select_timeout(SilcSchedule schedule)
438 SilcTaskQueue queue = schedule->timeout_queue;
440 struct timeval curtime;
442 /* Get the current time */
443 silc_gettimeofday(&curtime);
444 schedule->timeout = NULL;
446 /* First task in the task queue has always the smallest timeout. */
449 if (task && task->valid == TRUE) {
450 /* If the timeout is in past, we will run the task and all other
451 timeout tasks from the past. */
452 if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
453 silc_schedule_dispatch_timeout(schedule);
455 /* The task(s) has expired and doesn't exist on the task queue
456 anymore. We continue with new timeout. */
457 queue = schedule->timeout_queue;
459 if (task == NULL || task->valid == FALSE)
463 /* Calculate the next timeout for select() */
464 queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
465 queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
466 if (queue->timeout.tv_sec < 0)
467 queue->timeout.tv_sec = 0;
469 /* We wouldn't want to go under zero, check for it. */
470 if (queue->timeout.tv_usec < 0) {
471 queue->timeout.tv_sec -= 1;
472 if (queue->timeout.tv_sec < 0)
473 queue->timeout.tv_sec = 0;
474 queue->timeout.tv_usec += 1000000L;
477 /* We've got the timeout value */
480 /* Task is not valid, remove it and try next one. */
481 silc_schedule_task_remove(queue, task);
483 if (queue->task == NULL)
488 /* Save the timeout */
490 schedule->timeout = &queue->timeout;
491 SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout->tv_sec,
492 schedule->timeout->tv_usec));
496 /* Runs the scheduler once and then returns. */
498 bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
500 struct timeval timeout;
503 SILC_LOG_DEBUG(("In scheduler loop"));
505 if (!schedule->is_locked)
506 silc_mutex_lock(schedule->lock);
508 /* If the task queues aren't initialized or we aren't valid anymore
510 if ((!schedule->fd_queue && !schedule->timeout_queue
511 && !schedule->generic_queue) || schedule->valid == FALSE) {
512 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
513 if (!schedule->is_locked)
514 silc_mutex_unlock(schedule->lock);
518 /* Calculate next timeout for silc_select(). This is the timeout value
519 when at earliest some of the timeout tasks expire. */
520 silc_mutex_lock(schedule->timeout_queue->lock);
521 silc_schedule_select_timeout(schedule);
522 silc_mutex_unlock(schedule->timeout_queue->lock);
524 if (timeout_usecs >= 0) {
526 timeout.tv_usec = timeout_usecs;
527 schedule->timeout = &timeout;
530 silc_mutex_unlock(schedule->lock);
532 /* This is the main select(). The program blocks here until some
533 of the selected file descriptors change status or the selected
535 SILC_LOG_DEBUG(("Select"));
536 ret = silc_select(schedule->fd_list, schedule->last_fd + 1,
539 silc_mutex_lock(schedule->lock);
546 SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
550 silc_mutex_lock(schedule->timeout_queue->lock);
551 silc_schedule_dispatch_timeout(schedule);
552 silc_mutex_unlock(schedule->timeout_queue->lock);
555 /* There is some data available now */
556 SILC_LOG_DEBUG(("Running non-timeout tasks"));
557 silc_schedule_dispatch_nontimeout(schedule);
561 if (!schedule->is_locked)
562 silc_mutex_unlock(schedule->lock);
567 /* The SILC scheduler. This is actually the main routine in SILC programs.
568 When this returns the program is to be ended. Before this function can
569 be called, one must call silc_schedule_init function. */
571 void silc_schedule(SilcSchedule schedule)
573 SILC_LOG_DEBUG(("Running scheduler"));
575 if (schedule->valid == FALSE) {
576 SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
580 silc_mutex_lock(schedule->lock);
581 schedule->is_locked = TRUE;
583 /* Start the scheduler loop */
584 while (silc_schedule_one(schedule, -1))
587 silc_mutex_unlock(schedule->lock);
590 /* Wakes up the scheduler. This is used only in multi-threaded
591 environments where threads may add new tasks or remove old tasks
592 from task queues. This is called to wake up the scheduler in the
593 main thread so that it detects the changes in the task queues.
594 If threads support is not compiled in this function has no effect.
595 Implementation of this function is platform specific. */
597 void silc_schedule_wakeup(SilcSchedule schedule)
600 SILC_LOG_DEBUG(("Wakeup scheduler"));
601 silc_mutex_lock(schedule->lock);
602 silc_schedule_wakeup_internal(schedule->wakeup);
603 silc_mutex_unlock(schedule->lock);
607 /* Add new task to the scheduler */
609 SilcTask silc_schedule_task_add(SilcSchedule schedule, uint32 fd,
610 SilcTaskCallback callback, void *context,
611 long seconds, long useconds,
613 SilcTaskPriority priority)
619 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", fd,
622 queue = SILC_SCHEDULE_GET_QUEUE(type);
624 /* If the task is generic task, we check whether this task has already
625 been registered. Generic tasks are registered only once and after that
626 the same task applies to all file descriptors to be registered. */
627 if (type == SILC_TASK_GENERIC) {
628 silc_mutex_lock(queue->lock);
631 SilcTask task = queue->task;
633 if ((task->callback == callback) && (task->context == context)) {
634 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
636 silc_mutex_unlock(queue->lock);
638 /* Add the fd to be listened, the task found now applies to this
640 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
644 if (queue->task == task->next)
651 silc_mutex_unlock(queue->lock);
654 newtask = silc_calloc(1, sizeof(*newtask));
656 newtask->context = context;
657 newtask->callback = callback;
658 newtask->valid = TRUE;
659 newtask->priority = priority;
660 newtask->type = type;
661 newtask->next = newtask;
662 newtask->prev = newtask;
664 /* Create timeout if marked to be timeout task */
665 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
666 silc_gettimeofday(&newtask->timeout);
667 newtask->timeout.tv_sec += seconds + (useconds / 1000000L);
668 newtask->timeout.tv_usec += (useconds % 1000000L);
669 if (newtask->timeout.tv_usec > 999999L) {
670 newtask->timeout.tv_sec += 1;
671 newtask->timeout.tv_usec -= 1000000L;
676 /* If the task is non-timeout task we have to tell the scheduler that we
677 would like to have these tasks scheduled at some odd distant future. */
678 if (type != SILC_TASK_TIMEOUT)
679 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
681 silc_mutex_lock(queue->lock);
683 /* Is this first task of the queue? */
684 if (queue->task == NULL) {
685 queue->task = newtask;
686 silc_mutex_unlock(queue->lock);
691 newtask = silc_task_add_timeout(queue, newtask, priority);
693 newtask = silc_task_add(queue, newtask, priority);
695 silc_mutex_unlock(queue->lock);
700 /* Removes a task from the scheduler */
702 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
704 SilcTaskQueue queue = SILC_SCHEDULE_GET_QUEUE(task->type);
706 /* Unregister all tasks */
707 if (task == SILC_ALL_TASKS) {
709 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
711 silc_mutex_lock(queue->lock);
714 silc_mutex_unlock(queue->lock);
723 if (queue->task == next->next)
728 silc_mutex_unlock(queue->lock);
732 SILC_LOG_DEBUG(("Unregistering task"));
734 silc_mutex_lock(queue->lock);
736 /* Unregister the specific task */
740 silc_mutex_unlock(queue->lock);
743 /* Remove task by fd */
745 void silc_schedule_task_del_by_fd(SilcSchedule schedule, uint32 fd)
747 SILC_LOG_DEBUG(("Unregister task by fd"));
749 silc_task_del_by_fd(schedule->timeout_queue, fd);
750 silc_task_del_by_fd(schedule->fd_queue, fd);
753 /* Remove task by task callback. */
755 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
756 SilcTaskCallback callback)
758 SILC_LOG_DEBUG(("Unregister task by callback"));
760 silc_task_del_by_callback(schedule->timeout_queue, callback);
761 silc_task_del_by_callback(schedule->fd_queue, callback);
762 silc_task_del_by_callback(schedule->generic_queue, callback);
765 /* Remove task by context. */
767 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
769 SILC_LOG_DEBUG(("Unregister task by context"));
771 silc_task_del_by_context(schedule->timeout_queue, context);
772 silc_task_del_by_context(schedule->fd_queue, context);
773 silc_task_del_by_context(schedule->generic_queue, context);
776 /* Sets a file descriptor to be listened by select() in scheduler. One can
777 call this directly if wanted. This can be called multiple times for
778 one file descriptor to set different iomasks. */
780 void silc_schedule_set_listen_fd(SilcSchedule schedule,
781 uint32 fd, SilcTaskEvent iomask)
786 silc_mutex_lock(schedule->lock);
788 for (i = 0; i < schedule->max_fd; i++)
789 if (schedule->fd_list[i].fd == fd) {
790 schedule->fd_list[i].fd = fd;
791 schedule->fd_list[i].events = iomask;
792 if (i > schedule->last_fd)
793 schedule->last_fd = i;
799 for (i = 0; i < schedule->max_fd; i++)
800 if (schedule->fd_list[i].events == 0) {
801 schedule->fd_list[i].fd = fd;
802 schedule->fd_list[i].events = iomask;
803 if (i > schedule->last_fd)
804 schedule->last_fd = i;
808 silc_mutex_unlock(schedule->lock);
811 /* Removes a file descriptor from listen list. */
813 void silc_schedule_unset_listen_fd(SilcSchedule schedule, uint32 fd)
817 silc_mutex_lock(schedule->lock);
819 for (i = 0; i < schedule->max_fd; i++)
820 if (schedule->fd_list[i].fd == fd) {
821 schedule->fd_list[i].fd = 0;
822 schedule->fd_list[i].events = 0;
823 if (schedule->last_fd == i)
824 schedule->last_fd = schedule->max_fd - 1;
828 silc_mutex_unlock(schedule->lock);
831 /* Allocates a newtask task queue into the scheduler */
833 static void silc_task_queue_alloc(SilcTaskQueue *queue)
835 *queue = silc_calloc(1, sizeof(**queue));
836 silc_mutex_alloc(&(*queue)->lock);
839 /* Free's a task queue. */
841 static void silc_task_queue_free(SilcTaskQueue queue)
843 silc_mutex_free(queue->lock);
847 /* Return task by its fd. */
849 static SilcTask silc_task_find(SilcTaskQueue queue, uint32 fd)
861 if (queue->task == next->next)
869 /* Adds a non-timeout task into the task queue. This function is used
870 by silc_task_register function. Returns a pointer to the registered
873 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
874 SilcTaskPriority priority)
876 SilcTask task, next, prev;
878 /* Take the first task in the queue */
882 case SILC_TASK_PRI_LOW:
883 /* Lowest priority. The task is added at the end of the list. */
885 newtask->prev = prev;
886 newtask->next = task;
887 prev->next = newtask;
888 task->prev = newtask;
890 case SILC_TASK_PRI_NORMAL:
891 /* Normal priority. The task is added before lower priority tasks
892 but after tasks with higher priority. */
894 while(prev != task) {
895 if (prev->priority > SILC_TASK_PRI_LOW)
900 /* There are only lower priorities in the list, we will
901 sit before them and become the first task in the queue. */
903 newtask->prev = prev;
904 newtask->next = task;
905 task->prev = newtask;
906 prev->next = newtask;
908 /* We are now the first task in queue */
909 queue->task = newtask;
911 /* Found a spot from the list, add the task to the list. */
913 newtask->prev = prev;
914 newtask->next = next;
915 prev->next = newtask;
916 next->prev = newtask;
927 /* Return the timeout task with smallest timeout. */
929 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
943 if (silc_schedule_task_timeout_compare(&prev->timeout, &task->timeout))
952 /* Adds a timeout task into the task queue. This function is used by
953 silc_task_register function. Returns a pointer to the registered
954 task. Timeout tasks are sorted by their timeout value in ascending
955 order. The priority matters if there are more than one task with
958 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
959 SilcTaskPriority priority)
961 SilcTask task, prev, next;
963 /* Take the first task in the queue */
966 /* Take last task from the list */
970 case SILC_TASK_PRI_LOW:
971 /* Lowest priority. The task is added at the end of the list. */
972 while(prev != task) {
974 /* If we have longer timeout than with the task head of us
975 we have found our spot. */
976 if (silc_schedule_task_timeout_compare(&prev->timeout,
980 /* If we are equal size of timeout we will be after it. */
981 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
985 /* We have shorter timeout, compare to next one. */
988 /* Found a spot from the list, add the task to the list. */
990 newtask->prev = prev;
991 newtask->next = next;
992 prev->next = newtask;
993 next->prev = newtask;
996 /* Check if we are going to be the first task in the queue */
997 if (silc_schedule_task_timeout_compare(&prev->timeout,
1000 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1004 /* We are now the first task in queue */
1005 queue->task = newtask;
1008 case SILC_TASK_PRI_NORMAL:
1009 /* Normal priority. The task is added before lower priority tasks
1010 but after tasks with higher priority. */
1011 while(prev != task) {
1013 /* If we have longer timeout than with the task head of us
1014 we have found our spot. */
1015 if (silc_schedule_task_timeout_compare(&prev->timeout,
1019 /* If we are equal size of timeout, priority kicks in place. */
1020 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1022 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1025 /* We have shorter timeout or higher priority, compare to next one. */
1028 /* Found a spot from the list, add the task to the list. */
1030 newtask->prev = prev;
1031 newtask->next = next;
1032 prev->next = newtask;
1033 next->prev = newtask;
1036 /* Check if we are going to be the first task in the queue */
1037 if (silc_schedule_task_timeout_compare(&prev->timeout,
1040 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1042 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1045 /* We are now the first task in queue */
1046 queue->task = newtask;
1057 /* Removes (unregisters) a task from particular task queue. This function
1058 is used internally by scheduler. This must be called holding the
1061 static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task)
1063 SilcTask first, old, next;
1065 if (!queue || !task)
1072 first = queue->task;
1074 /* Unregister all tasks in queue */
1075 if (task == SILC_ALL_TASKS) {
1076 SILC_LOG_DEBUG(("Removing all tasks at once"));
1081 silc_free(next->prev);
1090 SILC_LOG_DEBUG(("Removing task"));
1092 /* Unregister the task */
1096 SilcTask prev, next;
1103 if (prev == old && next == old)
1105 if (queue->task == old)
1106 queue->task = silc_task_get_first(queue, next);
1119 /* Compare two time values. If the first argument is smaller than the
1120 second this function returns TRUE. */
1122 static int silc_schedule_task_timeout_compare(struct timeval *smaller,
1123 struct timeval *bigger)
1125 if ((smaller->tv_sec < bigger->tv_sec) ||
1126 ((smaller->tv_sec == bigger->tv_sec) &&
1127 (smaller->tv_usec < bigger->tv_usec)))
1133 static void silc_task_del_by_fd(SilcTaskQueue queue, uint32 fd)
1137 silc_mutex_lock(queue->lock);
1140 silc_mutex_unlock(queue->lock);
1148 next->valid = FALSE;
1149 if (queue->task == next->next)
1154 silc_mutex_unlock(queue->lock);
1157 static void silc_task_del_by_callback(SilcTaskQueue queue,
1158 SilcTaskCallback callback)
1162 silc_mutex_lock(queue->lock);
1165 silc_mutex_unlock(queue->lock);
1172 if (next->callback == callback)
1173 next->valid = FALSE;
1174 if (queue->task == next->next)
1179 silc_mutex_unlock(queue->lock);
1182 static void silc_task_del_by_context(SilcTaskQueue queue, void *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);