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.
23 * Revision 1.1 2000/06/27 11:36:55 priikone
29 #include "silcincludes.h"
31 /* Allocates a new task queue into the Silc. If 'valid' is TRUE the
32 queue becomes valid task queue. If it is FALSE scheduler will skip
35 void silc_task_queue_alloc(SilcTaskQueue *new, int valid)
38 SILC_LOG_DEBUG(("Allocating new task queue"));
40 *new = silc_calloc(1, sizeof(**new));
42 SILC_LOG_ERROR(("Could not allocate new task queue object: %s",
47 /* Set the pointers */
48 (*new)->valid = valid;
50 (*new)->register_task = silc_task_register;
51 (*new)->unregister_task = silc_task_unregister;
52 (*new)->set_iotype = silc_task_set_iotype;
53 (*new)->reset_iotype = silc_task_reset_iotype;
56 /* Free's a task queue. */
58 void silc_task_queue_free(SilcTaskQueue old)
64 /* Adds a non-timeout task into the task queue. This function is used
65 by silc_task_register function. Returns a pointer to the registered
68 SilcTask silc_task_add(SilcTaskQueue queue, SilcTask new,
69 SilcTaskPriority priority)
71 SilcTask task, next, prev;
73 /* Take the first task in the queue */
77 case SILC_TASK_PRI_LOW:
78 /* Lowest priority. The task is added at the end of the list. */
85 case SILC_TASK_PRI_NORMAL:
86 /* Normal priority. The task is added before lower priority tasks
87 but after tasks with higher priority. */
90 if (prev->priority > SILC_TASK_PRI_LOW &&
91 prev->priority <= SILC_TASK_PRI_REALTIME)
96 /* There are only lower priorities in the list, we will
97 sit before them and become the first task in the queue. */
104 /* We are now the first task in queue */
107 /* Found a spot from the list, add the task to the list. */
115 case SILC_TASK_PRI_HIGH:
116 /* High priority. The task is added before lower priority tasks
117 but after tasks with higher priority. */
119 while(prev != task) {
120 if (prev->priority > SILC_TASK_PRI_NORMAL &&
121 prev->priority <= SILC_TASK_PRI_REALTIME)
126 /* There are only lower priorities in the list, we will
127 sit before them and become the first task in the queue. */
134 /* We are now the first task in queue */
137 /* Found a spot from the list, add the task to the list. */
145 case SILC_TASK_PRI_REALTIME:
146 /* Highest priority. The task is added at the head of the list.
147 The last registered task is added to the very head of the list
148 thus we get the LIFO (Last-In-First-Out) order. */
155 /* We are the first task in the queue */
166 /* Adds a timeout task into the task queue. This function is used by
167 silc_task_register function. Returns a pointer to the registered
168 task. Timeout tasks are sorted by their timeout value in ascending
169 order. The priority matters if there are more than one task with
172 SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask new,
173 SilcTaskPriority priority)
175 SilcTask task, prev, next;
177 /* Take the first task in the queue */
180 /* Take last task from the list */
184 case SILC_TASK_PRI_LOW:
185 /* Lowest priority. The task is added at the end of the list. */
186 while(prev != task) {
188 /* If we have longer timeout than with the task head of us
189 we have found our spot. */
190 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
193 /* If we are equal size of timeout we will be after it. */
194 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
197 /* We have shorter timeout, compare to next one. */
200 /* Found a spot from the list, add the task to the list. */
208 /* Check if we are going to be the first task in the queue */
209 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
211 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
214 /* We are now the first task in queue */
218 case SILC_TASK_PRI_NORMAL:
219 /* Normal priority. The task is added before lower priority tasks
220 but after tasks with higher priority. */
221 while(prev != task) {
223 /* If we have longer timeout than with the task head of us
224 we have found our spot. */
225 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
228 /* If we are equal size of timeout, priority kicks in place. */
229 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
230 if (prev->priority >= SILC_TASK_PRI_NORMAL)
233 /* We have shorter timeout or higher priority, compare to next one. */
236 /* Found a spot from the list, add the task to the list. */
244 /* Check if we are going to be the first task in the queue */
245 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
247 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
248 if (prev->priority >= SILC_TASK_PRI_NORMAL)
251 /* We are now the first task in queue */
255 case SILC_TASK_PRI_HIGH:
256 /* High priority. The task is added before lower priority tasks
257 but after tasks with higher priority. */
258 while(prev != task) {
260 /* If we have longer timeout than with the task head of us
261 we have found our spot. */
262 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
265 /* If we are equal size of timeout, priority kicks in place. */
266 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
267 if (prev->priority >= SILC_TASK_PRI_HIGH)
270 /* We have shorter timeout or higher priority, compare to next one. */
273 /* Found a spot from the list, add the task to the list. */
281 /* Check if we are going to be the first task in the queue */
282 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
284 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
285 if (prev->priority >= SILC_TASK_PRI_HIGH)
288 /* We are now the first task in queue */
292 case SILC_TASK_PRI_REALTIME:
293 /* Highest priority. The task is added at the head of the list.
294 The last registered task is added to the very head of the list
295 thus we get the LIFO (Last-In-First-Out) order. */
297 while(next != task) {
299 /* If we have shorter timeout than the next task we've found
301 if (silc_task_timeout_compare(&new->timeout, &next->timeout))
304 /* If we are equal size of timeout we will be first. */
305 if (!silc_task_timeout_compare(&next->timeout, &new->timeout))
308 /* We have longer timeout, compare to next one. */
311 /* Found a spot from the list, add the task to the list. */
319 /* Check if we are going to be the first task in the queue */
320 if (silc_task_timeout_compare(&next->timeout, &new->timeout))
323 /* We are now the first task in queue */
334 /* Registers a new task into the task queue. The task becomes valid
335 automatically when it is registered. Returns a pointer to the
338 SilcTask silc_task_register(SilcTaskQueue queue, int fd,
339 SilcTaskCallback cb, void *context,
340 long seconds, long useconds,
341 SilcTaskType type, SilcTaskPriority priority)
346 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d",
347 fd, type, priority));
349 /* If the task is generic task, we check whether this task has already
350 been registered. Generic tasks are registered only once and after that
351 the same task applies to all file descriptors to be registered. */
352 if ((type == SILC_TASK_GENERIC) && queue->task) {
357 if ((task->callback == cb) && (task->context == context)) {
358 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
360 /* Add the fd to be listened, the task found now applies to this
362 silc_schedule_set_listen_fd(fd, (1L << SILC_TASK_READ));
366 if (queue->task == task->next)
373 new = silc_calloc(1, sizeof(*new));
375 SILC_LOG_ERROR(("Could not allocate new task object"));
380 new->context = context;
383 new->priority = priority;
384 new->iomask = (1L << SILC_TASK_READ);
388 /* If the task is non-timeout task we have to tell the scheduler that we
389 would like to have these tasks scheduled at some odd distant future. */
390 if (type != SILC_TASK_TIMEOUT)
391 silc_schedule_set_listen_fd(fd, (1L << SILC_TASK_READ));
393 /* Create timeout if marked to be timeout task */
394 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
395 gettimeofday(&new->timeout, NULL);
396 new->timeout.tv_sec += seconds + (useconds / 1000000L);
397 new->timeout.tv_usec += (useconds % 1000000L);
398 if (new->timeout.tv_usec > 999999L) {
399 new->timeout.tv_sec += 1;
400 new->timeout.tv_usec -= 1000000L;
405 /* Is this first task of the queue? */
406 if (queue->task == NULL) {
412 return silc_task_add_timeout(queue, new, priority);
414 return silc_task_add(queue, new, priority);
417 /* Removes (unregisters) a task from particular task queue. This function
418 is used internally by scheduler. One should not call this function
419 to unregister tasks, instead silc_task_unregister_task function
422 int silc_task_remove(SilcTaskQueue queue, SilcTask task)
424 SilcTask first, old, next;
426 if (!queue || !queue->task)
431 /* Unregister all tasks in queue */
432 if (task == SILC_ALL_TASKS) {
433 SILC_LOG_DEBUG(("Removing all tasks at once"));
438 silc_free(next->prev);
447 SILC_LOG_DEBUG(("Removing task"));
449 /* Unregister the task */
460 if (prev == old && next == old)
462 if (queue->task == old)
475 /* Unregisters a task from the task queue. This is the unregister_task
476 function pointer in task queue object. One should use this function
477 to unregister tasks. This function invalidates the task. */
479 void silc_task_unregister(SilcTaskQueue queue, SilcTask task)
482 /* Unregister all tasks */
483 if (task == SILC_ALL_TASKS) {
485 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
487 if (queue->task == NULL)
495 if (queue->task == next->next)
502 SILC_LOG_DEBUG(("Unregistering task"));
504 /* Unregister the specific task */
509 /* Unregister a task by file descriptor. This invalidates the task. */
511 void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd)
515 SILC_LOG_DEBUG(("Unregister task by fd"));
517 if (queue->task == NULL)
525 if (queue->task == next->next)
531 /* Sets the I/O mask for the task. Only one I/O type can be set at a
534 void silc_task_set_iotype(SilcTask task, int type)
536 task->iomask |= (1L << type);
539 /* Resets the I/O mask to the type sent as argument. */
541 void silc_task_reset_iotype(SilcTask task, int type)
543 task->iomask = (1L << type);
546 /* Compare two time values. If the first argument is smaller than the
547 second this function returns TRUE. */
549 int silc_task_timeout_compare(struct timeval *smaller,
550 struct timeval *bigger)
552 if ((smaller->tv_sec < bigger->tv_sec) ||
553 ((smaller->tv_sec == bigger->tv_sec) &&
554 (smaller->tv_usec < bigger->tv_usec)))