5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1998 - 2000 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"
24 /* Allocates a new task queue into the Silc. If 'valid' is TRUE the
25 queue becomes valid task queue. If it is FALSE scheduler will skip
28 void silc_task_queue_alloc(SilcTaskQueue *new, int valid)
31 SILC_LOG_DEBUG(("Allocating new task queue"));
33 *new = silc_calloc(1, sizeof(**new));
35 /* Set the pointers */
36 (*new)->valid = valid;
38 (*new)->register_task = silc_task_register;
39 (*new)->unregister_task = silc_task_unregister;
40 (*new)->set_iotype = silc_task_set_iotype;
41 (*new)->reset_iotype = silc_task_reset_iotype;
44 /* Free's a task queue. */
46 void silc_task_queue_free(SilcTaskQueue old)
52 /* Adds a non-timeout task into the task queue. This function is used
53 by silc_task_register function. Returns a pointer to the registered
56 SilcTask silc_task_add(SilcTaskQueue queue, SilcTask new,
57 SilcTaskPriority priority)
59 SilcTask task, next, prev;
61 /* Take the first task in the queue */
65 case SILC_TASK_PRI_LOW:
66 /* Lowest priority. The task is added at the end of the list. */
73 case SILC_TASK_PRI_NORMAL:
74 /* Normal priority. The task is added before lower priority tasks
75 but after tasks with higher priority. */
78 if (prev->priority > SILC_TASK_PRI_LOW &&
79 prev->priority <= SILC_TASK_PRI_REALTIME)
84 /* There are only lower priorities in the list, we will
85 sit before them and become the first task in the queue. */
92 /* We are now the first task in queue */
95 /* Found a spot from the list, add the task to the list. */
103 case SILC_TASK_PRI_HIGH:
104 /* High priority. The task is added before lower priority tasks
105 but after tasks with higher priority. */
107 while(prev != task) {
108 if (prev->priority > SILC_TASK_PRI_NORMAL &&
109 prev->priority <= SILC_TASK_PRI_REALTIME)
114 /* There are only lower priorities in the list, we will
115 sit before them and become the first task in the queue. */
122 /* We are now the first task in queue */
125 /* Found a spot from the list, add the task to the list. */
133 case SILC_TASK_PRI_REALTIME:
134 /* Highest priority. The task is added at the head of the list.
135 The last registered task is added to the very head of the list
136 thus we get the LIFO (Last-In-First-Out) order. */
143 /* We are the first task in the queue */
154 /* Adds a timeout task into the task queue. This function is used by
155 silc_task_register function. Returns a pointer to the registered
156 task. Timeout tasks are sorted by their timeout value in ascending
157 order. The priority matters if there are more than one task with
160 SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask new,
161 SilcTaskPriority priority)
163 SilcTask task, prev, next;
165 /* Take the first task in the queue */
168 /* Take last task from the list */
172 case SILC_TASK_PRI_LOW:
173 /* Lowest priority. The task is added at the end of the list. */
174 while(prev != task) {
176 /* If we have longer timeout than with the task head of us
177 we have found our spot. */
178 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
181 /* If we are equal size of timeout we will be after it. */
182 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
185 /* We have shorter timeout, compare to next one. */
188 /* Found a spot from the list, add the task to the list. */
196 /* Check if we are going to be the first task in the queue */
197 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
199 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
202 /* We are now the first task in queue */
206 case SILC_TASK_PRI_NORMAL:
207 /* Normal priority. The task is added before lower priority tasks
208 but after tasks with higher priority. */
209 while(prev != task) {
211 /* If we have longer timeout than with the task head of us
212 we have found our spot. */
213 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
216 /* If we are equal size of timeout, priority kicks in place. */
217 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
218 if (prev->priority >= SILC_TASK_PRI_NORMAL)
221 /* We have shorter timeout or higher priority, compare to next one. */
224 /* Found a spot from the list, add the task to the list. */
232 /* Check if we are going to be the first task in the queue */
233 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
235 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
236 if (prev->priority >= SILC_TASK_PRI_NORMAL)
239 /* We are now the first task in queue */
243 case SILC_TASK_PRI_HIGH:
244 /* High priority. The task is added before lower priority tasks
245 but after tasks with higher priority. */
246 while(prev != task) {
248 /* If we have longer timeout than with the task head of us
249 we have found our spot. */
250 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
253 /* If we are equal size of timeout, priority kicks in place. */
254 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
255 if (prev->priority >= SILC_TASK_PRI_HIGH)
258 /* We have shorter timeout or higher priority, compare to next one. */
261 /* Found a spot from the list, add the task to the list. */
269 /* Check if we are going to be the first task in the queue */
270 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
272 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
273 if (prev->priority >= SILC_TASK_PRI_HIGH)
276 /* We are now the first task in queue */
280 case SILC_TASK_PRI_REALTIME:
281 /* Highest priority. The task is added at the head of the list.
282 The last registered task is added to the very head of the list
283 thus we get the LIFO (Last-In-First-Out) order. */
285 while(next != task) {
287 /* If we have shorter timeout than the next task we've found
289 if (silc_task_timeout_compare(&new->timeout, &next->timeout))
292 /* If we are equal size of timeout we will be first. */
293 if (!silc_task_timeout_compare(&next->timeout, &new->timeout))
296 /* We have longer timeout, compare to next one. */
299 /* Found a spot from the list, add the task to the list. */
307 /* Check if we are going to be the first task in the queue */
308 if (silc_task_timeout_compare(&next->timeout, &new->timeout))
311 /* We are now the first task in queue */
322 /* Registers a new task into the task queue. The task becomes valid
323 automatically when it is registered. Returns a pointer to the
326 SilcTask silc_task_register(SilcTaskQueue queue, int fd,
327 SilcTaskCallback cb, void *context,
328 long seconds, long useconds,
329 SilcTaskType type, SilcTaskPriority priority)
334 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d",
335 fd, type, priority));
337 /* If the task is generic task, we check whether this task has already
338 been registered. Generic tasks are registered only once and after that
339 the same task applies to all file descriptors to be registered. */
340 if ((type == SILC_TASK_GENERIC) && queue->task) {
345 if ((task->callback == cb) && (task->context == context)) {
346 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
348 /* Add the fd to be listened, the task found now applies to this
350 silc_schedule_set_listen_fd(fd, (1L << SILC_TASK_READ));
354 if (queue->task == task->next)
361 new = silc_calloc(1, sizeof(*new));
363 new->context = context;
366 new->priority = priority;
367 new->iomask = (1L << SILC_TASK_READ);
371 /* If the task is non-timeout task we have to tell the scheduler that we
372 would like to have these tasks scheduled at some odd distant future. */
373 if (type != SILC_TASK_TIMEOUT)
374 silc_schedule_set_listen_fd(fd, (1L << SILC_TASK_READ));
376 /* Create timeout if marked to be timeout task */
377 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
378 gettimeofday(&new->timeout, NULL);
379 new->timeout.tv_sec += seconds + (useconds / 1000000L);
380 new->timeout.tv_usec += (useconds % 1000000L);
381 if (new->timeout.tv_usec > 999999L) {
382 new->timeout.tv_sec += 1;
383 new->timeout.tv_usec -= 1000000L;
388 /* Is this first task of the queue? */
389 if (queue->task == NULL) {
395 return silc_task_add_timeout(queue, new, priority);
397 return silc_task_add(queue, new, priority);
400 /* Removes (unregisters) a task from particular task queue. This function
401 is used internally by scheduler. One should not call this function
402 to unregister tasks, instead silc_task_unregister_task function
405 int silc_task_remove(SilcTaskQueue queue, SilcTask task)
407 SilcTask first, old, next;
409 if (!queue || !queue->task)
414 /* Unregister all tasks in queue */
415 if (task == SILC_ALL_TASKS) {
416 SILC_LOG_DEBUG(("Removing all tasks at once"));
421 silc_free(next->prev);
430 SILC_LOG_DEBUG(("Removing task"));
432 /* Unregister the task */
443 if (prev == old && next == old)
445 if (queue->task == old)
458 /* Unregisters a task from the task queue. This is the unregister_task
459 function pointer in task queue object. One should use this function
460 to unregister tasks. This function invalidates the task. */
462 void silc_task_unregister(SilcTaskQueue queue, SilcTask task)
465 /* Unregister all tasks */
466 if (task == SILC_ALL_TASKS) {
468 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
470 if (queue->task == NULL)
478 if (queue->task == next->next)
485 SILC_LOG_DEBUG(("Unregistering task"));
487 /* Unregister the specific task */
492 /* Unregister a task by file descriptor. This invalidates the task. */
494 void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd)
498 SILC_LOG_DEBUG(("Unregister task by fd"));
500 if (queue->task == NULL)
508 if (queue->task == next->next)
514 /* Sets the I/O mask for the task. Only one I/O type can be set at a
517 void silc_task_set_iotype(SilcTask task, int type)
519 task->iomask |= (1L << type);
522 /* Resets the I/O mask to the type sent as argument. */
524 void silc_task_reset_iotype(SilcTask task, int type)
526 task->iomask = (1L << type);
529 /* Compare two time values. If the first argument is smaller than the
530 second this function returns TRUE. */
532 int silc_task_timeout_compare(struct timeval *smaller,
533 struct timeval *bigger)
535 if ((smaller->tv_sec < bigger->tv_sec) ||
536 ((smaller->tv_sec == bigger->tv_sec) &&
537 (smaller->tv_usec < bigger->tv_usec)))