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, void *context);
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_schedule_dispatch_timeout(SilcSchedule schedule,
75 static void silc_task_queue_alloc(SilcTaskQueue *queue);
76 static void silc_task_queue_free(SilcTaskQueue queue);
77 static SilcTask silc_task_find(SilcTaskQueue queue, SilcUInt32 fd);
78 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
79 SilcTaskPriority priority);
80 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first);
81 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
82 SilcTaskPriority priority);
83 static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task);
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 void *app_context; /* Application specific context */
212 SilcTaskQueue fd_queue;
213 SilcTaskQueue timeout_queue;
214 SilcTaskQueue generic_queue;
215 SilcScheduleFd fd_list;
218 struct timeval *timeout;
221 SILC_MUTEX_DEFINE(lock);
226 /* Initializes the scheduler. This returns the scheduler context that
227 is given as arugment usually to all silc_schedule_* functions.
228 The `max_tasks' indicates the number of maximum tasks that the
229 scheduler can handle. The `app_context' is application specific
230 context that is delivered to task callbacks. */
232 SilcSchedule silc_schedule_init(int max_tasks, void *app_context)
234 SilcSchedule schedule;
236 SILC_LOG_DEBUG(("Initializing scheduler"));
238 schedule = silc_calloc(1, sizeof(*schedule));
240 /* Allocate three task queues, one for file descriptor based tasks,
241 one for timeout tasks and one for generic tasks. */
242 silc_task_queue_alloc(&schedule->fd_queue);
243 silc_task_queue_alloc(&schedule->timeout_queue);
244 silc_task_queue_alloc(&schedule->generic_queue);
249 /* Initialize the scheduler */
250 schedule->fd_list = silc_calloc(max_tasks, sizeof(*schedule->fd_list));
251 schedule->max_fd = max_tasks;
252 schedule->timeout = NULL;
253 schedule->valid = TRUE;
254 schedule->app_context = app_context;
256 /* Allocate scheduler lock */
257 silc_mutex_alloc(&schedule->lock);
259 /* Initialize the platform specific scheduler. */
260 schedule->internal = silc_schedule_internal_init(schedule, app_context);
265 /* Uninitializes the schedule. This is called when the program is ready
266 to end. This removes all tasks and task queues. Returns FALSE if the
267 scheduler could not be uninitialized. This happens when the scheduler
268 is still valid and silc_schedule_stop has not been called. */
270 bool silc_schedule_uninit(SilcSchedule schedule)
272 SILC_LOG_DEBUG(("Uninitializing scheduler"));
274 if (schedule->valid == TRUE)
277 /* Dispatch all timeouts before going away */
278 silc_mutex_lock(schedule->timeout_queue->lock);
279 silc_schedule_dispatch_timeout(schedule, TRUE);
280 silc_mutex_unlock(schedule->timeout_queue->lock);
282 /* Deliver signals before going away */
283 if (schedule->signal_tasks) {
284 SILC_SCHEDULE_UNLOCK(schedule);
285 silc_schedule_internal_signals_call(schedule->internal, schedule);
286 schedule->signal_tasks = FALSE;
287 SILC_SCHEDULE_LOCK(schedule);
290 /* Unregister all tasks */
291 silc_schedule_task_remove(schedule->fd_queue, SILC_ALL_TASKS);
292 silc_schedule_task_remove(schedule->timeout_queue, SILC_ALL_TASKS);
293 silc_schedule_task_remove(schedule->generic_queue, SILC_ALL_TASKS);
295 /* Unregister all task queues */
296 silc_task_queue_free(schedule->fd_queue);
297 silc_task_queue_free(schedule->timeout_queue);
298 silc_task_queue_free(schedule->generic_queue);
300 silc_free(schedule->fd_list);
302 /* Uninit the platform specific scheduler. */
303 silc_schedule_internal_uninit(schedule->internal);
305 silc_mutex_free(schedule->lock);
311 /* Enlarge the capabilities of the scheduler to handle tasks to `max_tasks'. */
313 bool silc_schedule_reinit(SilcSchedule schedule, int max_tasks)
315 SILC_SCHEDULE_LOCK(schedule);
316 if (schedule->max_fd <= max_tasks)
318 schedule->fd_list = silc_realloc(schedule->fd_list,
319 (sizeof(*schedule->fd_list) * max_tasks));
320 schedule->max_fd = max_tasks;
321 SILC_SCHEDULE_UNLOCK(schedule);
325 /* Stops the schedule even if it is not supposed to be stopped yet.
326 After calling this, one should call silc_schedule_uninit (after the
327 silc_schedule has returned). */
329 void silc_schedule_stop(SilcSchedule schedule)
331 SILC_LOG_DEBUG(("Stopping scheduler"));
332 SILC_SCHEDULE_LOCK(schedule);
333 schedule->valid = FALSE;
334 SILC_SCHEDULE_UNLOCK(schedule);
337 /* Executes nontimeout tasks. It then checks whether any of ther fd tasks
338 was signaled by the silc_select. If some task was not signaled then
339 all generic tasks are executed for that task. The generic tasks are
340 never executed for task that has explicit fd task set. */
341 /* This holds the schedule->lock and the queue locks. */
343 static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule)
346 int i, last_fd = schedule->last_fd;
349 for (i = 0; i <= last_fd; i++) {
350 if (schedule->fd_list[i].events == 0)
353 fd = schedule->fd_list[i].fd;
355 /* First check whether this fd has task in the fd queue */
356 silc_mutex_lock(schedule->fd_queue->lock);
357 task = silc_task_find(schedule->fd_queue, fd);
359 /* If the task was found then execute its callbacks. If not then
360 execute all generic tasks for that fd. */
362 /* Validity of the task is checked always before and after
363 execution beacuse the task might have been unregistered
364 in the callback function, ie. it is not valid anymore. */
366 /* Is the task ready for reading */
367 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
368 silc_mutex_unlock(schedule->fd_queue->lock);
369 SILC_SCHEDULE_UNLOCK(schedule);
370 task->callback(schedule, schedule->app_context,
371 SILC_TASK_READ, task->fd, task->context);
372 SILC_SCHEDULE_LOCK(schedule);
373 silc_mutex_lock(schedule->fd_queue->lock);
376 /* Is the task ready for writing */
377 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
378 silc_mutex_unlock(schedule->fd_queue->lock);
379 SILC_SCHEDULE_UNLOCK(schedule);
380 task->callback(schedule, schedule->app_context,
381 SILC_TASK_WRITE, task->fd, task->context);
382 SILC_SCHEDULE_LOCK(schedule);
383 silc_mutex_lock(schedule->fd_queue->lock);
387 silc_schedule_task_remove(schedule->fd_queue, task);
389 silc_mutex_unlock(schedule->fd_queue->lock);
391 /* Run generic tasks for this fd. */
393 silc_mutex_unlock(schedule->fd_queue->lock);
395 silc_mutex_lock(schedule->generic_queue->lock);
396 if (!schedule->generic_queue->task) {
397 silc_mutex_unlock(schedule->generic_queue->lock);
401 task = schedule->generic_queue->task;
403 /* Validity of the task is checked always before and after
404 execution beacuse the task might have been unregistered
405 in the callback function, ie. it is not valid anymore. */
407 /* Is the task ready for reading */
408 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
409 silc_mutex_unlock(schedule->generic_queue->lock);
410 SILC_SCHEDULE_UNLOCK(schedule);
411 task->callback(schedule, schedule->app_context,
412 SILC_TASK_READ, fd, task->context);
413 SILC_SCHEDULE_LOCK(schedule);
414 silc_mutex_lock(schedule->generic_queue->lock);
417 /* Is the task ready for writing */
418 if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
419 silc_mutex_unlock(schedule->generic_queue->lock);
420 SILC_SCHEDULE_UNLOCK(schedule);
421 task->callback(schedule, schedule->app_context,
422 SILC_TASK_WRITE, fd, task->context);
423 SILC_SCHEDULE_LOCK(schedule);
424 silc_mutex_lock(schedule->generic_queue->lock);
428 /* Invalid (unregistered) tasks are removed from the
430 if (schedule->generic_queue->task == task->next) {
431 silc_schedule_task_remove(schedule->generic_queue, task);
432 silc_mutex_unlock(schedule->generic_queue->lock);
437 silc_schedule_task_remove(schedule->generic_queue, task);
441 /* Break if there isn't more tasks in the queue */
442 if (schedule->generic_queue->task == task->next)
448 silc_mutex_unlock(schedule->generic_queue->lock);
453 /* Executes all tasks whose timeout has expired. The task is removed from
454 the task queue after the callback function has returned. Also, invalid
455 tasks are removed here. We don't have to care about priorities because
456 tasks are already sorted in their priority order at the registration
458 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
460 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
463 SilcTaskQueue queue = schedule->timeout_queue;
465 struct timeval curtime;
467 SILC_LOG_DEBUG(("Running timeout tasks"));
469 silc_gettimeofday(&curtime);
471 queue = schedule->timeout_queue;
472 if (queue && queue->task) {
475 /* Walk thorugh all tasks in the particular task queue and run all
476 the expired tasks. */
478 /* Execute the task if the timeout has expired */
480 silc_compare_timeval(&task->timeout, &curtime)) {
482 silc_mutex_unlock(queue->lock);
483 SILC_SCHEDULE_UNLOCK(schedule);
484 task->callback(schedule, schedule->app_context,
485 SILC_TASK_EXPIRE, task->fd, task->context);
486 SILC_SCHEDULE_LOCK(schedule);
487 silc_mutex_lock(queue->lock);
490 /* Break if there isn't more tasks in the queue */
491 if (queue->task == task->next) {
492 silc_schedule_task_remove(queue, task);
498 /* Remove the task from queue */
499 silc_schedule_task_remove(queue, task->prev);
501 /* The timeout hasn't expired, check for next one */
503 /* Break if there isn't more tasks in the queue */
504 if (queue->task == task->next)
513 /* Calculates next timeout for select(). This is the timeout value
514 when at earliest some of the timeout tasks expire. If this is in the
515 past, they will be run now. */
516 /* This holds the schedule->lock and the schedule->timeout_queue->lock */
518 static void silc_schedule_select_timeout(SilcSchedule schedule)
520 SilcTaskQueue queue = schedule->timeout_queue;
522 struct timeval curtime;
524 /* Get the current time */
525 silc_gettimeofday(&curtime);
526 schedule->timeout = NULL;
528 /* First task in the task queue has always the smallest timeout. */
531 if (task && task->valid == TRUE) {
532 /* If the timeout is in past, we will run the task and all other
533 timeout tasks from the past. */
534 if (silc_compare_timeval(&task->timeout, &curtime)) {
535 silc_schedule_dispatch_timeout(schedule, FALSE);
537 /* The task(s) has expired and doesn't exist on the task queue
538 anymore. We continue with new timeout. */
539 queue = schedule->timeout_queue;
541 if (task == NULL || task->valid == FALSE)
545 /* Calculate the next timeout for select() */
546 queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
547 queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
548 if (queue->timeout.tv_sec < 0)
549 queue->timeout.tv_sec = 0;
551 /* We wouldn't want to go under zero, check for it. */
552 if (queue->timeout.tv_usec < 0) {
553 queue->timeout.tv_sec -= 1;
554 if (queue->timeout.tv_sec < 0)
555 queue->timeout.tv_sec = 0;
556 queue->timeout.tv_usec += 1000000L;
559 /* We've got the timeout value */
562 /* Task is not valid, remove it and try next one. */
563 silc_schedule_task_remove(queue, task);
565 if (queue->task == NULL)
570 /* Save the timeout */
572 schedule->timeout = &queue->timeout;
573 SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout->tv_sec,
574 schedule->timeout->tv_usec));
578 /* Runs the scheduler once and then returns. */
580 bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
582 struct timeval timeout;
585 SILC_LOG_DEBUG(("In scheduler loop"));
587 if (!schedule->is_locked)
588 SILC_SCHEDULE_LOCK(schedule);
590 /* Deliver signals if any has been set to be called */
591 if (schedule->signal_tasks) {
592 SILC_SCHEDULE_UNLOCK(schedule);
593 silc_schedule_internal_signals_call(schedule->internal, schedule);
594 schedule->signal_tasks = FALSE;
595 SILC_SCHEDULE_LOCK(schedule);
598 /* If the task queues aren't initialized or we aren't valid anymore
600 if ((!schedule->fd_queue && !schedule->timeout_queue
601 && !schedule->generic_queue) || schedule->valid == FALSE) {
602 SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
603 if (!schedule->is_locked)
604 SILC_SCHEDULE_UNLOCK(schedule);
608 /* Calculate next timeout for silc_select(). This is the timeout value
609 when at earliest some of the timeout tasks expire. */
610 silc_mutex_lock(schedule->timeout_queue->lock);
611 silc_schedule_select_timeout(schedule);
612 silc_mutex_unlock(schedule->timeout_queue->lock);
614 if (timeout_usecs >= 0) {
616 timeout.tv_usec = timeout_usecs;
617 schedule->timeout = &timeout;
620 SILC_SCHEDULE_UNLOCK(schedule);
622 /* This is the main select(). The program blocks here until some
623 of the selected file descriptors change status or the selected
625 SILC_LOG_DEBUG(("Select"));
626 ret = silc_select(schedule->fd_list, schedule->last_fd + 1,
629 SILC_SCHEDULE_LOCK(schedule);
636 SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
640 silc_mutex_lock(schedule->timeout_queue->lock);
641 silc_schedule_dispatch_timeout(schedule, FALSE);
642 silc_mutex_unlock(schedule->timeout_queue->lock);
645 /* There is some data available now */
646 SILC_LOG_DEBUG(("Running non-timeout tasks"));
647 silc_schedule_dispatch_nontimeout(schedule);
651 if (!schedule->is_locked)
652 SILC_SCHEDULE_UNLOCK(schedule);
657 /* The SILC scheduler. This is actually the main routine in SILC programs.
658 When this returns the program is to be ended. Before this function can
659 be called, one must call silc_schedule_init function. */
661 void silc_schedule(SilcSchedule schedule)
663 SILC_LOG_DEBUG(("Running scheduler"));
665 if (schedule->valid == FALSE) {
666 SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
670 SILC_SCHEDULE_LOCK(schedule);
671 schedule->is_locked = TRUE;
673 /* Start the scheduler loop */
674 while (silc_schedule_one(schedule, -1))
677 SILC_SCHEDULE_UNLOCK(schedule);
680 /* Wakes up the scheduler. This is used only in multi-threaded
681 environments where threads may add new tasks or remove old tasks
682 from task queues. This is called to wake up the scheduler in the
683 main thread so that it detects the changes in the task queues.
684 If threads support is not compiled in this function has no effect.
685 Implementation of this function is platform specific. */
687 void silc_schedule_wakeup(SilcSchedule schedule)
690 SILC_LOG_DEBUG(("Wakeup scheduler"));
691 SILC_SCHEDULE_LOCK(schedule);
692 silc_schedule_internal_wakeup(schedule->internal);
693 SILC_SCHEDULE_UNLOCK(schedule);
697 /* Returns the application specific context that was saved into the
698 scheduler in silc_schedule_init function. The context is also
699 returned to application in task callback functions, but this function
700 may be used to get it as well if needed. */
702 void *silc_schedule_get_context(SilcSchedule schedule)
704 return schedule->app_context;
707 /* Add new task to the scheduler */
709 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
710 SilcTaskCallback callback, void *context,
711 long seconds, long useconds,
713 SilcTaskPriority priority)
719 if (!schedule->valid)
722 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", fd,
725 queue = SILC_SCHEDULE_GET_QUEUE(type);
727 /* If the task is generic task, we check whether this task has already
728 been registered. Generic tasks are registered only once and after that
729 the same task applies to all file descriptors to be registered. */
730 if (type == SILC_TASK_GENERIC) {
731 silc_mutex_lock(queue->lock);
734 SilcTask task = queue->task;
736 if ((task->callback == callback) && (task->context == context)) {
737 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
739 silc_mutex_unlock(queue->lock);
741 /* Add the fd to be listened, the task found now applies to this
743 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ, FALSE);
747 if (queue->task == task->next)
754 silc_mutex_unlock(queue->lock);
757 newtask = silc_calloc(1, sizeof(*newtask));
759 newtask->context = context;
760 newtask->callback = callback;
761 newtask->valid = TRUE;
762 newtask->priority = priority;
763 newtask->type = type;
764 newtask->next = newtask;
765 newtask->prev = newtask;
767 /* Create timeout if marked to be timeout task */
768 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
769 silc_gettimeofday(&newtask->timeout);
770 newtask->timeout.tv_sec += seconds + (useconds / 1000000L);
771 newtask->timeout.tv_usec += (useconds % 1000000L);
772 if (newtask->timeout.tv_usec > 999999L) {
773 newtask->timeout.tv_sec += 1;
774 newtask->timeout.tv_usec -= 1000000L;
779 /* If the task is non-timeout task we have to tell the scheduler that we
780 would like to have these tasks scheduled at some odd distant future. */
781 if (type != SILC_TASK_TIMEOUT)
782 silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ, FALSE);
784 silc_mutex_lock(queue->lock);
786 /* Is this first task of the queue? */
787 if (queue->task == NULL) {
788 queue->task = newtask;
789 silc_mutex_unlock(queue->lock);
794 newtask = silc_task_add_timeout(queue, newtask, priority);
796 newtask = silc_task_add(queue, newtask, priority);
798 silc_mutex_unlock(queue->lock);
803 /* Removes a task from the scheduler */
805 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
807 SilcTaskQueue queue = SILC_SCHEDULE_GET_QUEUE(task->type);
809 /* Unregister all tasks */
810 if (task == SILC_ALL_TASKS) {
812 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
814 silc_mutex_lock(queue->lock);
817 silc_mutex_unlock(queue->lock);
826 if (queue->task == next->next)
831 silc_mutex_unlock(queue->lock);
835 SILC_LOG_DEBUG(("Unregistering task"));
837 silc_mutex_lock(queue->lock);
839 /* Unregister the specific task */
843 silc_mutex_unlock(queue->lock);
846 /* Remove task by fd */
848 void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
850 SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
852 silc_task_del_by_fd(schedule->timeout_queue, fd);
853 silc_task_del_by_fd(schedule->fd_queue, fd);
856 /* Remove task by task callback. */
858 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
859 SilcTaskCallback callback)
861 SILC_LOG_DEBUG(("Unregister task by callback"));
863 silc_task_del_by_callback(schedule->timeout_queue, callback);
864 silc_task_del_by_callback(schedule->fd_queue, callback);
865 silc_task_del_by_callback(schedule->generic_queue, callback);
868 /* Remove task by context. */
870 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
872 SILC_LOG_DEBUG(("Unregister task by context"));
874 silc_task_del_by_context(schedule->timeout_queue, context);
875 silc_task_del_by_context(schedule->fd_queue, context);
876 silc_task_del_by_context(schedule->generic_queue, context);
879 /* Sets a file descriptor to be listened by select() in scheduler. One can
880 call this directly if wanted. This can be called multiple times for
881 one file descriptor to set different iomasks. */
883 void silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
884 SilcTaskEvent mask, bool send_events)
889 if (!schedule->valid)
892 SILC_SCHEDULE_LOCK(schedule);
894 for (i = 0; i < schedule->max_fd; i++)
895 if (schedule->fd_list[i].fd == fd) {
896 schedule->fd_list[i].fd = fd;
897 schedule->fd_list[i].events = mask;
898 if (i > schedule->last_fd)
899 schedule->last_fd = i;
902 schedule->fd_list[i].revents = mask;
903 silc_schedule_dispatch_nontimeout(schedule);
909 for (i = 0; i < schedule->max_fd; i++)
910 if (schedule->fd_list[i].events == 0) {
911 schedule->fd_list[i].fd = fd;
912 schedule->fd_list[i].events = mask;
913 if (i > schedule->last_fd)
914 schedule->last_fd = i;
916 schedule->fd_list[i].revents = mask;
917 silc_schedule_dispatch_nontimeout(schedule);
922 SILC_SCHEDULE_UNLOCK(schedule);
925 /* Removes a file descriptor from listen list. */
927 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
931 SILC_SCHEDULE_LOCK(schedule);
933 SILC_LOG_DEBUG(("Unset listen fd %d", fd));
935 for (i = 0; i < schedule->max_fd; i++)
936 if (schedule->fd_list[i].fd == fd) {
937 schedule->fd_list[i].fd = 0;
938 schedule->fd_list[i].events = 0;
939 if (schedule->last_fd == i)
940 schedule->last_fd = schedule->max_fd - 1;
944 SILC_SCHEDULE_UNLOCK(schedule);
947 /* Register a new signal */
949 void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal,
950 SilcTaskCallback callback, void *context)
952 silc_schedule_internal_signal_register(schedule->internal, signal,
956 /* Unregister a new signal */
958 void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal,
959 SilcTaskCallback callback, void *context)
961 silc_schedule_internal_signal_unregister(schedule->internal, signal,
965 /* Call signal indicated by `signal'. */
967 void silc_schedule_signal_call(SilcSchedule schedule, SilcUInt32 signal)
969 /* Mark that signals needs to be delivered later. */
970 silc_schedule_internal_signal_call(schedule->internal, signal);
971 schedule->signal_tasks = TRUE;
974 /* Allocates a newtask task queue into the scheduler */
976 static void silc_task_queue_alloc(SilcTaskQueue *queue)
978 *queue = silc_calloc(1, sizeof(**queue));
979 silc_mutex_alloc(&(*queue)->lock);
982 /* Free's a task queue. */
984 static void silc_task_queue_free(SilcTaskQueue queue)
986 silc_mutex_free(queue->lock);
987 memset(queue, 'F', sizeof(*queue));
991 /* Return task by its fd. */
993 static SilcTask silc_task_find(SilcTaskQueue queue, SilcUInt32 fd)
1005 if (queue->task == next->next)
1013 /* Adds a non-timeout task into the task queue. This function is used
1014 by silc_task_register function. Returns a pointer to the registered
1017 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask,
1018 SilcTaskPriority priority)
1020 SilcTask task, next, prev;
1022 /* Take the first task in the queue */
1026 case SILC_TASK_PRI_LOW:
1027 /* Lowest priority. The task is added at the end of the list. */
1029 newtask->prev = prev;
1030 newtask->next = task;
1031 prev->next = newtask;
1032 task->prev = newtask;
1034 case SILC_TASK_PRI_NORMAL:
1035 /* Normal priority. The task is added before lower priority tasks
1036 but after tasks with higher priority. */
1038 while(prev != task) {
1039 if (prev->priority > SILC_TASK_PRI_LOW)
1044 /* There are only lower priorities in the list, we will
1045 sit before them and become the first task in the queue. */
1047 newtask->prev = prev;
1048 newtask->next = task;
1049 task->prev = newtask;
1050 prev->next = newtask;
1052 /* We are now the first task in queue */
1053 queue->task = newtask;
1055 /* Found a spot from the list, add the task to the list. */
1057 newtask->prev = prev;
1058 newtask->next = next;
1059 prev->next = newtask;
1060 next->prev = newtask;
1071 /* Return the timeout task with smallest timeout. */
1073 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
1075 SilcTask prev, task;
1087 if (silc_compare_timeval(&prev->timeout, &task->timeout))
1096 /* Adds a timeout task into the task queue. This function is used by
1097 silc_task_register function. Returns a pointer to the registered
1098 task. Timeout tasks are sorted by their timeout value in ascending
1099 order. The priority matters if there are more than one task with
1102 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
1103 SilcTaskPriority priority)
1105 SilcTask task, prev, next;
1107 /* Take the first task in the queue */
1110 /* Take last task from the list */
1114 case SILC_TASK_PRI_LOW:
1115 /* Lowest priority. The task is added at the end of the list. */
1116 while(prev != task) {
1118 /* If we have longer timeout than with the task head of us
1119 we have found our spot. */
1120 if (silc_compare_timeval(&prev->timeout, &newtask->timeout))
1123 /* If we are equal size of timeout we will be after it. */
1124 if (!silc_compare_timeval(&newtask->timeout, &prev->timeout))
1127 /* We have shorter timeout, compare to next one. */
1130 /* Found a spot from the list, add the task to the list. */
1132 newtask->prev = prev;
1133 newtask->next = next;
1134 prev->next = newtask;
1135 next->prev = newtask;
1138 /* Check if we are going to be the first task in the queue */
1139 if (silc_compare_timeval(&prev->timeout, &newtask->timeout))
1141 if (!silc_compare_timeval(&newtask->timeout, &prev->timeout))
1144 /* We are now the first task in queue */
1145 queue->task = newtask;
1148 case SILC_TASK_PRI_NORMAL:
1149 /* Normal priority. The task is added before lower priority tasks
1150 but after tasks with higher priority. */
1151 while(prev != task) {
1153 /* If we have longer timeout than with the task head of us
1154 we have found our spot. */
1155 if (silc_compare_timeval(&prev->timeout, &newtask->timeout))
1158 /* If we are equal size of timeout, priority kicks in place. */
1159 if (!silc_compare_timeval(&newtask->timeout, &prev->timeout))
1160 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1163 /* We have shorter timeout or higher priority, compare to next one. */
1166 /* Found a spot from the list, add the task to the list. */
1168 newtask->prev = prev;
1169 newtask->next = next;
1170 prev->next = newtask;
1171 next->prev = newtask;
1174 /* Check if we are going to be the first task in the queue */
1175 if (silc_compare_timeval(&prev->timeout, &newtask->timeout))
1177 if (!silc_compare_timeval(&newtask->timeout, &prev->timeout))
1178 if (prev->priority >= SILC_TASK_PRI_NORMAL)
1181 /* We are now the first task in queue */
1182 queue->task = newtask;
1193 /* Removes (unregisters) a task from particular task queue. This function
1194 is used internally by scheduler. This must be called holding the
1197 static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task)
1199 SilcTask first, old, next;
1201 if (!queue || !task)
1208 first = queue->task;
1210 /* Unregister all tasks in queue */
1211 if (task == SILC_ALL_TASKS) {
1212 SILC_LOG_DEBUG(("Removing all tasks at once"));
1227 SILC_LOG_DEBUG(("Removing task"));
1229 /* Unregister the task */
1233 SilcTask prev, next;
1240 if (prev == old && next == old)
1242 if (queue->task == old)
1243 queue->task = silc_task_get_first(queue, next);
1256 static void silc_task_del_by_fd(SilcTaskQueue queue, SilcUInt32 fd)
1260 silc_mutex_lock(queue->lock);
1263 silc_mutex_unlock(queue->lock);
1271 next->valid = FALSE;
1272 if (queue->task == next->next)
1277 silc_mutex_unlock(queue->lock);
1280 static void silc_task_del_by_callback(SilcTaskQueue queue,
1281 SilcTaskCallback callback)
1285 silc_mutex_lock(queue->lock);
1288 silc_mutex_unlock(queue->lock);
1295 if (next->callback == callback)
1296 next->valid = FALSE;
1297 if (queue->task == next->next)
1302 silc_mutex_unlock(queue->lock);
1305 static void silc_task_del_by_context(SilcTaskQueue queue, void *context)
1309 silc_mutex_lock(queue->lock);
1312 silc_mutex_unlock(queue->lock);
1319 if (next->context == context)
1320 next->valid = FALSE;
1321 if (queue->task == next->next)
1326 silc_mutex_unlock(queue->lock);