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"
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(SilcSchedule schedule, SilcTaskQueue *new,
31 SILC_LOG_DEBUG(("Allocating new task queue"));
33 *new = silc_calloc(1, sizeof(**new));
35 /* Set the pointers */
36 (*new)->schedule = schedule;
37 (*new)->valid = valid;
40 /* Free's a task queue. */
42 void silc_task_queue_free(SilcTaskQueue queue)
47 /* Adds a non-timeout task into the task queue. This function is used
48 by silc_task_register function. Returns a pointer to the registered
51 SilcTask silc_task_add(SilcTaskQueue queue, SilcTask new,
52 SilcTaskPriority priority)
54 SilcTask task, next, prev;
56 /* Take the first task in the queue */
60 case SILC_TASK_PRI_LOW:
61 /* Lowest priority. The task is added at the end of the list. */
68 case SILC_TASK_PRI_NORMAL:
69 /* Normal priority. The task is added before lower priority tasks
70 but after tasks with higher priority. */
73 if (prev->priority > SILC_TASK_PRI_LOW)
78 /* There are only lower priorities in the list, we will
79 sit before them and become the first task in the queue. */
86 /* We are now the first task in queue */
89 /* Found a spot from the list, add the task to the list. */
105 /* Return the timeout task with smallest timeout. */
107 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
121 if (silc_task_timeout_compare(&prev->timeout, &task->timeout))
130 /* Adds a timeout task into the task queue. This function is used by
131 silc_task_register function. Returns a pointer to the registered
132 task. Timeout tasks are sorted by their timeout value in ascending
133 order. The priority matters if there are more than one task with
136 SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask new,
137 SilcTaskPriority priority)
139 SilcTask task, prev, next;
141 /* Take the first task in the queue */
144 /* Take last task from the list */
148 case SILC_TASK_PRI_LOW:
149 /* Lowest priority. The task is added at the end of the list. */
150 while(prev != task) {
152 /* If we have longer timeout than with the task head of us
153 we have found our spot. */
154 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
157 /* If we are equal size of timeout we will be after it. */
158 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
161 /* We have shorter timeout, compare to next one. */
164 /* Found a spot from the list, add the task to the list. */
172 /* Check if we are going to be the first task in the queue */
173 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
175 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
178 /* We are now the first task in queue */
182 case SILC_TASK_PRI_NORMAL:
183 /* Normal priority. The task is added before lower priority tasks
184 but after tasks with higher priority. */
185 while(prev != task) {
187 /* If we have longer timeout than with the task head of us
188 we have found our spot. */
189 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
192 /* If we are equal size of timeout, priority kicks in place. */
193 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
194 if (prev->priority >= SILC_TASK_PRI_NORMAL)
197 /* We have shorter timeout or higher priority, 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))
212 if (prev->priority >= SILC_TASK_PRI_NORMAL)
215 /* We are now the first task in queue */
227 /* Registers a new task to the task queue. Arguments are as follows:
229 SilcTaskQueue queue Queue where the task is to be registered
230 int fd File descriptor
231 SilcTaskCallback cb Callback function to call
232 void *context Context to be passed to callback function
233 long seconds Seconds to timeout
234 long useconds Microseconds to timeout
235 SilcTaskType type Type of the task
236 SilcTaskPriority priority Priority of the task
238 The same function is used to register all types of tasks. The type
239 argument tells what type of the task is. Note that when registering
240 non-timeout tasks one should also pass 0 as timeout as timeout will
241 be ignored anyway. Also, note, that one cannot register timeout task
242 with 0 timeout. There cannot be zero timeouts, passing zero means
243 no timeout is used for the task and SILC_TASK_FD_TASK is used as
244 default task type in this case.
246 One should be careful not to register timeout tasks to the non-timeout
247 task queue, because they will never expire. As one should not register
248 non-timeout tasks to timeout task queue because they will never get
251 There is a one distinct difference between timeout and non-timeout
252 tasks when they are executed. Non-timeout tasks remain on the task
253 queue after execution. Timeout tasks, however, are removed from the
254 task queue after they have expired. It is safe to re-register a task
255 in its own callback function. It is also safe to unregister a task
256 in a callback function.
258 Generic tasks apply to all file descriptors, however, one still must
259 pass the correct file descriptor to the function when registering
262 SilcTask silc_task_register(SilcTaskQueue queue, int fd,
263 SilcTaskCallback cb, void *context,
264 long seconds, long useconds,
265 SilcTaskType type, SilcTaskPriority priority)
270 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d",
271 fd, type, priority));
273 /* If the task is generic task, we check whether this task has already
274 been registered. Generic tasks are registered only once and after that
275 the same task applies to all file descriptors to be registered. */
276 if ((type == SILC_TASK_GENERIC) && queue->task) {
281 if ((task->callback == cb) && (task->context == context)) {
282 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
284 /* Add the fd to be listened, the task found now applies to this
286 silc_schedule_set_listen_fd(queue->schedule,
287 fd, (1L << SILC_TASK_READ));
291 if (queue->task == task->next)
298 new = silc_calloc(1, sizeof(*new));
300 new->context = context;
303 new->priority = priority;
304 new->iomask = (1L << SILC_TASK_READ);
308 /* If the task is non-timeout task we have to tell the scheduler that we
309 would like to have these tasks scheduled at some odd distant future. */
310 if (type != SILC_TASK_TIMEOUT)
311 silc_schedule_set_listen_fd(queue->schedule, fd, (1L << SILC_TASK_READ));
313 /* Create timeout if marked to be timeout task */
314 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
315 gettimeofday(&new->timeout, NULL);
316 new->timeout.tv_sec += seconds + (useconds / 1000000L);
317 new->timeout.tv_usec += (useconds % 1000000L);
318 if (new->timeout.tv_usec > 999999L) {
319 new->timeout.tv_sec += 1;
320 new->timeout.tv_usec -= 1000000L;
325 /* Is this first task of the queue? */
326 if (queue->task == NULL) {
332 return silc_task_add_timeout(queue, new, priority);
334 return silc_task_add(queue, new, priority);
337 /* Removes (unregisters) a task from particular task queue. This function
338 is used internally by scheduler. One should not call this function
339 to unregister tasks, instead silc_task_unregister_task function
342 int silc_task_remove(SilcTaskQueue queue, SilcTask task)
344 SilcTask first, old, next;
346 if (!queue || !queue->task)
351 /* Unregister all tasks in queue */
352 if (task == SILC_ALL_TASKS) {
353 SILC_LOG_DEBUG(("Removing all tasks at once"));
358 silc_free(next->prev);
367 SILC_LOG_DEBUG(("Removing task"));
369 /* Unregister the task */
380 if (prev == old && next == old)
382 if (queue->task == old)
383 queue->task = silc_task_get_first(queue, next);
395 /* Unregisters a task already in the queue. Arguments are as follows:
397 SilcTaskQueue queue Queue where from the task is unregistered
398 SilcTask task Task to be unregistered
400 The same function is used to unregister timeout and non-timeout
401 tasks. One can also unregister all tasks from the queue by passing
402 SILC_ALL_TASKS as task to the function. It is safe to unregister
403 a task in a callback function. */
405 void silc_task_unregister(SilcTaskQueue queue, SilcTask task)
408 /* Unregister all tasks */
409 if (task == SILC_ALL_TASKS) {
411 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
413 if (queue->task == NULL)
421 if (queue->task == next->next)
428 SILC_LOG_DEBUG(("Unregistering task"));
430 /* Unregister the specific task */
435 /* Unregister a task by file descriptor. This invalidates the task. */
437 void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd)
441 SILC_LOG_DEBUG(("Unregister task by fd"));
443 if (queue->task == NULL)
451 if (queue->task == next->next)
457 /* Unregister a task by callback function. This invalidates the task. */
459 void silc_task_unregister_by_callback(SilcTaskQueue queue,
460 SilcTaskCallback callback)
464 SILC_LOG_DEBUG(("Unregister task by callback"));
466 if (queue->task == NULL)
472 if (next->callback == callback)
474 if (queue->task == next->next)
480 /* Unregister a task by context. This invalidates the task. */
482 void silc_task_unregister_by_context(SilcTaskQueue queue, void *context)
486 SILC_LOG_DEBUG(("Unregister task by context"));
488 if (queue->task == NULL)
494 if (next->context == context)
496 if (queue->task == next->next)
502 /* Sets the I/O type of the task. The scheduler checks for this value
503 and a task must always have at least one of the I/O types set at
504 all time. When registering new task the type is set by default to
505 SILC_TASK_READ. If the task doesn't perform reading one must reset
506 the value to SILC_TASK_WRITE.
508 The type sent as argumenet is masked into the task. If the tasks
509 I/O mask already includes this type this function has no effect.
510 Only one I/O type can be added at once. If the task must perform
511 both reading and writing one must call this function for value
512 SILC_TASK_WRITE as well. */
514 void silc_task_set_iotype(SilcTask task, int type)
516 task->iomask |= (1L << type);
519 /* Resets the mask to the type sent as argument. Note that this resets
520 the previous values to zero and then adds the type sent as argument.
521 This function can be used to remove one of the types masked earlier
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)))