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;
38 silc_mutex_alloc(&(*new)->lock);
41 /* Free's a task queue. */
43 void silc_task_queue_free(SilcTaskQueue queue)
45 silc_mutex_lock(queue->lock);
47 silc_mutex_unlock(queue->lock);
48 silc_mutex_free(queue->lock);
52 /* Wakes up the task queue. This actually wakes up the scheduler of this
53 task queue. This is called in multi-threaded environment to wake up
54 the scheduler after adding or removing tasks from the task queue. */
56 void silc_task_queue_wakeup(SilcTaskQueue queue)
58 silc_mutex_lock(queue->lock);
59 silc_schedule_wakeup(queue->schedule);
60 silc_mutex_unlock(queue->lock);
63 /* Adds a non-timeout task into the task queue. This function is used
64 by silc_task_register function. Returns a pointer to the registered
67 SilcTask silc_task_add(SilcTaskQueue queue, SilcTask new,
68 SilcTaskPriority priority)
70 SilcTask task, next, prev;
72 /* Take the first task in the queue */
76 case SILC_TASK_PRI_LOW:
77 /* Lowest priority. The task is added at the end of the list. */
84 case SILC_TASK_PRI_NORMAL:
85 /* Normal priority. The task is added before lower priority tasks
86 but after tasks with higher priority. */
89 if (prev->priority > SILC_TASK_PRI_LOW)
94 /* There are only lower priorities in the list, we will
95 sit before them and become the first task in the queue. */
102 /* We are now the first task in queue */
105 /* Found a spot from the list, add the task to the list. */
121 /* Return the timeout task with smallest timeout. */
123 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
137 if (silc_task_timeout_compare(&prev->timeout, &task->timeout))
146 /* Adds a timeout task into the task queue. This function is used by
147 silc_task_register function. Returns a pointer to the registered
148 task. Timeout tasks are sorted by their timeout value in ascending
149 order. The priority matters if there are more than one task with
152 SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask new,
153 SilcTaskPriority priority)
155 SilcTask task, prev, next;
157 /* Take the first task in the queue */
160 /* Take last task from the list */
164 case SILC_TASK_PRI_LOW:
165 /* Lowest priority. The task is added at the end of the list. */
166 while(prev != task) {
168 /* If we have longer timeout than with the task head of us
169 we have found our spot. */
170 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
173 /* If we are equal size of timeout we will be after it. */
174 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
177 /* We have shorter timeout, compare to next one. */
180 /* Found a spot from the list, add the task to the list. */
188 /* Check if we are going to be the first task in the queue */
189 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
191 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
194 /* We are now the first task in queue */
198 case SILC_TASK_PRI_NORMAL:
199 /* Normal priority. The task is added before lower priority tasks
200 but after tasks with higher priority. */
201 while(prev != task) {
203 /* If we have longer timeout than with the task head of us
204 we have found our spot. */
205 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
208 /* If we are equal size of timeout, priority kicks in place. */
209 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
210 if (prev->priority >= SILC_TASK_PRI_NORMAL)
213 /* We have shorter timeout or higher priority, compare to next one. */
216 /* Found a spot from the list, add the task to the list. */
224 /* Check if we are going to be the first task in the queue */
225 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
227 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
228 if (prev->priority >= SILC_TASK_PRI_NORMAL)
231 /* We are now the first task in queue */
243 /* Registers a new task to the task queue. Arguments are as follows:
245 SilcTaskQueue queue Queue where the task is to be registered
246 int fd File descriptor
247 SilcTaskCallback cb Callback function to call
248 void *context Context to be passed to callback function
249 long seconds Seconds to timeout
250 long useconds Microseconds to timeout
251 SilcTaskType type Type of the task
252 SilcTaskPriority priority Priority of the task
254 The same function is used to register all types of tasks. The type
255 argument tells what type of the task is. Note that when registering
256 non-timeout tasks one should also pass 0 as timeout as timeout will
257 be ignored anyway. Also, note, that one cannot register timeout task
258 with 0 timeout. There cannot be zero timeouts, passing zero means
259 no timeout is used for the task and SILC_TASK_FD_TASK is used as
260 default task type in this case.
262 One should be careful not to register timeout tasks to the non-timeout
263 task queue, because they will never expire. As one should not register
264 non-timeout tasks to timeout task queue because they will never get
267 There is a one distinct difference between timeout and non-timeout
268 tasks when they are executed. Non-timeout tasks remain on the task
269 queue after execution. Timeout tasks, however, are removed from the
270 task queue after they have expired. It is safe to re-register a task
271 in its own callback function. It is also safe to unregister a task
272 in a callback function.
274 Generic tasks apply to all file descriptors, however, one still must
275 pass the correct file descriptor to the function when registering
278 SilcTask silc_task_register(SilcTaskQueue queue, int fd,
279 SilcTaskCallback cb, void *context,
280 long seconds, long useconds,
281 SilcTaskType type, SilcTaskPriority priority)
286 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d",
287 fd, type, priority));
289 /* If the task is generic task, we check whether this task has already
290 been registered. Generic tasks are registered only once and after that
291 the same task applies to all file descriptors to be registered. */
292 if (type == SILC_TASK_GENERIC) {
293 silc_mutex_lock(queue->lock);
296 SilcTask task = queue->task;
298 if ((task->callback == cb) && (task->context == context)) {
299 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
301 silc_mutex_unlock(queue->lock);
303 /* Add the fd to be listened, the task found now applies to this
305 silc_schedule_set_listen_fd(queue->schedule,
306 fd, (1L << SILC_TASK_READ));
310 if (queue->task == task->next)
317 silc_mutex_unlock(queue->lock);
320 new = silc_calloc(1, sizeof(*new));
322 new->context = context;
325 new->priority = priority;
326 new->iomask = (1L << SILC_TASK_READ);
330 /* Create timeout if marked to be timeout task */
331 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
332 silc_gettimeofday(&new->timeout);
333 new->timeout.tv_sec += seconds + (useconds / 1000000L);
334 new->timeout.tv_usec += (useconds % 1000000L);
335 if (new->timeout.tv_usec > 999999L) {
336 new->timeout.tv_sec += 1;
337 new->timeout.tv_usec -= 1000000L;
342 /* If the task is non-timeout task we have to tell the scheduler that we
343 would like to have these tasks scheduled at some odd distant future. */
344 if (type != SILC_TASK_TIMEOUT)
345 silc_schedule_set_listen_fd(queue->schedule, fd, (1L << SILC_TASK_READ));
347 silc_mutex_lock(queue->lock);
349 /* Is this first task of the queue? */
350 if (queue->task == NULL) {
352 silc_mutex_unlock(queue->lock);
357 new = silc_task_add_timeout(queue, new, priority);
359 new = silc_task_add(queue, new, priority);
361 silc_mutex_unlock(queue->lock);
366 /* Removes (unregisters) a task from particular task queue. This function
367 is used internally by scheduler. This must be called holding the
370 int silc_task_remove(SilcTaskQueue queue, SilcTask task)
372 SilcTask first, old, next;
383 /* Unregister all tasks in queue */
384 if (task == SILC_ALL_TASKS) {
385 SILC_LOG_DEBUG(("Removing all tasks at once"));
390 silc_free(next->prev);
399 SILC_LOG_DEBUG(("Removing task"));
401 /* Unregister the task */
412 if (prev == old && next == old)
414 if (queue->task == old)
415 queue->task = silc_task_get_first(queue, next);
428 /* Unregisters a task already in the queue. Arguments are as follows:
430 SilcTaskQueue queue Queue where from the task is unregistered
431 SilcTask task Task to be unregistered
433 The same function is used to unregister timeout and non-timeout
434 tasks. One can also unregister all tasks from the queue by passing
435 SILC_ALL_TASKS as task to the function. It is safe to unregister
436 a task in a callback function. */
438 void silc_task_unregister(SilcTaskQueue queue, SilcTask task)
441 /* Unregister all tasks */
442 if (task == SILC_ALL_TASKS) {
444 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
446 silc_mutex_lock(queue->lock);
449 silc_mutex_unlock(queue->lock);
458 if (queue->task == next->next)
463 silc_mutex_unlock(queue->lock);
467 SILC_LOG_DEBUG(("Unregistering task"));
469 silc_mutex_lock(queue->lock);
471 /* Unregister the specific task */
475 silc_mutex_unlock(queue->lock);
478 /* Unregister a task by file descriptor. This invalidates the task. */
480 void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd)
484 SILC_LOG_DEBUG(("Unregister task by fd"));
486 silc_mutex_lock(queue->lock);
489 silc_mutex_unlock(queue->lock);
498 if (queue->task == next->next)
503 silc_mutex_unlock(queue->lock);
506 /* Unregister a task by callback function. This invalidates the task. */
508 void silc_task_unregister_by_callback(SilcTaskQueue queue,
509 SilcTaskCallback callback)
513 SILC_LOG_DEBUG(("Unregister task by callback"));
515 silc_mutex_lock(queue->lock);
518 silc_mutex_unlock(queue->lock);
525 if (next->callback == callback)
527 if (queue->task == next->next)
532 silc_mutex_unlock(queue->lock);
535 /* Unregister a task by context. This invalidates the task. */
537 void silc_task_unregister_by_context(SilcTaskQueue queue, void *context)
541 SILC_LOG_DEBUG(("Unregister task by context"));
543 silc_mutex_lock(queue->lock);
546 silc_mutex_unlock(queue->lock);
553 if (next->context == context)
555 if (queue->task == next->next)
560 silc_mutex_unlock(queue->lock);
563 /* Sets the I/O type of the task. The scheduler checks for this value
564 and a task must always have at least one of the I/O types set at
565 all time. When registering new task the type is set by default to
566 SILC_TASK_READ. If the task doesn't perform reading one must reset
567 the value to SILC_TASK_WRITE.
569 The type sent as argumenet is masked into the task. If the tasks
570 I/O mask already includes this type this function has no effect.
571 Only one I/O type can be added at once. If the task must perform
572 both reading and writing one must call this function for value
573 SILC_TASK_WRITE as well. */
575 void silc_task_set_iotype(SilcTask task, int type)
577 task->iomask |= (1L << type);
580 /* Resets the mask to the type sent as argument. Note that this resets
581 the previous values to zero and then adds the type sent as argument.
582 This function can be used to remove one of the types masked earlier
585 void silc_task_reset_iotype(SilcTask task, int type)
587 task->iomask = (1L << type);
590 /* Compare two time values. If the first argument is smaller than the
591 second this function returns TRUE. */
593 int silc_task_timeout_compare(struct timeval *smaller,
594 struct timeval *bigger)
596 if ((smaller->tv_sec < bigger->tv_sec) ||
597 ((smaller->tv_sec == bigger->tv_sec) &&
598 (smaller->tv_usec < bigger->tv_usec)))