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 */
155 void dump_tasks(SilcTaskQueue queue)
157 SilcTask first, prev;
164 fprintf(stderr, "\nqueue->task:\t%p\t%d\n", queue->task,
165 queue->task->timeout.tv_sec);
172 fprintf(stderr, "task :\t%p\t%d\n", prev, prev->timeout.tv_sec);
176 fprintf(stderr, "\n");
180 /* Return the timeout task with smallest timeout. */
182 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
196 if (silc_task_timeout_compare(&prev->timeout, &task->timeout))
205 /* Adds a timeout task into the task queue. This function is used by
206 silc_task_register function. Returns a pointer to the registered
207 task. Timeout tasks are sorted by their timeout value in ascending
208 order. The priority matters if there are more than one task with
211 SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask new,
212 SilcTaskPriority priority)
214 SilcTask task, prev, next;
216 /* Take the first task in the queue */
219 /* Take last task from the list */
223 case SILC_TASK_PRI_LOW:
224 /* Lowest priority. The task is added at the end of the list. */
225 while(prev != task) {
227 /* If we have longer timeout than with the task head of us
228 we have found our spot. */
229 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
232 /* If we are equal size of timeout we will be after it. */
233 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
236 /* We have shorter timeout, compare to next one. */
239 /* Found a spot from the list, add the task to the list. */
247 /* Check if we are going to be the first task in the queue */
248 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
250 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
253 /* We are now the first task in queue */
257 case SILC_TASK_PRI_NORMAL:
258 /* Normal priority. The task is added before lower priority tasks
259 but after tasks with higher priority. */
260 while(prev != task) {
262 /* If we have longer timeout than with the task head of us
263 we have found our spot. */
264 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
267 /* If we are equal size of timeout, priority kicks in place. */
268 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
269 if (prev->priority >= SILC_TASK_PRI_NORMAL)
272 /* We have shorter timeout or higher priority, compare to next one. */
275 /* Found a spot from the list, add the task to the list. */
283 /* Check if we are going to be the first task in the queue */
284 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
286 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
287 if (prev->priority >= SILC_TASK_PRI_NORMAL)
290 /* We are now the first task in queue */
294 case SILC_TASK_PRI_HIGH:
295 /* High priority. The task is added before lower priority tasks
296 but after tasks with higher priority. */
297 while(prev != task) {
299 /* If we have longer timeout than with the task head of us
300 we have found our spot. */
301 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
304 /* If we are equal size of timeout, priority kicks in place. */
305 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
306 if (prev->priority >= SILC_TASK_PRI_HIGH)
309 /* We have shorter timeout or higher priority, compare to next one. */
312 /* Found a spot from the list, add the task to the list. */
320 /* Check if we are going to be the first task in the queue */
321 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
323 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
324 if (prev->priority >= SILC_TASK_PRI_HIGH)
327 /* We are now the first task in queue */
331 case SILC_TASK_PRI_REALTIME:
332 /* Highest priority. The task is added at the head of the list.
333 The last registered task is added to the very head of the list
334 thus we get the LIFO (Last-In-First-Out) order. */
336 while(next != task) {
338 /* If we have shorter timeout than the next task we've found
340 if (silc_task_timeout_compare(&new->timeout, &next->timeout))
343 /* If we are equal size of timeout we will be first. */
344 if (!silc_task_timeout_compare(&next->timeout, &new->timeout))
347 /* We have longer timeout, compare to next one. */
350 /* Found a spot from the list, add the task to the list. */
358 /* Check if we are going to be the first task in the queue */
359 if (silc_task_timeout_compare(&next->timeout, &new->timeout))
362 /* We are now the first task in queue */
377 /* Registers a new task into the task queue. The task becomes valid
378 automatically when it is registered. Returns a pointer to the
381 SilcTask silc_task_register(SilcTaskQueue queue, int fd,
382 SilcTaskCallback cb, void *context,
383 long seconds, long useconds,
384 SilcTaskType type, SilcTaskPriority priority)
389 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d",
390 fd, type, priority));
392 /* If the task is generic task, we check whether this task has already
393 been registered. Generic tasks are registered only once and after that
394 the same task applies to all file descriptors to be registered. */
395 if ((type == SILC_TASK_GENERIC) && queue->task) {
400 if ((task->callback == cb) && (task->context == context)) {
401 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
403 /* Add the fd to be listened, the task found now applies to this
405 silc_schedule_set_listen_fd(fd, (1L << SILC_TASK_READ));
409 if (queue->task == task->next)
416 new = silc_calloc(1, sizeof(*new));
418 new->context = context;
421 new->priority = priority;
422 new->iomask = (1L << SILC_TASK_READ);
426 /* If the task is non-timeout task we have to tell the scheduler that we
427 would like to have these tasks scheduled at some odd distant future. */
428 if (type != SILC_TASK_TIMEOUT)
429 silc_schedule_set_listen_fd(fd, (1L << SILC_TASK_READ));
431 /* Create timeout if marked to be timeout task */
432 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
433 gettimeofday(&new->timeout, NULL);
434 new->timeout.tv_sec += seconds + (useconds / 1000000L);
435 new->timeout.tv_usec += (useconds % 1000000L);
436 if (new->timeout.tv_usec > 999999L) {
437 new->timeout.tv_sec += 1;
438 new->timeout.tv_usec -= 1000000L;
443 /* Is this first task of the queue? */
444 if (queue->task == NULL) {
450 return silc_task_add_timeout(queue, new, priority);
452 return silc_task_add(queue, new, priority);
455 /* Removes (unregisters) a task from particular task queue. This function
456 is used internally by scheduler. One should not call this function
457 to unregister tasks, instead silc_task_unregister_task function
460 int silc_task_remove(SilcTaskQueue queue, SilcTask task)
462 SilcTask first, old, next;
464 if (!queue || !queue->task)
469 /* Unregister all tasks in queue */
470 if (task == SILC_ALL_TASKS) {
471 SILC_LOG_DEBUG(("Removing all tasks at once"));
476 silc_free(next->prev);
485 SILC_LOG_DEBUG(("Removing task"));
487 /* Unregister the task */
498 if (prev == old && next == old)
500 if (queue->task == old)
501 queue->task = silc_task_get_first(queue, next);
502 /*queue->task = next;*/
518 /* Unregisters a task from the task queue. This is the unregister_task
519 function pointer in task queue object. One should use this function
520 to unregister tasks. This function invalidates the task. */
522 void silc_task_unregister(SilcTaskQueue queue, SilcTask task)
525 /* Unregister all tasks */
526 if (task == SILC_ALL_TASKS) {
528 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
530 if (queue->task == NULL)
538 if (queue->task == next->next)
545 SILC_LOG_DEBUG(("Unregistering task"));
547 /* Unregister the specific task */
552 /* Unregister a task by file descriptor. This invalidates the task. */
554 void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd)
558 SILC_LOG_DEBUG(("Unregister task by fd"));
560 if (queue->task == NULL)
568 if (queue->task == next->next)
574 /* Sets the I/O mask for the task. Only one I/O type can be set at a
577 void silc_task_set_iotype(SilcTask task, int type)
579 task->iomask |= (1L << type);
582 /* Resets the I/O mask to the type sent as argument. */
584 void silc_task_reset_iotype(SilcTask task, int type)
586 task->iomask = (1L << type);
589 /* Compare two time values. If the first argument is smaller than the
590 second this function returns TRUE. */
592 int silc_task_timeout_compare(struct timeval *smaller,
593 struct timeval *bigger)
595 if ((smaller->tv_sec < bigger->tv_sec) ||
596 ((smaller->tv_sec == bigger->tv_sec) &&
597 (smaller->tv_usec < bigger->tv_usec)))