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/, win32/ and such. */
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 platform specific scheduler. This for example initializes
35 the wakeup mechanism of the scheduler. In multi-threaded environment
36 the scheduler needs to be wakenup when tasks are added or removed from
37 the task queues. Returns context to the platform specific scheduler. */
38 void *silc_schedule_internal_init(SilcSchedule schedule);
40 /* Uninitializes the platform specific scheduler context. */
41 void silc_schedule_internal_uninit(void *context);
43 /* Wakes up the scheduler. This is platform specific routine */
44 void silc_schedule_internal_wakeup(void *context);
47 void silc_schedule_internal_signal_register(void *context,
49 SilcTaskCallback callback,
50 void *callback_context);
52 /* Unregister signal */
53 void silc_schedule_internal_signal_unregister(void *context,
55 SilcTaskCallback callback,
56 void *callback_context);
58 /* Mark signal to be called later. */
59 void silc_schedule_internal_signal_call(void *context, SilcUInt32 signal);
61 /* Call all signals */
62 void silc_schedule_internal_signals_call(void *context,
63 SilcSchedule schedule);
65 /* Block registered signals in scheduler. */
66 void silc_schedule_internal_signals_block(void *context);
68 /* Unblock registered signals in schedule. */
69 void silc_schedule_internal_signals_unblock(void *context);
71 /* Internal task management routines. */
73 static void silc_task_queue_alloc(SilcTaskQueue *queue);
74 static void silc_task_queue_free(SilcTaskQueue queue);
75 static SilcTask silc_task_find(SilcTaskQueue queue, SilcUInt32 fd);
76 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
77 SilcTaskPriority priority);
78 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first);
79 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
80 SilcTaskPriority priority);
81 static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task);
82 static int silc_schedule_task_timeout_compare(struct timeval *smaller,
83 struct timeval *bigger);
84 static void silc_task_del_by_context(SilcTaskQueue queue, void *context);
85 static void silc_task_del_by_callback(SilcTaskQueue queue,
86 SilcTaskCallback callback);
87 static void silc_task_del_by_fd(SilcTaskQueue queue, SilcUInt32 fd);
89 /* Returns the task queue by task type */
90 #define SILC_SCHEDULE_GET_QUEUE(type) \
91 (type == SILC_TASK_FD ? schedule->fd_queue : \
92 type == SILC_TASK_TIMEOUT ? schedule->timeout_queue : \
93 schedule->generic_queue)
95 /* Locks. These also blocks signals that we care about and thus guarantee
96 that while we are in scheduler no signals can happen. This way we can
97 synchronise signals with SILC Scheduler. */
98 #define SILC_SCHEDULE_LOCK(schedule) \
100 silc_schedule_internal_signals_block(schedule->internal); \
101 silc_mutex_lock(schedule->lock); \
103 #define SILC_SCHEDULE_UNLOCK(schedule) \
105 silc_mutex_unlock(schedule->lock); \
106 silc_schedule_internal_signals_unblock(schedule->internal); \
109 /* SILC Task object. Represents one task in the scheduler. */
110 struct SilcTaskStruct {
112 SilcTaskCallback callback; /* Task callback */
113 void *context; /* Task callback context */
114 struct timeval timeout; /* Set for timeout tasks */
115 unsigned int valid : 1; /* Set when task is valid */
116 unsigned int priority : 2; /* Priority of the task */
117 unsigned int type : 5; /* Type of the task */
119 /* Pointers forming doubly linked circular list */
120 struct SilcTaskStruct *next;
121 struct SilcTaskStruct *prev;
124 /* SILC Task Queue object. The queue holds all the tasks in the scheduler.
125 There are always three task queues in the scheduler. One for non-timeout
126 tasks (fd tasks performing tasks over specified file descriptor),
127 one for timeout tasks and one for generic tasks. */
128 struct SilcTaskQueueStruct {
129 SilcTask task; /* Pointer to all tasks */
130 struct timeval timeout; /* Current timeout */
131 SILC_MUTEX_DEFINE(lock); /* Queue's lock */
135 SILC Scheduler structure.
137 This is the actual schedule object in SILC. Both SILC client and server
138 uses this same scheduler. Actually, this scheduler could be used by any
139 program needing scheduling.
141 Following short description of the fields:
143 SilcTaskQueue fd_queue
145 Task queue hook for non-timeout tasks. Usually this means that these
146 tasks perform different kind of I/O on file descriptors. File
147 descriptors are usually network sockets but they actually can be
148 any file descriptors. This hook is initialized in silc_schedule_init
149 function. Timeout tasks should not be added to this queue because
150 they will never expire.
152 SilcTaskQueue timeout_queue
154 Task queue hook for timeout tasks. This hook is reserved specificly
155 for tasks with timeout. Non-timeout tasks should not be added to this
156 queue because they will never get scheduled. This hook is also
157 initialized in silc_schedule_init function.
159 SilcTaskQueue generic_queue
161 Task queue hook for generic tasks. This hook is reserved specificly
162 for generic tasks, tasks that apply to all file descriptors, except
163 to those that have specificly registered a non-timeout task. This hook
164 is also initialized in silc_schedule_init function.
166 SilcScheduleFd fd_list
168 List of file descriptors the scheduler is supposed to be listenning.
169 This is updated internally.
174 Size of the fd_list list. There can be `max_fd' many tasks in
175 the scheduler at once. The `last_fd' is the last valid entry
178 struct timeval *timeout;
180 Pointer to the schedules next timeout. Value of this timeout is
181 automatically updated in the silc_schedule function.
185 Marks validity of the scheduler. This is a boolean value. When this
186 is false the scheduler is terminated and the program will end. This
187 set to true when the scheduler is initialized with silc_schedule_init
193 File descriptor sets for select(). These are automatically managed
194 by the scheduler and should not be touched otherwise.
198 System specific scheduler context.
200 SILC_MUTEX_DEFINE(lock)
206 TRUE when tasks has been registered from signals. Next round in
207 scheduler will call the callbacks when this is TRUE.
210 struct SilcScheduleStruct {
211 SilcTaskQueue fd_queue;
212 SilcTaskQueue timeout_queue;
213 SilcTaskQueue generic_queue;
214 SilcScheduleFd fd_list;
217 struct timeval *timeout;
220 SILC_MUTEX_DEFINE(lock);
225 /* Initializes the scheduler. This returns the scheduler context that
226 is given as arugment usually to all silc_schedule_* functions.
227 The `max_tasks' indicates the number of maximum tasks that the
228 scheduler can handle. */
230 SilcSchedule silc_schedule_init(int max_tasks)
232 SilcSchedule schedule;
234 SILC_LOG_DEBUG(("Initializing scheduler"));
236 schedule = silc_calloc(1, sizeof(*schedule));
238 /* Allocate three task queues, one for file descriptor based tasks,
239 one for timeout tasks and one for generic tasks. */
240 silc_task_queue_alloc(&schedule->fd_queue);
241 silc_task_queue_alloc(&schedule->timeout_queue);
242 silc_task_queue_alloc(&schedule->generic_queue);
247 /* Initialize the scheduler */
248 schedule->fd_list = silc_calloc(max_tasks, sizeof(*schedule->fd_list));
249 schedule->max_fd = max_tasks;
250 schedule->timeout = NULL;
251 schedule->valid = TRUE;
253 /* Allocate scheduler lock */
254 silc_mutex_alloc(&schedule->lock);
256 /* Initialize the platform specific scheduler. */
257 schedule->internal = silc_schedule_internal_init(schedule);
262 /* Uninitializes the schedule. This is called when the program is ready
263 to end. This removes all tasks and task queues. Returns FALSE if the
264 scheduler could not be uninitialized. This happens when the scheduler
265 is still valid and silc_schedule_stop has not been called. */
267 bool silc_schedule_uninit(SilcSchedule schedule)
269 SILC_LOG_DEBUG(("Uninitializing scheduler"));
271 if (schedule->valid == TRUE)
274 /* Unregister all tasks */
275 silc_schedule_task_remove(schedule->fd_queue, SILC_ALL_TASKS);
276 silc_schedule_task_remove(schedule->timeout_queue, SILC_ALL_TASKS);
277 silc_schedule_task_remove(schedule->generic_queue, SILC_ALL_TASKS);
279 /* Unregister all task queues */
280 silc_task_queue_free(schedule->fd_queue);
281 silc_task_queue_free(schedule->timeout_queue);
282 silc_task_queue_free(schedule->generic_queue);
284 silc_free(schedule->fd_list);
286 /* Uninit the platform specific scheduler. */
287 silc_schedule_internal_uninit(schedule->internal);
289 silc_mutex_free(schedule->lock);
295 /* Enlarge the capabilities of the scheduler to handle tasks to `max_tasks'. */
297 bool silc_schedule_reinit(SilcSchedule schedule, int max_tasks)
299 SILC_SCHEDULE_LOCK(schedule);
300 if (schedule->max_fd <= max_tasks)
302 schedule->fd_list = silc_realloc(schedule->fd_list,
303 (sizeof(*schedule->fd_list) * max_tasks));
304 schedule->max_fd = max_tasks;
305 SILC_SCHEDULE_UNLOCK(schedule);
309 /* Stops the schedule even if it is not supposed to be stopped yet.
310 After calling this, one should call silc_schedule_uninit (after the
311 silc_schedule has returned). */
313 void silc_schedule_stop(SilcSchedule schedule)
315 SILC_LOG_DEBUG(("Stopping scheduler"));
316 SILC_SCHEDULE_LOCK(schedule);
317 schedule->valid = FALSE;
318 SILC_SCHEDULE_UNLOCK(schedule);
321 /* Executes nontimeout tasks. It then checks whether any of ther fd tasks
322 was signaled by the silc_select. If some task was not signaled then
323 all generic tasks are executed for that task. The generic tasks are
324 never executed for task that has explicit fd task set. */
325 /* This holds the schedule->lock and the queue locks. */
327 static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule)
330 int i, last_fd = schedule->last_fd;
333 for (i = 0; i <= last_fd; i++) {
334 if (schedule->fd_list[i].events == 0)
337 fd = schedule->fd_list[i].fd;
339 /* First check whether this fd has task in the fd queue */
340 silc_mutex_lock(schedule->fd_queue->lock);
341 task = silc_task_find(schedule->fd_queue, fd);
343 /* If the task was found then execute its callbacks. If not then
344 execute all generic tasks for that fd. */
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->fd_queue->lock);
353 SILC_SCHEDULE_UNLOCK(schedule);
354 task->callback(schedule, SILC_TASK_READ, task->fd, task->context);
355 SILC_SCHEDULE_LOCK(schedule);
356 silc_mutex_lock(schedule->fd_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->fd_queue->lock);
362 SILC_SCHEDULE_UNLOCK(schedule);
363 task->callback(schedule, SILC_TASK_WRITE, task->fd, task->context);
364 SILC_SCHEDULE_LOCK(schedule);
365 silc_mutex_lock(schedule->fd_queue->lock);
369 silc_schedule_task_remove(schedule->fd_queue, task);
371 silc_mutex_unlock(schedule->fd_queue->lock);
373 /* Run generic tasks for this fd. */
375 silc_mutex_unlock(schedule->fd_queue->lock);
377 silc_mutex_lock(schedule->generic_queue->lock);
378 if (!schedule->generic_queue->task) {
379 silc_mutex_unlock(schedule->generic_queue->lock);
383 task = schedule->generic_queue->task;
385 /* Validity of the task is checked always before and after
386 execution beacuse the task might have been unregistered
387 in the callback function, ie. it is not valid anymore. */
389 /* Is the task ready for reading */
390 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
391 silc_mutex_unlock(schedule->generic_queue->lock);
392 SILC_SCHEDULE_UNLOCK(schedule);
393 task->callback(schedule, SILC_TASK_READ, fd, task->context);
394 SILC_SCHEDULE_LOCK(schedule);
395 silc_mutex_lock(schedule->generic_queue->lock);
398 /* Is the task ready for writing */
399 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
400 silc_mutex_unlock(schedule->generic_queue->lock);
401 SILC_SCHEDULE_UNLOCK(schedule);
402 task->callback(schedule, SILC_TASK_WRITE, fd, task->context);
403 SILC_SCHEDULE_LOCK(schedule);
404 silc_mutex_lock(schedule->generic_queue->lock);
408 /* Invalid (unregistered) tasks are removed from the
410 if (schedule->generic_queue->task == task->next) {
411 silc_schedule_task_remove(schedule->generic_queue, task);
412 silc_mutex_unlock(schedule->generic_queue->lock);
417 silc_schedule_task_remove(schedule->generic_queue, task);
421 /* Break if there isn't more tasks in the queue */
422 if (schedule->generic_queue->task == task->next)
428 silc_mutex_unlock(schedule->generic_queue->lock);
433 /* Executes all tasks whose timeout has expired. The task is removed from
434 the task queue after the callback function has returned. Also, invalid
435 tasks are removed here. We don't have to care about priorities because
436 tasks are already sorted in their priority order at the registration
438 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
440 static void silc_schedule_dispatch_timeout(SilcSchedule schedule)
442 SilcTaskQueue queue = schedule->timeout_queue;
444 struct timeval curtime;
446 SILC_LOG_DEBUG(("Running timeout tasks"));
448 silc_gettimeofday(&curtime);
450 queue = schedule->timeout_queue;
451 if (queue && queue->task) {
454 /* Walk thorugh all tasks in the particular task queue and run all
455 the expired tasks. */
457 /* Execute the task if the timeout has expired */
458 if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
460 silc_mutex_unlock(queue->lock);
461 SILC_SCHEDULE_UNLOCK(schedule);
462 task->callback(schedule, SILC_TASK_EXPIRE, task->fd, task->context);
463 SILC_SCHEDULE_LOCK(schedule);
464 silc_mutex_lock(queue->lock);
467 /* Break if there isn't more tasks in the queue */
468 if (queue->task == task->next) {
469 silc_schedule_task_remove(queue, task);
475 /* Remove the task from queue */
476 silc_schedule_task_remove(queue, task->prev);
478 /* The timeout hasn't expired, check for next one */
480 /* Break if there isn't more tasks in the queue */
481 if (queue->task == task->next)
490 /* Calculates next timeout for select(). This is the timeout value
491 when at earliest some of the timeout tasks expire. If this is in the
492 past, they will be run now. */
493 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
495 static void silc_schedule_select_timeout(SilcSchedule schedule)
497 SilcTaskQueue queue = schedule->timeout_queue;
499 struct timeval curtime;
501 /* Get the current time */
502 silc_gettimeofday(&curtime);
503 schedule->timeout = NULL;
505 /* First task in the task queue has always the smallest timeout. */
508 if (task && task->valid == TRUE) {
509 /* If the timeout is in past, we will run the task and all other
510 timeout tasks from the past. */
511 if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
512 silc_schedule_dispatch_timeout(schedule);
514 /* The task(s) has expired and doesn't exist on the task queue
515 anymore. We continue with new timeout. */
516 queue = schedule->timeout_queue;
518 if (task == NULL || task->valid == FALSE)
522 /* Calculate the next timeout for select() */
523 queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
524 queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
525 if (queue->timeout.tv_sec < 0)
526 queue->timeout.tv_sec = 0;
528 /* We wouldn't want to go under zero, check for it. */
529 if (queue->timeout.tv_usec < 0) {
530 queue->timeout.tv_sec -= 1;
531 if (queue->timeout.tv_sec < 0)
532 queue->timeout.tv_sec = 0;
533 queue->timeout.tv_usec += 1000000L;
536 /* We've got the timeout value */
539 /* Task is not valid, remove it and try next one. */
540 silc_schedule_task_remove(queue, task);
542 if (queue->task == NULL)
547 /* Save the timeout */
549 schedule->timeout = &queue->timeout;
550 SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout->tv_sec,
551 schedule->timeout->tv_usec));
555 /* Runs the scheduler once and then returns. */
557 bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
559 struct timeval timeout;
562 SILC_LOG_DEBUG(("In scheduler loop"));
564 if (!schedule->is_locked)
565 SILC_SCHEDULE_LOCK(schedule);
567 /* Deliver signals if any has been set to be called */
568 if (schedule->signal_tasks) {
569 SILC_SCHEDULE_UNLOCK(schedule);
570 silc_schedule_internal_signals_call(schedule->internal, schedule);
571 schedule->signal_tasks = FALSE;
572 SILC_SCHEDULE_LOCK(schedule);
575 /* If the task queues aren't initialized or we aren't valid anymore
577 if ((!schedule->fd_queue && !schedule->timeout_queue
578 && !schedule->generic_queue) || schedule->valid == FALSE) {
579 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
580 if (!schedule->is_locked)
581 SILC_SCHEDULE_UNLOCK(schedule);
585 /* Calculate next timeout for silc_select(). This is the timeout value
586 when at earliest some of the timeout tasks expire. */
587 silc_mutex_lock(schedule->timeout_queue->lock);
588 silc_schedule_select_timeout(schedule);
589 silc_mutex_unlock(schedule->timeout_queue->lock);
591 if (timeout_usecs >= 0) {
593 timeout.tv_usec = timeout_usecs;
594 schedule->timeout = &timeout;
597 SILC_SCHEDULE_UNLOCK(schedule);
599 /* This is the main select(). The program blocks here until some
600 of the selected file descriptors change status or the selected
602 SILC_LOG_DEBUG(("Select"));
603 ret = silc_select(schedule->fd_list, schedule->last_fd + 1,
606 SILC_SCHEDULE_LOCK(schedule);
613 SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
617 silc_mutex_lock(schedule->timeout_queue->lock);
618 silc_schedule_dispatch_timeout(schedule);
619 silc_mutex_unlock(schedule->timeout_queue->lock);
622 /* There is some data available now */
623 SILC_LOG_DEBUG(("Running non-timeout tasks"));
624 silc_schedule_dispatch_nontimeout(schedule);
628 if (!schedule->is_locked)
629 SILC_SCHEDULE_UNLOCK(schedule);
634 /* The SILC scheduler. This is actually the main routine in SILC programs.
635 When this returns the program is to be ended. Before this function can
636 be called, one must call silc_schedule_init function. */
638 void silc_schedule(SilcSchedule schedule)
640 SILC_LOG_DEBUG(("Running scheduler"));
642 if (schedule->valid == FALSE) {
643 SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
647 SILC_SCHEDULE_LOCK(schedule);
648 schedule->is_locked = TRUE;
650 /* Start the scheduler loop */
651 while (silc_schedule_one(schedule, -1))
654 SILC_SCHEDULE_UNLOCK(schedule);
657 /* Wakes up the scheduler. This is used only in multi-threaded
658 environments where threads may add new tasks or remove old tasks
659 from task queues. This is called to wake up the scheduler in the
660 main thread so that it detects the changes in the task queues.
661 If threads support is not compiled in this function has no effect.
662 Implementation of this function is platform specific. */
664 void silc_schedule_wakeup(SilcSchedule schedule)
667 SILC_LOG_DEBUG(("Wakeup scheduler"));
668 SILC_SCHEDULE_LOCK(schedule);
669 silc_schedule_internal_wakeup(schedule->internal);
670 SILC_SCHEDULE_UNLOCK(schedule);
674 /* Add new task to the scheduler */
676 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
677 SilcTaskCallback callback, void *context,
678 long seconds, long useconds,
680 SilcTaskPriority priority)
686 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", fd,
689 queue = SILC_SCHEDULE_GET_QUEUE(type);
691 /* If the task is generic task, we check whether this task has already
692 been registered. Generic tasks are registered only once and after that
693 the same task applies to all file descriptors to be registered. */
694 if (type == SILC_TASK_GENERIC) {
695 silc_mutex_lock(queue->lock);
698 SilcTask task = queue->task;
700 if ((task->callback == callback) && (task->context == context)) {
701 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
703 silc_mutex_unlock(queue->lock);
705 /* Add the fd to be listened, the task found now applies to this
707 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
711 if (queue->task == task->next)
718 silc_mutex_unlock(queue->lock);
721 newtask = silc_calloc(1, sizeof(*newtask));
723 newtask->context = context;
724 newtask->callback = callback;
725 newtask->valid = TRUE;
726 newtask->priority = priority;
727 newtask->type = type;
728 newtask->next = newtask;
729 newtask->prev = newtask;
731 /* Create timeout if marked to be timeout task */
732 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
733 silc_gettimeofday(&newtask->timeout);
734 newtask->timeout.tv_sec += seconds + (useconds / 1000000L);
735 newtask->timeout.tv_usec += (useconds % 1000000L);
736 if (newtask->timeout.tv_usec > 999999L) {
737 newtask->timeout.tv_sec += 1;
738 newtask->timeout.tv_usec -= 1000000L;
743 /* If the task is non-timeout task we have to tell the scheduler that we
744 would like to have these tasks scheduled at some odd distant future. */
745 if (type != SILC_TASK_TIMEOUT)
746 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
748 silc_mutex_lock(queue->lock);
750 /* Is this first task of the queue? */
751 if (queue->task == NULL) {
752 queue->task = newtask;
753 silc_mutex_unlock(queue->lock);
758 newtask = silc_task_add_timeout(queue, newtask, priority);
760 newtask = silc_task_add(queue, newtask, priority);
762 silc_mutex_unlock(queue->lock);
767 /* Removes a task from the scheduler */
769 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
771 SilcTaskQueue queue = SILC_SCHEDULE_GET_QUEUE(task->type);
773 /* Unregister all tasks */
774 if (task == SILC_ALL_TASKS) {
776 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
778 silc_mutex_lock(queue->lock);
781 silc_mutex_unlock(queue->lock);
790 if (queue->task == next->next)
795 silc_mutex_unlock(queue->lock);
799 SILC_LOG_DEBUG(("Unregistering task"));
801 silc_mutex_lock(queue->lock);
803 /* Unregister the specific task */
807 silc_mutex_unlock(queue->lock);
810 /* Remove task by fd */
812 void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
814 SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
816 silc_task_del_by_fd(schedule->timeout_queue, fd);
817 silc_task_del_by_fd(schedule->fd_queue, fd);
820 /* Remove task by task callback. */
822 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
823 SilcTaskCallback callback)
825 SILC_LOG_DEBUG(("Unregister task by callback"));
827 silc_task_del_by_callback(schedule->timeout_queue, callback);
828 silc_task_del_by_callback(schedule->fd_queue, callback);
829 silc_task_del_by_callback(schedule->generic_queue, callback);
832 /* Remove task by context. */
834 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
836 SILC_LOG_DEBUG(("Unregister task by context"));
838 silc_task_del_by_context(schedule->timeout_queue, context);
839 silc_task_del_by_context(schedule->fd_queue, context);
840 silc_task_del_by_context(schedule->generic_queue, context);
843 /* Sets a file descriptor to be listened by select() in scheduler. One can
844 call this directly if wanted. This can be called multiple times for
845 one file descriptor to set different iomasks. */
847 void silc_schedule_set_listen_fd(SilcSchedule schedule,
848 SilcUInt32 fd, SilcTaskEvent iomask)
853 SILC_SCHEDULE_LOCK(schedule);
855 for (i = 0; i < schedule->max_fd; i++)
856 if (schedule->fd_list[i].fd == fd) {
857 schedule->fd_list[i].fd = fd;
858 schedule->fd_list[i].events = iomask;
859 if (i > schedule->last_fd)
860 schedule->last_fd = i;
866 for (i = 0; i < schedule->max_fd; i++)
867 if (schedule->fd_list[i].events == 0) {
868 schedule->fd_list[i].fd = fd;
869 schedule->fd_list[i].events = iomask;
870 if (i > schedule->last_fd)
871 schedule->last_fd = i;
875 SILC_SCHEDULE_UNLOCK(schedule);
878 /* Removes a file descriptor from listen list. */
880 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
884 SILC_SCHEDULE_LOCK(schedule);
886 SILC_LOG_DEBUG(("Unset listen fd %d", fd));
888 for (i = 0; i < schedule->max_fd; i++)
889 if (schedule->fd_list[i].fd == fd) {
890 schedule->fd_list[i].fd = 0;
891 schedule->fd_list[i].events = 0;
892 if (schedule->last_fd == i)
893 schedule->last_fd = schedule->max_fd - 1;
897 SILC_SCHEDULE_UNLOCK(schedule);
900 /* Register a new signal */
902 void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal,
903 SilcTaskCallback callback, void *context)
905 silc_schedule_internal_signal_register(schedule->internal, signal,
909 /* Unregister a new signal */
911 void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal,
912 SilcTaskCallback callback, void *context)
914 silc_schedule_internal_signal_unregister(schedule->internal, signal,
918 /* Call signal indicated by `signal'. */
920 void silc_schedule_signal_call(SilcSchedule schedule, SilcUInt32 signal)
922 /* Mark that signals needs to be delivered later. */
923 silc_schedule_internal_signal_call(schedule->internal, signal);
924 schedule->signal_tasks = TRUE;
927 /* Allocates a newtask task queue into the scheduler */
929 static void silc_task_queue_alloc(SilcTaskQueue *queue)
931 *queue = silc_calloc(1, sizeof(**queue));
932 silc_mutex_alloc(&(*queue)->lock);
935 /* Free's a task queue. */
937 static void silc_task_queue_free(SilcTaskQueue queue)
939 silc_mutex_free(queue->lock);
940 memset(queue, 'F', sizeof(*queue));
944 /* Return task by its fd. */
946 static SilcTask silc_task_find(SilcTaskQueue queue, SilcUInt32 fd)
958 if (queue->task == next->next)
966 /* Adds a non-timeout task into the task queue. This function is used
967 by silc_task_register function. Returns a pointer to the registered
970 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
971 SilcTaskPriority priority)
973 SilcTask task, next, prev;
975 /* Take the first task in the queue */
979 case SILC_TASK_PRI_LOW:
980 /* Lowest priority. The task is added at the end of the list. */
982 newtask->prev = prev;
983 newtask->next = task;
984 prev->next = newtask;
985 task->prev = newtask;
987 case SILC_TASK_PRI_NORMAL:
988 /* Normal priority. The task is added before lower priority tasks
989 but after tasks with higher priority. */
991 while(prev != task) {
992 if (prev->priority > SILC_TASK_PRI_LOW)
997 /* There are only lower priorities in the list, we will
998 sit before them and become the first task in the queue. */
1000 newtask->prev = prev;
1001 newtask->next = task;
1002 task->prev = newtask;
1003 prev->next = newtask;
1005 /* We are now the first task in queue */
1006 queue->task = newtask;
1008 /* Found a spot from the list, add the task to the list. */
1010 newtask->prev = prev;
1011 newtask->next = next;
1012 prev->next = newtask;
1013 next->prev = newtask;
1024 /* Return the timeout task with smallest timeout. */
1026 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
1028 SilcTask prev, task;
1040 if (silc_schedule_task_timeout_compare(&prev->timeout, &task->timeout))
1049 /* Adds a timeout task into the task queue. This function is used by
1050 silc_task_register function. Returns a pointer to the registered
1051 task. Timeout tasks are sorted by their timeout value in ascending
1052 order. The priority matters if there are more than one task with
1055 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
1056 SilcTaskPriority priority)
1058 SilcTask task, prev, next;
1060 /* Take the first task in the queue */
1063 /* Take last task from the list */
1067 case SILC_TASK_PRI_LOW:
1068 /* Lowest priority. The task is added at the end of the list. */
1069 while(prev != task) {
1071 /* If we have longer timeout than with the task head of us
1072 we have found our spot. */
1073 if (silc_schedule_task_timeout_compare(&prev->timeout,
1077 /* If we are equal size of timeout we will be after it. */
1078 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1082 /* We have shorter timeout, compare to next one. */
1085 /* Found a spot from the list, add the task to the list. */
1087 newtask->prev = prev;
1088 newtask->next = next;
1089 prev->next = newtask;
1090 next->prev = newtask;
1093 /* Check if we are going to be the first task in the queue */
1094 if (silc_schedule_task_timeout_compare(&prev->timeout,
1097 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1101 /* We are now the first task in queue */
1102 queue->task = newtask;
1105 case SILC_TASK_PRI_NORMAL:
1106 /* Normal priority. The task is added before lower priority tasks
1107 but after tasks with higher priority. */
1108 while(prev != task) {
1110 /* If we have longer timeout than with the task head of us
1111 we have found our spot. */
1112 if (silc_schedule_task_timeout_compare(&prev->timeout,
1116 /* If we are equal size of timeout, priority kicks in place. */
1117 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1119 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1122 /* We have shorter timeout or higher priority, compare to next one. */
1125 /* Found a spot from the list, add the task to the list. */
1127 newtask->prev = prev;
1128 newtask->next = next;
1129 prev->next = newtask;
1130 next->prev = newtask;
1133 /* Check if we are going to be the first task in the queue */
1134 if (silc_schedule_task_timeout_compare(&prev->timeout,
1137 if (!silc_schedule_task_timeout_compare(&newtask->timeout,
1139 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1142 /* We are now the first task in queue */
1143 queue->task = newtask;
1154 /* Removes (unregisters) a task from particular task queue. This function
1155 is used internally by scheduler. This must be called holding the
1158 static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task)
1160 SilcTask first, old, next;
1162 if (!queue || !task)
1169 first = queue->task;
1171 /* Unregister all tasks in queue */
1172 if (task == SILC_ALL_TASKS) {
1173 SILC_LOG_DEBUG(("Removing all tasks at once"));
1188 SILC_LOG_DEBUG(("Removing task"));
1190 /* Unregister the task */
1194 SilcTask prev, next;
1201 if (prev == old && next == old)
1203 if (queue->task == old)
1204 queue->task = silc_task_get_first(queue, next);
1217 /* Compare two time values. If the first argument is smaller than the
1218 second this function returns TRUE. */
1220 static int silc_schedule_task_timeout_compare(struct timeval *smaller,
1221 struct timeval *bigger)
1223 if ((smaller->tv_sec < bigger->tv_sec) ||
1224 ((smaller->tv_sec == bigger->tv_sec) &&
1225 (smaller->tv_usec < bigger->tv_usec)))
1231 static void silc_task_del_by_fd(SilcTaskQueue queue, SilcUInt32 fd)
1235 silc_mutex_lock(queue->lock);
1238 silc_mutex_unlock(queue->lock);
1246 next->valid = FALSE;
1247 if (queue->task == next->next)
1252 silc_mutex_unlock(queue->lock);
1255 static void silc_task_del_by_callback(SilcTaskQueue queue,
1256 SilcTaskCallback callback)
1260 silc_mutex_lock(queue->lock);
1263 silc_mutex_unlock(queue->lock);
1270 if (next->callback == callback)
1271 next->valid = FALSE;
1272 if (queue->task == next->next)
1277 silc_mutex_unlock(queue->lock);
1280 static void silc_task_del_by_context(SilcTaskQueue queue, void *context)
1284 silc_mutex_lock(queue->lock);
1287 silc_mutex_unlock(queue->lock);
1294 if (next->context == context)
1295 next->valid = FALSE;
1296 if (queue->task == next->next)
1301 silc_mutex_unlock(queue->lock);