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);
281 /* If the task was found then execute its callbacks. If not then
282 execute all generic tasks for that fd. */
284 /* Validity of the task is checked always before and after
285 execution beacuse the task might have been unregistered
286 in the callback function, ie. it is not valid anymore. */
288 /* Is the task ready for reading */
289 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
290 silc_mutex_unlock(schedule->fd_queue->lock);
291 silc_mutex_unlock(schedule->lock);
292 task->callback(schedule, SILC_TASK_READ, task->fd, task->context);
293 silc_mutex_lock(schedule->lock);
294 silc_mutex_lock(schedule->fd_queue->lock);
297 /* Is the task ready for writing */
298 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
299 silc_mutex_unlock(schedule->fd_queue->lock);
300 silc_mutex_unlock(schedule->lock);
301 task->callback(schedule, SILC_TASK_WRITE, task->fd, task->context);
302 silc_mutex_lock(schedule->lock);
303 silc_mutex_lock(schedule->fd_queue->lock);
307 silc_schedule_task_remove(schedule->fd_queue, task);
309 silc_mutex_unlock(schedule->fd_queue->lock);
311 /* Run generic tasks for this fd. */
313 silc_mutex_unlock(schedule->fd_queue->lock);
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_LOG_DEBUG(("Unregister task by fd"));
748 silc_task_del_by_fd(schedule->timeout_queue, fd);
749 silc_task_del_by_fd(schedule->fd_queue, fd);
752 /* Remove task by task callback. */
754 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
755 SilcTaskCallback callback)
757 SILC_LOG_DEBUG(("Unregister task by callback"));
759 silc_task_del_by_callback(schedule->timeout_queue, callback);
760 silc_task_del_by_callback(schedule->fd_queue, callback);
761 silc_task_del_by_callback(schedule->generic_queue, callback);
764 /* Remove task by context. */
766 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
768 SILC_LOG_DEBUG(("Unregister task by context"));
770 silc_task_del_by_context(schedule->timeout_queue, context);
771 silc_task_del_by_context(schedule->fd_queue, context);
772 silc_task_del_by_context(schedule->generic_queue, context);
775 /* Sets a file descriptor to be listened by select() in scheduler. One can
776 call this directly if wanted. This can be called multiple times for
777 one file descriptor to set different iomasks. */
779 void silc_schedule_set_listen_fd(SilcSchedule schedule,
780 uint32 fd, SilcTaskEvent iomask)
785 silc_mutex_lock(schedule->lock);
787 for (i = 0; i < schedule->max_fd; i++)
788 if (schedule->fd_list[i].fd == fd) {
789 schedule->fd_list[i].fd = fd;
790 schedule->fd_list[i].events = iomask;
791 if (i > schedule->last_fd)
792 schedule->last_fd = i;
798 for (i = 0; i < schedule->max_fd; i++)
799 if (schedule->fd_list[i].events == 0) {
800 schedule->fd_list[i].fd = fd;
801 schedule->fd_list[i].events = iomask;
802 if (i > schedule->last_fd)
803 schedule->last_fd = i;
807 silc_mutex_unlock(schedule->lock);
810 /* Removes a file descriptor from listen list. */
812 void silc_schedule_unset_listen_fd(SilcSchedule schedule, uint32 fd)
816 silc_mutex_lock(schedule->lock);
818 for (i = 0; i < schedule->max_fd; i++)
819 if (schedule->fd_list[i].fd == fd) {
820 schedule->fd_list[i].fd = 0;
821 schedule->fd_list[i].events = 0;
822 if (schedule->last_fd == i)
823 schedule->last_fd = schedule->max_fd - 1;
827 silc_mutex_unlock(schedule->lock);
830 /* Allocates a newtask task queue into the scheduler */
832 static void silc_task_queue_alloc(SilcTaskQueue *queue)
834 *queue = silc_calloc(1, sizeof(**queue));
835 silc_mutex_alloc(&(*queue)->lock);
838 /* Free's a task queue. */
840 static void silc_task_queue_free(SilcTaskQueue queue)
842 silc_mutex_free(queue->lock);
846 /* Return task by its fd. */
848 static SilcTask silc_task_find(SilcTaskQueue queue, uint32 fd)
860 if (queue->task == next->next)
868 /* Adds a non-timeout task into the task queue. This function is used
869 by silc_task_register function. Returns a pointer to the registered
872 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
873 SilcTaskPriority priority)
875 SilcTask task, next, prev;
877 /* Take the first task in the queue */
881 case SILC_TASK_PRI_LOW:
882 /* Lowest priority. The task is added at the end of the list. */
884 newtask->prev = prev;
885 newtask->next = task;
886 prev->next = newtask;
887 task->prev = newtask;
889 case SILC_TASK_PRI_NORMAL:
890 /* Normal priority. The task is added before lower priority tasks
891 but after tasks with higher priority. */
893 while(prev != task) {
894 if (prev->priority > SILC_TASK_PRI_LOW)
899 /* There are only lower priorities in the list, we will
900 sit before them and become the first task in the queue. */
902 newtask->prev = prev;
903 newtask->next = task;
904 task->prev = newtask;
905 prev->next = newtask;
907 /* We are now the first task in queue */
908 queue->task = newtask;
910 /* Found a spot from the list, add the task to the list. */
912 newtask->prev = prev;
913 newtask->next = next;
914 prev->next = newtask;
915 next->prev = newtask;
926 /* Return the timeout task with smallest timeout. */
928 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
942 if (silc_schedule_task_timeout_compare(&prev->timeout, &task->timeout))
951 /* Adds a timeout task into the task queue. This function is used by
952 silc_task_register function. Returns a pointer to the registered
953 task. Timeout tasks are sorted by their timeout value in ascending
954 order. The priority matters if there are more than one task with
957 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
958 SilcTaskPriority priority)
960 SilcTask task, prev, next;
962 /* Take the first task in the queue */
965 /* Take last task from the list */
969 case SILC_TASK_PRI_LOW:
970 /* Lowest priority. The task is added at the end of the list. */
971 while(prev != task) {
973 /* If we have longer timeout than with the task head of us
974 we have found our spot. */
975 if (silc_schedule_task_timeout_compare(&prev->timeout,
979 /* If we are equal size of timeout we will be after it. */
980 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
984 /* We have shorter timeout, compare to next one. */
987 /* Found a spot from the list, add the task to the list. */
989 newtask->prev = prev;
990 newtask->next = next;
991 prev->next = newtask;
992 next->prev = newtask;
995 /* Check if we are going to be the first task in the queue */
996 if (silc_schedule_task_timeout_compare(&prev->timeout,
999 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1003 /* We are now the first task in queue */
1004 queue->task = newtask;
1007 case SILC_TASK_PRI_NORMAL:
1008 /* Normal priority. The task is added before lower priority tasks
1009 but after tasks with higher priority. */
1010 while(prev != task) {
1012 /* If we have longer timeout than with the task head of us
1013 we have found our spot. */
1014 if (silc_schedule_task_timeout_compare(&prev->timeout,
1018 /* If we are equal size of timeout, priority kicks in place. */
1019 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1021 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1024 /* We have shorter timeout or higher priority, compare to next one. */
1027 /* Found a spot from the list, add the task to the list. */
1029 newtask->prev = prev;
1030 newtask->next = next;
1031 prev->next = newtask;
1032 next->prev = newtask;
1035 /* Check if we are going to be the first task in the queue */
1036 if (silc_schedule_task_timeout_compare(&prev->timeout,
1039 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1041 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1044 /* We are now the first task in queue */
1045 queue->task = newtask;
1056 /* Removes (unregisters) a task from particular task queue. This function
1057 is used internally by scheduler. This must be called holding the
1060 static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task)
1062 SilcTask first, old, next;
1064 if (!queue || !task)
1071 first = queue->task;
1073 /* Unregister all tasks in queue */
1074 if (task == SILC_ALL_TASKS) {
1075 SILC_LOG_DEBUG(("Removing all tasks at once"));
1080 silc_free(next->prev);
1089 SILC_LOG_DEBUG(("Removing task"));
1091 /* Unregister the task */
1095 SilcTask prev, next;
1102 if (prev == old && next == old)
1104 if (queue->task == old)
1105 queue->task = silc_task_get_first(queue, next);
1118 /* Compare two time values. If the first argument is smaller than the
1119 second this function returns TRUE. */
1121 static int silc_schedule_task_timeout_compare(struct timeval *smaller,
1122 struct timeval *bigger)
1124 if ((smaller->tv_sec < bigger->tv_sec) ||
1125 ((smaller->tv_sec == bigger->tv_sec) &&
1126 (smaller->tv_usec < bigger->tv_usec)))
1132 static void silc_task_del_by_fd(SilcTaskQueue queue, uint32 fd)
1136 silc_mutex_lock(queue->lock);
1139 silc_mutex_unlock(queue->lock);
1147 next->valid = FALSE;
1148 if (queue->task == next->next)
1153 silc_mutex_unlock(queue->lock);
1156 static void silc_task_del_by_callback(SilcTaskQueue queue,
1157 SilcTaskCallback callback)
1161 silc_mutex_lock(queue->lock);
1164 silc_mutex_unlock(queue->lock);
1171 if (next->callback == callback)
1172 next->valid = FALSE;
1173 if (queue->task == next->next)
1178 silc_mutex_unlock(queue->lock);
1181 static void silc_task_del_by_context(SilcTaskQueue queue, void *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);