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_schedule_wakeup(queue->schedule);
61 /* Adds a non-timeout task into the task queue. This function is used
62 by silc_task_register function. Returns a pointer to the registered
65 SilcTask silc_task_add(SilcTaskQueue queue, SilcTask new,
66 SilcTaskPriority priority)
68 SilcTask task, next, prev;
70 /* Take the first task in the queue */
74 case SILC_TASK_PRI_LOW:
75 /* Lowest priority. The task is added at the end of the list. */
82 case SILC_TASK_PRI_NORMAL:
83 /* Normal priority. The task is added before lower priority tasks
84 but after tasks with higher priority. */
87 if (prev->priority > SILC_TASK_PRI_LOW)
92 /* There are only lower priorities in the list, we will
93 sit before them and become the first task in the queue. */
100 /* We are now the first task in queue */
103 /* Found a spot from the list, add the task to the list. */
119 /* Return the timeout task with smallest timeout. */
121 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
135 if (silc_task_timeout_compare(&prev->timeout, &task->timeout))
144 /* Adds a timeout task into the task queue. This function is used by
145 silc_task_register function. Returns a pointer to the registered
146 task. Timeout tasks are sorted by their timeout value in ascending
147 order. The priority matters if there are more than one task with
150 SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask new,
151 SilcTaskPriority priority)
153 SilcTask task, prev, next;
155 /* Take the first task in the queue */
158 /* Take last task from the list */
162 case SILC_TASK_PRI_LOW:
163 /* Lowest priority. The task is added at the end of the list. */
164 while(prev != task) {
166 /* If we have longer timeout than with the task head of us
167 we have found our spot. */
168 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
171 /* If we are equal size of timeout we will be after it. */
172 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
175 /* We have shorter timeout, compare to next one. */
178 /* Found a spot from the list, add the task to the list. */
186 /* Check if we are going to be the first task in the queue */
187 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
189 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
192 /* We are now the first task in queue */
196 case SILC_TASK_PRI_NORMAL:
197 /* Normal priority. The task is added before lower priority tasks
198 but after tasks with higher priority. */
199 while(prev != task) {
201 /* If we have longer timeout than with the task head of us
202 we have found our spot. */
203 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
206 /* If we are equal size of timeout, priority kicks in place. */
207 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
208 if (prev->priority >= SILC_TASK_PRI_NORMAL)
211 /* We have shorter timeout or higher priority, compare to next one. */
214 /* Found a spot from the list, add the task to the list. */
222 /* Check if we are going to be the first task in the queue */
223 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
225 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
226 if (prev->priority >= SILC_TASK_PRI_NORMAL)
229 /* We are now the first task in queue */
241 /* Registers a new task to the task queue. Arguments are as follows:
243 SilcTaskQueue queue Queue where the task is to be registered
244 int fd File descriptor
245 SilcTaskCallback cb Callback function to call
246 void *context Context to be passed to callback function
247 long seconds Seconds to timeout
248 long useconds Microseconds to timeout
249 SilcTaskType type Type of the task
250 SilcTaskPriority priority Priority of the task
252 The same function is used to register all types of tasks. The type
253 argument tells what type of the task is. Note that when registering
254 non-timeout tasks one should also pass 0 as timeout as timeout will
255 be ignored anyway. Also, note, that one cannot register timeout task
256 with 0 timeout. There cannot be zero timeouts, passing zero means
257 no timeout is used for the task and SILC_TASK_FD_TASK is used as
258 default task type in this case.
260 One should be careful not to register timeout tasks to the non-timeout
261 task queue, because they will never expire. As one should not register
262 non-timeout tasks to timeout task queue because they will never get
265 There is a one distinct difference between timeout and non-timeout
266 tasks when they are executed. Non-timeout tasks remain on the task
267 queue after execution. Timeout tasks, however, are removed from the
268 task queue after they have expired. It is safe to re-register a task
269 in its own callback function. It is also safe to unregister a task
270 in a callback function.
272 Generic tasks apply to all file descriptors, however, one still must
273 pass the correct file descriptor to the function when registering
276 SilcTask silc_task_register(SilcTaskQueue queue, int fd,
277 SilcTaskCallback cb, void *context,
278 long seconds, long useconds,
279 SilcTaskType type, SilcTaskPriority priority)
284 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d",
285 fd, type, priority));
287 /* If the task is generic task, we check whether this task has already
288 been registered. Generic tasks are registered only once and after that
289 the same task applies to all file descriptors to be registered. */
290 if (type == SILC_TASK_GENERIC) {
291 silc_mutex_lock(queue->lock);
294 SilcTask task = queue->task;
296 if ((task->callback == cb) && (task->context == context)) {
297 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
299 silc_mutex_unlock(queue->lock);
301 /* Add the fd to be listened, the task found now applies to this
303 silc_schedule_set_listen_fd(queue->schedule,
304 fd, (1L << SILC_TASK_READ));
308 if (queue->task == task->next)
315 silc_mutex_unlock(queue->lock);
318 new = silc_calloc(1, sizeof(*new));
320 new->context = context;
323 new->priority = priority;
324 new->iomask = (1L << SILC_TASK_READ);
328 /* Create timeout if marked to be timeout task */
329 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
330 silc_gettimeofday(&new->timeout);
331 new->timeout.tv_sec += seconds + (useconds / 1000000L);
332 new->timeout.tv_usec += (useconds % 1000000L);
333 if (new->timeout.tv_usec > 999999L) {
334 new->timeout.tv_sec += 1;
335 new->timeout.tv_usec -= 1000000L;
340 /* If the task is non-timeout task we have to tell the scheduler that we
341 would like to have these tasks scheduled at some odd distant future. */
342 if (type != SILC_TASK_TIMEOUT)
343 silc_schedule_set_listen_fd(queue->schedule, fd, (1L << SILC_TASK_READ));
345 silc_mutex_lock(queue->lock);
347 /* Is this first task of the queue? */
348 if (queue->task == NULL) {
350 silc_mutex_unlock(queue->lock);
355 new = silc_task_add_timeout(queue, new, priority);
357 new = silc_task_add(queue, new, priority);
359 silc_mutex_unlock(queue->lock);
364 /* Removes (unregisters) a task from particular task queue. This function
365 is used internally by scheduler. This must be called holding the
368 int silc_task_remove(SilcTaskQueue queue, SilcTask task)
370 SilcTask first, old, next;
381 /* Unregister all tasks in queue */
382 if (task == SILC_ALL_TASKS) {
383 SILC_LOG_DEBUG(("Removing all tasks at once"));
388 silc_free(next->prev);
397 SILC_LOG_DEBUG(("Removing task"));
399 /* Unregister the task */
410 if (prev == old && next == old)
412 if (queue->task == old)
413 queue->task = silc_task_get_first(queue, next);
426 /* Unregisters a task already in the queue. Arguments are as follows:
428 SilcTaskQueue queue Queue where from the task is unregistered
429 SilcTask task Task to be unregistered
431 The same function is used to unregister timeout and non-timeout
432 tasks. One can also unregister all tasks from the queue by passing
433 SILC_ALL_TASKS as task to the function. It is safe to unregister
434 a task in a callback function. */
436 void silc_task_unregister(SilcTaskQueue queue, SilcTask task)
439 /* Unregister all tasks */
440 if (task == SILC_ALL_TASKS) {
442 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
444 silc_mutex_lock(queue->lock);
447 silc_mutex_unlock(queue->lock);
456 if (queue->task == next->next)
461 silc_mutex_unlock(queue->lock);
465 SILC_LOG_DEBUG(("Unregistering task"));
467 silc_mutex_lock(queue->lock);
469 /* Unregister the specific task */
473 silc_mutex_unlock(queue->lock);
476 /* Unregister a task by file descriptor. This invalidates the task. */
478 void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd)
482 SILC_LOG_DEBUG(("Unregister task by fd"));
484 silc_mutex_lock(queue->lock);
487 silc_mutex_unlock(queue->lock);
496 if (queue->task == next->next)
501 silc_mutex_unlock(queue->lock);
504 /* Unregister a task by callback function. This invalidates the task. */
506 void silc_task_unregister_by_callback(SilcTaskQueue queue,
507 SilcTaskCallback callback)
511 SILC_LOG_DEBUG(("Unregister task by callback"));
513 silc_mutex_lock(queue->lock);
516 silc_mutex_unlock(queue->lock);
523 if (next->callback == callback)
525 if (queue->task == next->next)
530 silc_mutex_unlock(queue->lock);
533 /* Unregister a task by context. This invalidates the task. */
535 void silc_task_unregister_by_context(SilcTaskQueue queue, void *context)
539 SILC_LOG_DEBUG(("Unregister task by context"));
541 silc_mutex_lock(queue->lock);
544 silc_mutex_unlock(queue->lock);
551 if (next->context == context)
553 if (queue->task == next->next)
558 silc_mutex_unlock(queue->lock);
561 /* Sets the I/O type of the task. The scheduler checks for this value
562 and a task must always have at least one of the I/O types set at
563 all time. When registering new task the type is set by default to
564 SILC_TASK_READ. If the task doesn't perform reading one must reset
565 the value to SILC_TASK_WRITE.
567 The type sent as argumenet is masked into the task. If the tasks
568 I/O mask already includes this type this function has no effect.
569 Only one I/O type can be added at once. If the task must perform
570 both reading and writing one must call this function for value
571 SILC_TASK_WRITE as well. */
573 void silc_task_set_iotype(SilcTask task, int type)
575 task->iomask |= (1L << type);
578 /* Resets the mask to the type sent as argument. Note that this resets
579 the previous values to zero and then adds the type sent as argument.
580 This function can be used to remove one of the types masked earlier
583 void silc_task_reset_iotype(SilcTask task, int type)
585 task->iomask = (1L << type);
588 /* Compare two time values. If the first argument is smaller than the
589 second this function returns TRUE. */
591 int silc_task_timeout_compare(struct timeval *smaller,
592 struct timeval *bigger)
594 if ((smaller->tv_sec < bigger->tv_sec) ||
595 ((smaller->tv_sec == bigger->tv_sec) &&
596 (smaller->tv_usec < bigger->tv_usec)))