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;
40 /* Free's a task queue. */
42 void silc_task_queue_free(SilcTaskQueue old)
48 /* Adds a non-timeout task into the task queue. This function is used
49 by silc_task_register function. Returns a pointer to the registered
52 SilcTask silc_task_add(SilcTaskQueue queue, SilcTask new,
53 SilcTaskPriority priority)
55 SilcTask task, next, prev;
57 /* Take the first task in the queue */
61 case SILC_TASK_PRI_LOW:
62 /* Lowest priority. The task is added at the end of the list. */
69 case SILC_TASK_PRI_NORMAL:
70 /* Normal priority. The task is added before lower priority tasks
71 but after tasks with higher priority. */
74 if (prev->priority > SILC_TASK_PRI_LOW)
79 /* There are only lower priorities in the list, we will
80 sit before them and become the first task in the queue. */
87 /* We are now the first task in queue */
90 /* Found a spot from the list, add the task to the list. */
106 /* Return the timeout task with smallest timeout. */
108 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
122 if (silc_task_timeout_compare(&prev->timeout, &task->timeout))
131 /* Adds a timeout task into the task queue. This function is used by
132 silc_task_register function. Returns a pointer to the registered
133 task. Timeout tasks are sorted by their timeout value in ascending
134 order. The priority matters if there are more than one task with
137 SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask new,
138 SilcTaskPriority priority)
140 SilcTask task, prev, next;
142 /* Take the first task in the queue */
145 /* Take last task from the list */
149 case SILC_TASK_PRI_LOW:
150 /* Lowest priority. The task is added at the end of the list. */
151 while(prev != task) {
153 /* If we have longer timeout than with the task head of us
154 we have found our spot. */
155 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
158 /* If we are equal size of timeout we will be after it. */
159 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
162 /* We have shorter timeout, compare to next one. */
165 /* Found a spot from the list, add the task to the list. */
173 /* Check if we are going to be the first task in the queue */
174 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
176 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
179 /* We are now the first task in queue */
183 case SILC_TASK_PRI_NORMAL:
184 /* Normal priority. The task is added before lower priority tasks
185 but after tasks with higher priority. */
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, priority kicks in place. */
194 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
195 if (prev->priority >= SILC_TASK_PRI_NORMAL)
198 /* We have shorter timeout or higher priority, compare to next one. */
201 /* Found a spot from the list, add the task to the list. */
209 /* Check if we are going to be the first task in the queue */
210 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
212 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
213 if (prev->priority >= SILC_TASK_PRI_NORMAL)
216 /* We are now the first task in queue */
228 /* Registers a new task to the task queue. Arguments are as follows:
230 SilcTaskQueue queue Queue where the task is to be registered
231 int fd File descriptor
232 SilcTaskCallback cb Callback function to call
233 void *context Context to be passed to callback function
234 long seconds Seconds to timeout
235 long useconds Microseconds to timeout
236 SilcTaskType type Type of the task
237 SilcTaskPriority priority Priority of the task
239 The same function is used to register all types of tasks. The type
240 argument tells what type of the task is. Note that when registering
241 non-timeout tasks one should also pass 0 as timeout as timeout will
242 be ignored anyway. Also, note, that one cannot register timeout task
243 with 0 timeout. There cannot be zero timeouts, passing zero means
244 no timeout is used for the task and SILC_TASK_FD_TASK is used as
245 default task type in this case.
247 One should be careful not to register timeout tasks to the non-timeout
248 task queue, because they will never expire. As one should not register
249 non-timeout tasks to timeout task queue because they will never get
252 There is a one distinct difference between timeout and non-timeout
253 tasks when they are executed. Non-timeout tasks remain on the task
254 queue after execution. Timeout tasks, however, are removed from the
255 task queue after they have expired. It is safe to re-register a task
256 in its own callback function. It is also safe to unregister a task
257 in a callback function.
259 Generic tasks apply to all file descriptors, however, one still must
260 pass the correct file descriptor to the function when registering
263 SilcTask silc_task_register(SilcTaskQueue queue, int fd,
264 SilcTaskCallback cb, void *context,
265 long seconds, long useconds,
266 SilcTaskType type, SilcTaskPriority priority)
271 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d",
272 fd, type, priority));
274 /* If the task is generic task, we check whether this task has already
275 been registered. Generic tasks are registered only once and after that
276 the same task applies to all file descriptors to be registered. */
277 if ((type == SILC_TASK_GENERIC) && queue->task) {
282 if ((task->callback == cb) && (task->context == context)) {
283 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
285 /* Add the fd to be listened, the task found now applies to this
287 silc_schedule_set_listen_fd(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(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)))