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 /* Routine to compare task timeouts. */
25 int silc_task_timeout_compare(struct timeval *smaller,
26 struct timeval *bigger);
28 /* Allocates a new task queue into the Silc. If 'valid' is TRUE the
29 queue becomes valid task queue. If it is FALSE scheduler will skip
32 void silc_task_queue_alloc(SilcSchedule schedule, SilcTaskQueue *new,
35 SILC_LOG_DEBUG(("Allocating new task queue"));
37 *new = silc_calloc(1, sizeof(**new));
39 /* Set the pointers */
40 (*new)->schedule = schedule;
41 (*new)->valid = valid;
42 silc_mutex_alloc(&(*new)->lock);
45 /* Free's a task queue. */
47 void silc_task_queue_free(SilcTaskQueue queue)
49 silc_mutex_lock(queue->lock);
51 silc_mutex_unlock(queue->lock);
52 silc_mutex_free(queue->lock);
56 /* Wakes up the task queue. This actually wakes up the scheduler of this
57 task queue. This is called in multi-threaded environment to wake up
58 the scheduler after adding or removing tasks from the task queue. */
60 void silc_task_queue_wakeup(SilcTaskQueue queue)
62 silc_schedule_wakeup(queue->schedule);
65 /* Adds a non-timeout task into the task queue. This function is used
66 by silc_task_register function. Returns a pointer to the registered
69 static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask new,
70 SilcTaskPriority priority)
72 SilcTask task, next, prev;
74 /* Take the first task in the queue */
78 case SILC_TASK_PRI_LOW:
79 /* Lowest priority. The task is added at the end of the list. */
86 case SILC_TASK_PRI_NORMAL:
87 /* Normal priority. The task is added before lower priority tasks
88 but after tasks with higher priority. */
91 if (prev->priority > SILC_TASK_PRI_LOW)
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. */
123 /* Return the timeout task with smallest timeout. */
125 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
139 if (silc_task_timeout_compare(&prev->timeout, &task->timeout))
148 /* Adds a timeout task into the task queue. This function is used by
149 silc_task_register function. Returns a pointer to the registered
150 task. Timeout tasks are sorted by their timeout value in ascending
151 order. The priority matters if there are more than one task with
154 static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask new,
155 SilcTaskPriority priority)
157 SilcTask task, prev, next;
159 /* Take the first task in the queue */
162 /* Take last task from the list */
166 case SILC_TASK_PRI_LOW:
167 /* Lowest priority. The task is added at the end of the list. */
168 while(prev != task) {
170 /* If we have longer timeout than with the task head of us
171 we have found our spot. */
172 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
175 /* If we are equal size of timeout we will be after it. */
176 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
179 /* We have shorter timeout, compare to next one. */
182 /* Found a spot from the list, add the task to the list. */
190 /* Check if we are going to be the first task in the queue */
191 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
193 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
196 /* We are now the first task in queue */
200 case SILC_TASK_PRI_NORMAL:
201 /* Normal priority. The task is added before lower priority tasks
202 but after tasks with higher priority. */
203 while(prev != task) {
205 /* If we have longer timeout than with the task head of us
206 we have found our spot. */
207 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
210 /* If we are equal size of timeout, priority kicks in place. */
211 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
212 if (prev->priority >= SILC_TASK_PRI_NORMAL)
215 /* We have shorter timeout or higher priority, compare to next one. */
218 /* Found a spot from the list, add the task to the list. */
226 /* Check if we are going to be the first task in the queue */
227 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
229 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
230 if (prev->priority >= SILC_TASK_PRI_NORMAL)
233 /* We are now the first task in queue */
245 /* Registers a new task to the task queue. Arguments are as follows:
247 SilcTaskQueue queue Queue where the task is to be registered
248 int fd File descriptor
249 SilcTaskCallback cb Callback function to call
250 void *context Context to be passed to callback function
251 long seconds Seconds to timeout
252 long useconds Microseconds to timeout
253 SilcTaskType type Type of the task
254 SilcTaskPriority priority Priority of the task
256 The same function is used to register all types of tasks. The type
257 argument tells what type of the task is. Note that when registering
258 non-timeout tasks one should also pass 0 as timeout as timeout will
259 be ignored anyway. Also, note, that one cannot register timeout task
260 with 0 timeout. There cannot be zero timeouts, passing zero means
261 no timeout is used for the task and SILC_TASK_FD_TASK is used as
262 default task type in this case.
264 One should be careful not to register timeout tasks to the non-timeout
265 task queue, because they will never expire. As one should not register
266 non-timeout tasks to timeout task queue because they will never get
269 There is a one distinct difference between timeout and non-timeout
270 tasks when they are executed. Non-timeout tasks remain on the task
271 queue after execution. Timeout tasks, however, are removed from the
272 task queue after they have expired. It is safe to re-register a task
273 in its own callback function. It is also safe to unregister a task
274 in a callback function.
276 Generic tasks apply to all file descriptors, however, one still must
277 pass the correct file descriptor to the function when registering
280 SilcTask silc_task_register(SilcTaskQueue queue, int fd,
281 SilcTaskCallback cb, void *context,
282 long seconds, long useconds,
283 SilcTaskType type, SilcTaskPriority priority)
288 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d",
289 fd, type, priority));
291 /* If the task is generic task, we check whether this task has already
292 been registered. Generic tasks are registered only once and after that
293 the same task applies to all file descriptors to be registered. */
294 if (type == SILC_TASK_GENERIC) {
295 silc_mutex_lock(queue->lock);
298 SilcTask task = queue->task;
300 if ((task->callback == cb) && (task->context == context)) {
301 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
303 silc_mutex_unlock(queue->lock);
305 /* Add the fd to be listened, the task found now applies to this
307 silc_schedule_set_listen_fd(queue->schedule,
308 fd, (1L << SILC_TASK_READ));
312 if (queue->task == task->next)
319 silc_mutex_unlock(queue->lock);
322 new = silc_calloc(1, sizeof(*new));
324 new->context = context;
327 new->priority = priority;
328 new->iomask = (1L << SILC_TASK_READ);
332 /* Create timeout if marked to be timeout task */
333 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
334 silc_gettimeofday(&new->timeout);
335 new->timeout.tv_sec += seconds + (useconds / 1000000L);
336 new->timeout.tv_usec += (useconds % 1000000L);
337 if (new->timeout.tv_usec > 999999L) {
338 new->timeout.tv_sec += 1;
339 new->timeout.tv_usec -= 1000000L;
344 /* If the task is non-timeout task we have to tell the scheduler that we
345 would like to have these tasks scheduled at some odd distant future. */
346 if (type != SILC_TASK_TIMEOUT)
347 silc_schedule_set_listen_fd(queue->schedule, fd, (1L << SILC_TASK_READ));
349 silc_mutex_lock(queue->lock);
351 /* Is this first task of the queue? */
352 if (queue->task == NULL) {
354 silc_mutex_unlock(queue->lock);
359 new = silc_task_add_timeout(queue, new, priority);
361 new = silc_task_add(queue, new, priority);
363 silc_mutex_unlock(queue->lock);
368 /* Removes (unregisters) a task from particular task queue. This function
369 is used internally by scheduler. This must be called holding the
372 int silc_task_remove(SilcTaskQueue queue, SilcTask task)
374 SilcTask first, old, next;
385 /* Unregister all tasks in queue */
386 if (task == SILC_ALL_TASKS) {
387 SILC_LOG_DEBUG(("Removing all tasks at once"));
392 silc_free(next->prev);
401 SILC_LOG_DEBUG(("Removing task"));
403 /* Unregister the task */
414 if (prev == old && next == old)
416 if (queue->task == old)
417 queue->task = silc_task_get_first(queue, next);
430 /* Unregisters a task already in the queue. Arguments are as follows:
432 SilcTaskQueue queue Queue where from the task is unregistered
433 SilcTask task Task to be unregistered
435 The same function is used to unregister timeout and non-timeout
436 tasks. One can also unregister all tasks from the queue by passing
437 SILC_ALL_TASKS as task to the function. It is safe to unregister
438 a task in a callback function. */
440 void silc_task_unregister(SilcTaskQueue queue, SilcTask task)
443 /* Unregister all tasks */
444 if (task == SILC_ALL_TASKS) {
446 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
448 silc_mutex_lock(queue->lock);
451 silc_mutex_unlock(queue->lock);
460 if (queue->task == next->next)
465 silc_mutex_unlock(queue->lock);
469 SILC_LOG_DEBUG(("Unregistering task"));
471 silc_mutex_lock(queue->lock);
473 /* Unregister the specific task */
477 silc_mutex_unlock(queue->lock);
480 /* Unregister a task by file descriptor. This invalidates the task. */
482 void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd)
486 SILC_LOG_DEBUG(("Unregister task by fd"));
488 silc_mutex_lock(queue->lock);
491 silc_mutex_unlock(queue->lock);
500 if (queue->task == next->next)
505 silc_mutex_unlock(queue->lock);
508 /* Unregister a task by callback function. This invalidates the task. */
510 void silc_task_unregister_by_callback(SilcTaskQueue queue,
511 SilcTaskCallback callback)
515 SILC_LOG_DEBUG(("Unregister task by callback"));
517 silc_mutex_lock(queue->lock);
520 silc_mutex_unlock(queue->lock);
527 if (next->callback == callback)
529 if (queue->task == next->next)
534 silc_mutex_unlock(queue->lock);
537 /* Unregister a task by context. This invalidates the task. */
539 void silc_task_unregister_by_context(SilcTaskQueue queue, void *context)
543 SILC_LOG_DEBUG(("Unregister task by context"));
545 silc_mutex_lock(queue->lock);
548 silc_mutex_unlock(queue->lock);
555 if (next->context == context)
557 if (queue->task == next->next)
562 silc_mutex_unlock(queue->lock);
565 /* Sets the I/O type of the task. The scheduler checks for this value
566 and a task must always have at least one of the I/O types set at
567 all time. When registering new task the type is set by default to
568 SILC_TASK_READ. If the task doesn't perform reading one must reset
569 the value to SILC_TASK_WRITE.
571 The type sent as argumenet is masked into the task. If the tasks
572 I/O mask already includes this type this function has no effect.
573 Only one I/O type can be added at once. If the task must perform
574 both reading and writing one must call this function for value
575 SILC_TASK_WRITE as well. */
577 void silc_task_set_iotype(SilcTask task, int type)
579 task->iomask |= (1L << type);
582 /* Resets the mask to the type sent as argument. Note that this resets
583 the previous values to zero and then adds the type sent as argument.
584 This function can be used to remove one of the types masked earlier
587 void silc_task_reset_iotype(SilcTask task, int type)
589 task->iomask = (1L << type);
592 /* Compare two time values. If the first argument is smaller than the
593 second this function returns TRUE. */
595 int silc_task_timeout_compare(struct timeval *smaller,
596 struct timeval *bigger)
598 if ((smaller->tv_sec < bigger->tv_sec) ||
599 ((smaller->tv_sec == bigger->tv_sec) &&
600 (smaller->tv_usec < bigger->tv_usec)))