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, SilcUInt32 fds_count,
32 struct timeval *timeout);
34 /* Initializes the wakeup of the scheduler. In multi-threaded environment
35 the scheduler needs to be wakenup when tasks are added or removed from
36 the task queues. This will initialize the wakeup for the scheduler.
37 Any tasks that needs to be registered must be registered to the `queue'.
38 It is guaranteed that the scheduler will automatically free any
39 registered tasks in this queue. This is system specific routine. */
40 void *silc_schedule_wakeup_init(SilcSchedule schedule);
42 /* Uninitializes the system specific wakeup. */
43 void silc_schedule_wakeup_uninit(void *context);
45 /* Wakes up the scheduler. This is platform specific routine */
46 void silc_schedule_wakeup_internal(void *context);
49 /* Internal task management routines. */
51 static void silc_task_queue_alloc(SilcTaskQueue *queue);
52 static void silc_task_queue_free(SilcTaskQueue queue);
53 static SilcTask silc_task_find(SilcTaskQueue queue, SilcUInt32 fd);
54 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
55 SilcTaskPriority priority);
56 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first);
57 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
58 SilcTaskPriority priority);
59 static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task);
60 static int silc_schedule_task_timeout_compare(struct timeval *smaller,
61 struct timeval *bigger);
62 static void silc_task_del_by_context(SilcTaskQueue queue, void *context);
63 static void silc_task_del_by_callback(SilcTaskQueue queue,
64 SilcTaskCallback callback);
65 static void silc_task_del_by_fd(SilcTaskQueue queue, SilcUInt32 fd);
67 /* Returns the task queue by task type */
68 #define SILC_SCHEDULE_GET_QUEUE(type) \
69 (type == SILC_TASK_FD ? schedule->fd_queue : \
70 type == SILC_TASK_TIMEOUT ? schedule->timeout_queue : \
71 schedule->generic_queue)
74 #define SILC_SCHEDULE_LOCK(schedule) silc_mutex_lock(schedule->lock)
75 #define SILC_SCHEDULE_UNLOCK(schedule) silc_mutex_unlock(schedule->lock)
77 /* SILC Task object. Represents one task in the scheduler. */
78 struct SilcTaskStruct {
80 struct timeval timeout;
81 SilcTaskCallback callback;
84 SilcTaskPriority priority;
87 /* Pointers forming doubly linked circular list */
88 struct SilcTaskStruct *next;
89 struct SilcTaskStruct *prev;
92 /* SILC Task Queue object. The queue holds all the tasks in the scheduler.
93 There are always three task queues in the scheduler. One for non-timeout
94 tasks (fd tasks performing tasks over specified file descriptor),
95 one for timeout tasks and one for generic tasks. */
96 struct SilcTaskQueueStruct {
97 SilcTask task; /* Pointer to all tasks */
98 struct timeval timeout; /* Current timeout */
99 SILC_MUTEX_DEFINE(lock); /* Queue's lock */
103 SILC Scheduler structure.
105 This is the actual schedule object in SILC. Both SILC client and server
106 uses this same scheduler. Actually, this scheduler could be used by any
107 program needing scheduling.
109 Following short description of the fields:
111 SilcTaskQueue fd_queue
113 Task queue hook for non-timeout tasks. Usually this means that these
114 tasks perform different kind of I/O on file descriptors. File
115 descriptors are usually network sockets but they actually can be
116 any file descriptors. This hook is initialized in silc_schedule_init
117 function. Timeout tasks should not be added to this queue because
118 they will never expire.
120 SilcTaskQueue timeout_queue
122 Task queue hook for timeout tasks. This hook is reserved specificly
123 for tasks with timeout. Non-timeout tasks should not be added to this
124 queue because they will never get scheduled. This hook is also
125 initialized in silc_schedule_init function.
127 SilcTaskQueue generic_queue
129 Task queue hook for generic tasks. This hook is reserved specificly
130 for generic tasks, tasks that apply to all file descriptors, except
131 to those that have specificly registered a non-timeout task. This hook
132 is also initialized in silc_schedule_init function.
134 SilcScheduleFd fd_list
136 List of file descriptors the scheduler is supposed to be listenning.
137 This is updated internally.
142 Size of the fd_list list. There can be `max_fd' many tasks in
143 the scheduler at once. The `last_fd' is the last valid entry
146 struct timeval *timeout;
148 Pointer to the schedules next timeout. Value of this timeout is
149 automatically updated in the silc_schedule function.
153 Marks validity of the scheduler. This is a boolean value. When this
154 is false the scheduler is terminated and the program will end. This
155 set to true when the scheduler is initialized with silc_schedule_init
161 File descriptor sets for select(). These are automatically managed
162 by the scheduler and should not be touched otherwise.
166 System specific wakeup context. On multi-threaded environments the
167 scheduler needs to be wakenup (in the thread) when tasks are added
168 or removed. This is initialized by silc_schedule_wakeup_init.
170 SILC_MUTEX_DEFINE(lock)
175 struct SilcScheduleStruct {
176 SilcTaskQueue fd_queue;
177 SilcTaskQueue timeout_queue;
178 SilcTaskQueue generic_queue;
179 SilcScheduleFd fd_list;
182 struct timeval *timeout;
185 SILC_MUTEX_DEFINE(lock);
189 /* Initializes the scheduler. This returns the scheduler context that
190 is given as arugment usually to all silc_schedule_* functions.
191 The `max_tasks' indicates the number of maximum tasks that the
192 scheduler can handle. */
194 SilcSchedule silc_schedule_init(int max_tasks)
196 SilcSchedule schedule;
198 SILC_LOG_DEBUG(("Initializing scheduler"));
200 schedule = silc_calloc(1, sizeof(*schedule));
202 /* Allocate three task queues, one for file descriptor based tasks,
203 one for timeout tasks and one for generic tasks. */
204 silc_task_queue_alloc(&schedule->fd_queue);
205 silc_task_queue_alloc(&schedule->timeout_queue);
206 silc_task_queue_alloc(&schedule->generic_queue);
211 /* Initialize the scheduler */
212 schedule->fd_list = silc_calloc(max_tasks, sizeof(*schedule->fd_list));
213 schedule->max_fd = max_tasks;
214 schedule->timeout = NULL;
215 schedule->valid = TRUE;
217 /* Allocate scheduler lock */
218 silc_mutex_alloc(&schedule->lock);
220 /* Initialize the wakeup, for multi-threads support */
221 schedule->wakeup = silc_schedule_wakeup_init(schedule);
226 /* Uninitializes the schedule. This is called when the program is ready
227 to end. This removes all tasks and task queues. Returns FALSE if the
228 scheduler could not be uninitialized. This happens when the scheduler
229 is still valid and silc_schedule_stop has not been called. */
231 bool silc_schedule_uninit(SilcSchedule schedule)
233 SILC_LOG_DEBUG(("Uninitializing scheduler"));
235 if (schedule->valid == TRUE)
238 /* Unregister all tasks */
239 silc_schedule_task_remove(schedule->fd_queue, SILC_ALL_TASKS);
240 silc_schedule_task_remove(schedule->timeout_queue, SILC_ALL_TASKS);
241 silc_schedule_task_remove(schedule->generic_queue, SILC_ALL_TASKS);
243 /* Unregister all task queues */
244 silc_task_queue_free(schedule->fd_queue);
245 silc_task_queue_free(schedule->timeout_queue);
246 silc_task_queue_free(schedule->generic_queue);
248 silc_free(schedule->fd_list);
250 /* Uninit the wakeup */
251 silc_schedule_wakeup_uninit(schedule->wakeup);
253 silc_mutex_free(schedule->lock);
258 /* Enlarge the capabilities of the scheduler to handle tasks to `max_tasks'. */
260 bool silc_schedule_reinit(SilcSchedule schedule, int max_tasks)
262 SILC_SCHEDULE_LOCK(schedule);
263 if (schedule->max_fd <= max_tasks)
265 schedule->fd_list = silc_realloc(schedule->fd_list,
266 (sizeof(*schedule->fd_list) * max_tasks));
267 schedule->max_fd = max_tasks;
268 SILC_SCHEDULE_UNLOCK(schedule);
272 /* Stops the schedule even if it is not supposed to be stopped yet.
273 After calling this, one should call silc_schedule_uninit (after the
274 silc_schedule has returned). */
276 void silc_schedule_stop(SilcSchedule schedule)
278 SILC_LOG_DEBUG(("Stopping scheduler"));
279 schedule->valid = FALSE;
282 /* Executes nontimeout tasks. It then checks whether any of ther fd tasks
283 was signaled by the silc_select. If some task was not signaled then
284 all generic tasks are executed for that task. The generic tasks are
285 never executed for task that has explicit fd task set. */
286 /* This holds the schedule->lock and the queue locks. */
288 static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule)
291 int i, last_fd = schedule->last_fd;
294 for (i = 0; i <= last_fd; i++) {
295 if (schedule->fd_list[i].events == 0)
298 fd = schedule->fd_list[i].fd;
300 /* First check whether this fd has task in the fd queue */
301 silc_mutex_lock(schedule->fd_queue->lock);
302 task = silc_task_find(schedule->fd_queue, fd);
304 /* If the task was found then execute its callbacks. If not then
305 execute all generic tasks for that fd. */
307 /* Validity of the task is checked always before and after
308 execution beacuse the task might have been unregistered
309 in the callback function, ie. it is not valid anymore. */
311 /* Is the task ready for reading */
312 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
313 silc_mutex_unlock(schedule->fd_queue->lock);
314 SILC_SCHEDULE_UNLOCK(schedule);
315 task->callback(schedule, SILC_TASK_READ, task->fd, task->context);
316 SILC_SCHEDULE_LOCK(schedule);
317 silc_mutex_lock(schedule->fd_queue->lock);
320 /* Is the task ready for writing */
321 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
322 silc_mutex_unlock(schedule->fd_queue->lock);
323 SILC_SCHEDULE_UNLOCK(schedule);
324 task->callback(schedule, SILC_TASK_WRITE, task->fd, task->context);
325 SILC_SCHEDULE_LOCK(schedule);
326 silc_mutex_lock(schedule->fd_queue->lock);
330 silc_schedule_task_remove(schedule->fd_queue, task);
332 silc_mutex_unlock(schedule->fd_queue->lock);
334 /* Run generic tasks for this fd. */
336 silc_mutex_unlock(schedule->fd_queue->lock);
338 silc_mutex_lock(schedule->generic_queue->lock);
339 if (!schedule->generic_queue->task) {
340 silc_mutex_unlock(schedule->generic_queue->lock);
344 task = schedule->generic_queue->task;
346 /* Validity of the task is checked always before and after
347 execution beacuse the task might have been unregistered
348 in the callback function, ie. it is not valid anymore. */
350 /* Is the task ready for reading */
351 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
352 silc_mutex_unlock(schedule->generic_queue->lock);
353 SILC_SCHEDULE_UNLOCK(schedule);
354 task->callback(schedule, SILC_TASK_READ, fd, task->context);
355 SILC_SCHEDULE_LOCK(schedule);
356 silc_mutex_lock(schedule->generic_queue->lock);
359 /* Is the task ready for writing */
360 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
361 silc_mutex_unlock(schedule->generic_queue->lock);
362 SILC_SCHEDULE_UNLOCK(schedule);
363 task->callback(schedule, SILC_TASK_WRITE, fd, task->context);
364 SILC_SCHEDULE_LOCK(schedule);
365 silc_mutex_lock(schedule->generic_queue->lock);
369 /* Invalid (unregistered) tasks are removed from the
371 if (schedule->generic_queue->task == task->next) {
372 silc_schedule_task_remove(schedule->generic_queue, task);
373 silc_mutex_unlock(schedule->generic_queue->lock);
378 silc_schedule_task_remove(schedule->generic_queue, task);
382 /* Break if there isn't more tasks in the queue */
383 if (schedule->generic_queue->task == task->next)
389 silc_mutex_unlock(schedule->generic_queue->lock);
394 /* Executes all tasks whose timeout has expired. The task is removed from
395 the task queue after the callback function has returned. Also, invalid
396 tasks are removed here. We don't have to care about priorities because
397 tasks are already sorted in their priority order at the registration
399 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
401 static void silc_schedule_dispatch_timeout(SilcSchedule schedule)
403 SilcTaskQueue queue = schedule->timeout_queue;
405 struct timeval curtime;
407 SILC_LOG_DEBUG(("Running timeout tasks"));
409 silc_gettimeofday(&curtime);
411 queue = schedule->timeout_queue;
412 if (queue && queue->task) {
415 /* Walk thorugh all tasks in the particular task queue and run all
416 the expired tasks. */
418 /* Execute the task if the timeout has expired */
419 if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
421 silc_mutex_unlock(queue->lock);
422 SILC_SCHEDULE_UNLOCK(schedule);
423 task->callback(schedule, SILC_TASK_EXPIRE, task->fd, task->context);
424 SILC_SCHEDULE_LOCK(schedule);
425 silc_mutex_lock(queue->lock);
428 /* Break if there isn't more tasks in the queue */
429 if (queue->task == task->next) {
430 silc_schedule_task_remove(queue, task);
436 /* Remove the task from queue */
437 silc_schedule_task_remove(queue, task->prev);
439 /* The timeout hasn't expired, check for next one */
441 /* Break if there isn't more tasks in the queue */
442 if (queue->task == task->next)
451 /* Calculates next timeout for select(). This is the timeout value
452 when at earliest some of the timeout tasks expire. If this is in the
453 past, they will be run now. */
454 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
456 static void silc_schedule_select_timeout(SilcSchedule schedule)
458 SilcTaskQueue queue = schedule->timeout_queue;
460 struct timeval curtime;
462 /* Get the current time */
463 silc_gettimeofday(&curtime);
464 schedule->timeout = NULL;
466 /* First task in the task queue has always the smallest timeout. */
469 if (task && task->valid == TRUE) {
470 /* If the timeout is in past, we will run the task and all other
471 timeout tasks from the past. */
472 if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
473 silc_schedule_dispatch_timeout(schedule);
475 /* The task(s) has expired and doesn't exist on the task queue
476 anymore. We continue with new timeout. */
477 queue = schedule->timeout_queue;
479 if (task == NULL || task->valid == FALSE)
483 /* Calculate the next timeout for select() */
484 queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
485 queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
486 if (queue->timeout.tv_sec < 0)
487 queue->timeout.tv_sec = 0;
489 /* We wouldn't want to go under zero, check for it. */
490 if (queue->timeout.tv_usec < 0) {
491 queue->timeout.tv_sec -= 1;
492 if (queue->timeout.tv_sec < 0)
493 queue->timeout.tv_sec = 0;
494 queue->timeout.tv_usec += 1000000L;
497 /* We've got the timeout value */
500 /* Task is not valid, remove it and try next one. */
501 silc_schedule_task_remove(queue, task);
503 if (queue->task == NULL)
508 /* Save the timeout */
510 schedule->timeout = &queue->timeout;
511 SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout->tv_sec,
512 schedule->timeout->tv_usec));
516 /* Runs the scheduler once and then returns. */
518 bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
520 struct timeval timeout;
523 SILC_LOG_DEBUG(("In scheduler loop"));
525 if (!schedule->is_locked)
526 SILC_SCHEDULE_LOCK(schedule);
528 /* If the task queues aren't initialized or we aren't valid anymore
530 if ((!schedule->fd_queue && !schedule->timeout_queue
531 && !schedule->generic_queue) || schedule->valid == FALSE) {
532 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
533 if (!schedule->is_locked)
534 SILC_SCHEDULE_UNLOCK(schedule);
538 /* Calculate next timeout for silc_select(). This is the timeout value
539 when at earliest some of the timeout tasks expire. */
540 silc_mutex_lock(schedule->timeout_queue->lock);
541 silc_schedule_select_timeout(schedule);
542 silc_mutex_unlock(schedule->timeout_queue->lock);
544 if (timeout_usecs >= 0) {
546 timeout.tv_usec = timeout_usecs;
547 schedule->timeout = &timeout;
550 SILC_SCHEDULE_UNLOCK(schedule);
552 /* This is the main select(). The program blocks here until some
553 of the selected file descriptors change status or the selected
555 SILC_LOG_DEBUG(("Select"));
556 ret = silc_select(schedule->fd_list, schedule->last_fd + 1,
559 SILC_SCHEDULE_LOCK(schedule);
566 SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
570 silc_mutex_lock(schedule->timeout_queue->lock);
571 silc_schedule_dispatch_timeout(schedule);
572 silc_mutex_unlock(schedule->timeout_queue->lock);
575 /* There is some data available now */
576 SILC_LOG_DEBUG(("Running non-timeout tasks"));
577 silc_schedule_dispatch_nontimeout(schedule);
581 if (!schedule->is_locked)
582 SILC_SCHEDULE_UNLOCK(schedule);
587 /* The SILC scheduler. This is actually the main routine in SILC programs.
588 When this returns the program is to be ended. Before this function can
589 be called, one must call silc_schedule_init function. */
591 void silc_schedule(SilcSchedule schedule)
593 SILC_LOG_DEBUG(("Running scheduler"));
595 if (schedule->valid == FALSE) {
596 SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
600 SILC_SCHEDULE_LOCK(schedule);
601 schedule->is_locked = TRUE;
603 /* Start the scheduler loop */
604 while (silc_schedule_one(schedule, -1))
607 SILC_SCHEDULE_UNLOCK(schedule);
610 /* Wakes up the scheduler. This is used only in multi-threaded
611 environments where threads may add new tasks or remove old tasks
612 from task queues. This is called to wake up the scheduler in the
613 main thread so that it detects the changes in the task queues.
614 If threads support is not compiled in this function has no effect.
615 Implementation of this function is platform specific. */
617 void silc_schedule_wakeup(SilcSchedule schedule)
620 SILC_LOG_DEBUG(("Wakeup scheduler"));
621 SILC_SCHEDULE_LOCK(schedule);
622 silc_schedule_wakeup_internal(schedule->wakeup);
623 SILC_SCHEDULE_UNLOCK(schedule);
627 /* Add new task to the scheduler */
629 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
630 SilcTaskCallback callback, void *context,
631 long seconds, long useconds,
633 SilcTaskPriority priority)
639 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", fd,
642 queue = SILC_SCHEDULE_GET_QUEUE(type);
644 /* If the task is generic task, we check whether this task has already
645 been registered. Generic tasks are registered only once and after that
646 the same task applies to all file descriptors to be registered. */
647 if (type == SILC_TASK_GENERIC) {
648 silc_mutex_lock(queue->lock);
651 SilcTask task = queue->task;
653 if ((task->callback == callback) && (task->context == context)) {
654 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
656 silc_mutex_unlock(queue->lock);
658 /* Add the fd to be listened, the task found now applies to this
660 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
664 if (queue->task == task->next)
671 silc_mutex_unlock(queue->lock);
674 newtask = silc_calloc(1, sizeof(*newtask));
676 newtask->context = context;
677 newtask->callback = callback;
678 newtask->valid = TRUE;
679 newtask->priority = priority;
680 newtask->type = type;
681 newtask->next = newtask;
682 newtask->prev = newtask;
684 /* Create timeout if marked to be timeout task */
685 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
686 silc_gettimeofday(&newtask->timeout);
687 newtask->timeout.tv_sec += seconds + (useconds / 1000000L);
688 newtask->timeout.tv_usec += (useconds % 1000000L);
689 if (newtask->timeout.tv_usec > 999999L) {
690 newtask->timeout.tv_sec += 1;
691 newtask->timeout.tv_usec -= 1000000L;
696 /* If the task is non-timeout task we have to tell the scheduler that we
697 would like to have these tasks scheduled at some odd distant future. */
698 if (type != SILC_TASK_TIMEOUT)
699 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
701 silc_mutex_lock(queue->lock);
703 /* Is this first task of the queue? */
704 if (queue->task == NULL) {
705 queue->task = newtask;
706 silc_mutex_unlock(queue->lock);
711 newtask = silc_task_add_timeout(queue, newtask, priority);
713 newtask = silc_task_add(queue, newtask, priority);
715 silc_mutex_unlock(queue->lock);
720 /* Removes a task from the scheduler */
722 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
724 SilcTaskQueue queue = SILC_SCHEDULE_GET_QUEUE(task->type);
726 /* Unregister all tasks */
727 if (task == SILC_ALL_TASKS) {
729 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
731 silc_mutex_lock(queue->lock);
734 silc_mutex_unlock(queue->lock);
743 if (queue->task == next->next)
748 silc_mutex_unlock(queue->lock);
752 SILC_LOG_DEBUG(("Unregistering task"));
754 silc_mutex_lock(queue->lock);
756 /* Unregister the specific task */
760 silc_mutex_unlock(queue->lock);
763 /* Remove task by fd */
765 void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
767 SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
769 silc_task_del_by_fd(schedule->timeout_queue, fd);
770 silc_task_del_by_fd(schedule->fd_queue, fd);
773 /* Remove task by task callback. */
775 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
776 SilcTaskCallback callback)
778 SILC_LOG_DEBUG(("Unregister task by callback"));
780 silc_task_del_by_callback(schedule->timeout_queue, callback);
781 silc_task_del_by_callback(schedule->fd_queue, callback);
782 silc_task_del_by_callback(schedule->generic_queue, callback);
785 /* Remove task by context. */
787 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
789 SILC_LOG_DEBUG(("Unregister task by context"));
791 silc_task_del_by_context(schedule->timeout_queue, context);
792 silc_task_del_by_context(schedule->fd_queue, context);
793 silc_task_del_by_context(schedule->generic_queue, context);
796 /* Sets a file descriptor to be listened by select() in scheduler. One can
797 call this directly if wanted. This can be called multiple times for
798 one file descriptor to set different iomasks. */
800 void silc_schedule_set_listen_fd(SilcSchedule schedule,
801 SilcUInt32 fd, SilcTaskEvent iomask)
806 SILC_SCHEDULE_LOCK(schedule);
808 for (i = 0; i < schedule->max_fd; i++)
809 if (schedule->fd_list[i].fd == fd) {
810 schedule->fd_list[i].fd = fd;
811 schedule->fd_list[i].events = iomask;
812 if (i > schedule->last_fd)
813 schedule->last_fd = i;
819 for (i = 0; i < schedule->max_fd; i++)
820 if (schedule->fd_list[i].events == 0) {
821 schedule->fd_list[i].fd = fd;
822 schedule->fd_list[i].events = iomask;
823 if (i > schedule->last_fd)
824 schedule->last_fd = i;
828 SILC_SCHEDULE_UNLOCK(schedule);
831 /* Removes a file descriptor from listen list. */
833 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
837 SILC_SCHEDULE_LOCK(schedule);
839 SILC_LOG_DEBUG(("Unset listen fd %d", fd));
841 for (i = 0; i < schedule->max_fd; i++)
842 if (schedule->fd_list[i].fd == fd) {
843 schedule->fd_list[i].fd = 0;
844 schedule->fd_list[i].events = 0;
845 if (schedule->last_fd == i)
846 schedule->last_fd = schedule->max_fd - 1;
850 SILC_SCHEDULE_UNLOCK(schedule);
853 /* Allocates a newtask task queue into the scheduler */
855 static void silc_task_queue_alloc(SilcTaskQueue *queue)
857 *queue = silc_calloc(1, sizeof(**queue));
858 silc_mutex_alloc(&(*queue)->lock);
861 /* Free's a task queue. */
863 static void silc_task_queue_free(SilcTaskQueue queue)
865 silc_mutex_free(queue->lock);
869 /* Return task by its fd. */
871 static SilcTask silc_task_find(SilcTaskQueue queue, SilcUInt32 fd)
883 if (queue->task == next->next)
891 /* Adds a non-timeout task into the task queue. This function is used
892 by silc_task_register function. Returns a pointer to the registered
895 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
896 SilcTaskPriority priority)
898 SilcTask task, next, prev;
900 /* Take the first task in the queue */
904 case SILC_TASK_PRI_LOW:
905 /* Lowest priority. The task is added at the end of the list. */
907 newtask->prev = prev;
908 newtask->next = task;
909 prev->next = newtask;
910 task->prev = newtask;
912 case SILC_TASK_PRI_NORMAL:
913 /* Normal priority. The task is added before lower priority tasks
914 but after tasks with higher priority. */
916 while(prev != task) {
917 if (prev->priority > SILC_TASK_PRI_LOW)
922 /* There are only lower priorities in the list, we will
923 sit before them and become the first task in the queue. */
925 newtask->prev = prev;
926 newtask->next = task;
927 task->prev = newtask;
928 prev->next = newtask;
930 /* We are now the first task in queue */
931 queue->task = newtask;
933 /* Found a spot from the list, add the task to the list. */
935 newtask->prev = prev;
936 newtask->next = next;
937 prev->next = newtask;
938 next->prev = newtask;
949 /* Return the timeout task with smallest timeout. */
951 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
965 if (silc_schedule_task_timeout_compare(&prev->timeout, &task->timeout))
974 /* Adds a timeout task into the task queue. This function is used by
975 silc_task_register function. Returns a pointer to the registered
976 task. Timeout tasks are sorted by their timeout value in ascending
977 order. The priority matters if there are more than one task with
980 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
981 SilcTaskPriority priority)
983 SilcTask task, prev, next;
985 /* Take the first task in the queue */
988 /* Take last task from the list */
992 case SILC_TASK_PRI_LOW:
993 /* Lowest priority. The task is added at the end of the list. */
994 while(prev != task) {
996 /* If we have longer timeout than with the task head of us
997 we have found our spot. */
998 if (silc_schedule_task_timeout_compare(&prev->timeout,
1002 /* If we are equal size of timeout we will be after it. */
1003 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1007 /* We have shorter timeout, compare to next one. */
1010 /* Found a spot from the list, add the task to the list. */
1012 newtask->prev = prev;
1013 newtask->next = next;
1014 prev->next = newtask;
1015 next->prev = newtask;
1018 /* Check if we are going to be the first task in the queue */
1019 if (silc_schedule_task_timeout_compare(&prev->timeout,
1022 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1026 /* We are now the first task in queue */
1027 queue->task = newtask;
1030 case SILC_TASK_PRI_NORMAL:
1031 /* Normal priority. The task is added before lower priority tasks
1032 but after tasks with higher priority. */
1033 while(prev != task) {
1035 /* If we have longer timeout than with the task head of us
1036 we have found our spot. */
1037 if (silc_schedule_task_timeout_compare(&prev->timeout,
1041 /* If we are equal size of timeout, priority kicks in place. */
1042 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1044 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1047 /* We have shorter timeout or higher priority, compare to next one. */
1050 /* Found a spot from the list, add the task to the list. */
1052 newtask->prev = prev;
1053 newtask->next = next;
1054 prev->next = newtask;
1055 next->prev = newtask;
1058 /* Check if we are going to be the first task in the queue */
1059 if (silc_schedule_task_timeout_compare(&prev->timeout,
1062 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1064 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1067 /* We are now the first task in queue */
1068 queue->task = newtask;
1079 /* Removes (unregisters) a task from particular task queue. This function
1080 is used internally by scheduler. This must be called holding the
1083 static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task)
1085 SilcTask first, old, next;
1087 if (!queue || !task)
1094 first = queue->task;
1096 /* Unregister all tasks in queue */
1097 if (task == SILC_ALL_TASKS) {
1098 SILC_LOG_DEBUG(("Removing all tasks at once"));
1103 silc_free(next->prev);
1112 SILC_LOG_DEBUG(("Removing task"));
1114 /* Unregister the task */
1118 SilcTask prev, next;
1125 if (prev == old && next == old)
1127 if (queue->task == old)
1128 queue->task = silc_task_get_first(queue, next);
1141 /* Compare two time values. If the first argument is smaller than the
1142 second this function returns TRUE. */
1144 static int silc_schedule_task_timeout_compare(struct timeval *smaller,
1145 struct timeval *bigger)
1147 if ((smaller->tv_sec < bigger->tv_sec) ||
1148 ((smaller->tv_sec == bigger->tv_sec) &&
1149 (smaller->tv_usec < bigger->tv_usec)))
1155 static void silc_task_del_by_fd(SilcTaskQueue queue, SilcUInt32 fd)
1159 silc_mutex_lock(queue->lock);
1162 silc_mutex_unlock(queue->lock);
1170 next->valid = FALSE;
1171 if (queue->task == next->next)
1176 silc_mutex_unlock(queue->lock);
1179 static void silc_task_del_by_callback(SilcTaskQueue queue,
1180 SilcTaskCallback callback)
1184 silc_mutex_lock(queue->lock);
1187 silc_mutex_unlock(queue->lock);
1194 if (next->callback == callback)
1195 next->valid = FALSE;
1196 if (queue->task == next->next)
1201 silc_mutex_unlock(queue->lock);
1204 static void silc_task_del_by_context(SilcTaskQueue queue, void *context)
1208 silc_mutex_lock(queue->lock);
1211 silc_mutex_unlock(queue->lock);
1218 if (next->context == context)
1219 next->valid = FALSE;
1220 if (queue->task == next->next)
1225 silc_mutex_unlock(queue->lock);