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 /* Adds a non-timeout task into the task queue. This function is used
53 by silc_task_register function. Returns a pointer to the registered
56 SilcTask silc_task_add(SilcTaskQueue queue, SilcTask new,
57 SilcTaskPriority priority)
59 SilcTask task, next, prev;
61 /* Take the first task in the queue */
65 case SILC_TASK_PRI_LOW:
66 /* Lowest priority. The task is added at the end of the list. */
73 case SILC_TASK_PRI_NORMAL:
74 /* Normal priority. The task is added before lower priority tasks
75 but after tasks with higher priority. */
78 if (prev->priority > SILC_TASK_PRI_LOW)
83 /* There are only lower priorities in the list, we will
84 sit before them and become the first task in the queue. */
91 /* We are now the first task in queue */
94 /* Found a spot from the list, add the task to the list. */
110 /* Return the timeout task with smallest timeout. */
112 static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
126 if (silc_task_timeout_compare(&prev->timeout, &task->timeout))
135 /* Adds a timeout task into the task queue. This function is used by
136 silc_task_register function. Returns a pointer to the registered
137 task. Timeout tasks are sorted by their timeout value in ascending
138 order. The priority matters if there are more than one task with
141 SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask new,
142 SilcTaskPriority priority)
144 SilcTask task, prev, next;
146 /* Take the first task in the queue */
149 /* Take last task from the list */
153 case SILC_TASK_PRI_LOW:
154 /* Lowest priority. The task is added at the end of the list. */
155 while(prev != task) {
157 /* If we have longer timeout than with the task head of us
158 we have found our spot. */
159 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
162 /* If we are equal size of timeout we will be after it. */
163 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
166 /* We have shorter timeout, compare to next one. */
169 /* Found a spot from the list, add the task to the list. */
177 /* Check if we are going to be the first task in the queue */
178 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
180 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
183 /* We are now the first task in queue */
187 case SILC_TASK_PRI_NORMAL:
188 /* Normal priority. The task is added before lower priority tasks
189 but after tasks with higher priority. */
190 while(prev != task) {
192 /* If we have longer timeout than with the task head of us
193 we have found our spot. */
194 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
197 /* If we are equal size of timeout, priority kicks in place. */
198 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
199 if (prev->priority >= SILC_TASK_PRI_NORMAL)
202 /* We have shorter timeout or higher priority, compare to next one. */
205 /* Found a spot from the list, add the task to the list. */
213 /* Check if we are going to be the first task in the queue */
214 if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
216 if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
217 if (prev->priority >= SILC_TASK_PRI_NORMAL)
220 /* We are now the first task in queue */
232 /* Registers a new task to the task queue. Arguments are as follows:
234 SilcTaskQueue queue Queue where the task is to be registered
235 int fd File descriptor
236 SilcTaskCallback cb Callback function to call
237 void *context Context to be passed to callback function
238 long seconds Seconds to timeout
239 long useconds Microseconds to timeout
240 SilcTaskType type Type of the task
241 SilcTaskPriority priority Priority of the task
243 The same function is used to register all types of tasks. The type
244 argument tells what type of the task is. Note that when registering
245 non-timeout tasks one should also pass 0 as timeout as timeout will
246 be ignored anyway. Also, note, that one cannot register timeout task
247 with 0 timeout. There cannot be zero timeouts, passing zero means
248 no timeout is used for the task and SILC_TASK_FD_TASK is used as
249 default task type in this case.
251 One should be careful not to register timeout tasks to the non-timeout
252 task queue, because they will never expire. As one should not register
253 non-timeout tasks to timeout task queue because they will never get
256 There is a one distinct difference between timeout and non-timeout
257 tasks when they are executed. Non-timeout tasks remain on the task
258 queue after execution. Timeout tasks, however, are removed from the
259 task queue after they have expired. It is safe to re-register a task
260 in its own callback function. It is also safe to unregister a task
261 in a callback function.
263 Generic tasks apply to all file descriptors, however, one still must
264 pass the correct file descriptor to the function when registering
267 SilcTask silc_task_register(SilcTaskQueue queue, int fd,
268 SilcTaskCallback cb, void *context,
269 long seconds, long useconds,
270 SilcTaskType type, SilcTaskPriority priority)
275 SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d",
276 fd, type, priority));
278 /* If the task is generic task, we check whether this task has already
279 been registered. Generic tasks are registered only once and after that
280 the same task applies to all file descriptors to be registered. */
281 if (type == SILC_TASK_GENERIC) {
282 silc_mutex_lock(queue->lock);
285 SilcTask task = queue->task;
287 if ((task->callback == cb) && (task->context == context)) {
288 SILC_LOG_DEBUG(("Found matching generic task, using the match"));
290 silc_mutex_unlock(queue->lock);
292 /* Add the fd to be listened, the task found now applies to this
294 silc_schedule_set_listen_fd(queue->schedule,
295 fd, (1L << SILC_TASK_READ));
299 if (queue->task == task->next)
306 silc_mutex_unlock(queue->lock);
309 new = silc_calloc(1, sizeof(*new));
311 new->context = context;
314 new->priority = priority;
315 new->iomask = (1L << SILC_TASK_READ);
319 /* Create timeout if marked to be timeout task */
320 if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
321 silc_gettimeofday(&new->timeout);
322 new->timeout.tv_sec += seconds + (useconds / 1000000L);
323 new->timeout.tv_usec += (useconds % 1000000L);
324 if (new->timeout.tv_usec > 999999L) {
325 new->timeout.tv_sec += 1;
326 new->timeout.tv_usec -= 1000000L;
331 /* If the task is non-timeout task we have to tell the scheduler that we
332 would like to have these tasks scheduled at some odd distant future. */
333 if (type != SILC_TASK_TIMEOUT)
334 silc_schedule_set_listen_fd(queue->schedule, fd, (1L << SILC_TASK_READ));
336 silc_mutex_lock(queue->lock);
338 /* Is this first task of the queue? */
339 if (queue->task == NULL) {
341 silc_mutex_unlock(queue->lock);
346 new = silc_task_add_timeout(queue, new, priority);
348 new = silc_task_add(queue, new, priority);
350 silc_mutex_unlock(queue->lock);
355 /* Removes (unregisters) a task from particular task queue. This function
356 is used internally by scheduler. One should not call this function
357 to unregister tasks, instead silc_task_unregister_task function
360 int silc_task_remove(SilcTaskQueue queue, SilcTask task)
362 SilcTask first, old, next;
367 silc_mutex_lock(queue->lock);
370 silc_mutex_unlock(queue->lock);
376 /* Unregister all tasks in queue */
377 if (task == SILC_ALL_TASKS) {
378 SILC_LOG_DEBUG(("Removing all tasks at once"));
383 silc_free(next->prev);
389 silc_mutex_unlock(queue->lock);
393 SILC_LOG_DEBUG(("Removing task"));
395 /* Unregister the task */
406 if (prev == old && next == old)
408 if (queue->task == old)
409 queue->task = silc_task_get_first(queue, next);
412 silc_mutex_unlock(queue->lock);
418 silc_mutex_unlock(queue->lock);
424 /* Unregisters a task already in the queue. Arguments are as follows:
426 SilcTaskQueue queue Queue where from the task is unregistered
427 SilcTask task Task to be unregistered
429 The same function is used to unregister timeout and non-timeout
430 tasks. One can also unregister all tasks from the queue by passing
431 SILC_ALL_TASKS as task to the function. It is safe to unregister
432 a task in a callback function. */
434 void silc_task_unregister(SilcTaskQueue queue, SilcTask task)
437 /* Unregister all tasks */
438 if (task == SILC_ALL_TASKS) {
440 SILC_LOG_DEBUG(("Unregistering all tasks at once"));
442 silc_mutex_lock(queue->lock);
445 silc_mutex_unlock(queue->lock);
454 if (queue->task == next->next)
459 silc_mutex_unlock(queue->lock);
463 SILC_LOG_DEBUG(("Unregistering task"));
465 /* Unregister the specific task */
470 /* Unregister a task by file descriptor. This invalidates the task. */
472 void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd)
476 SILC_LOG_DEBUG(("Unregister task by fd"));
478 silc_mutex_lock(queue->lock);
481 silc_mutex_unlock(queue->lock);
490 if (queue->task == next->next)
495 silc_mutex_unlock(queue->lock);
498 /* Unregister a task by callback function. This invalidates the task. */
500 void silc_task_unregister_by_callback(SilcTaskQueue queue,
501 SilcTaskCallback callback)
505 SILC_LOG_DEBUG(("Unregister task by callback"));
507 silc_mutex_lock(queue->lock);
510 silc_mutex_unlock(queue->lock);
517 if (next->callback == callback)
519 if (queue->task == next->next)
524 silc_mutex_unlock(queue->lock);
527 /* Unregister a task by context. This invalidates the task. */
529 void silc_task_unregister_by_context(SilcTaskQueue queue, void *context)
533 SILC_LOG_DEBUG(("Unregister task by context"));
535 silc_mutex_lock(queue->lock);
538 silc_mutex_unlock(queue->lock);
545 if (next->context == context)
547 if (queue->task == next->next)
552 silc_mutex_unlock(queue->lock);
555 /* Sets the I/O type of the task. The scheduler checks for this value
556 and a task must always have at least one of the I/O types set at
557 all time. When registering new task the type is set by default to
558 SILC_TASK_READ. If the task doesn't perform reading one must reset
559 the value to SILC_TASK_WRITE.
561 The type sent as argumenet is masked into the task. If the tasks
562 I/O mask already includes this type this function has no effect.
563 Only one I/O type can be added at once. If the task must perform
564 both reading and writing one must call this function for value
565 SILC_TASK_WRITE as well. */
567 void silc_task_set_iotype(SilcTask task, int type)
569 task->iomask |= (1L << type);
572 /* Resets the mask to the type sent as argument. Note that this resets
573 the previous values to zero and then adds the type sent as argument.
574 This function can be used to remove one of the types masked earlier
577 void silc_task_reset_iotype(SilcTask task, int type)
579 task->iomask = (1L << type);
582 /* Compare two time values. If the first argument is smaller than the
583 second this function returns TRUE. */
585 int silc_task_timeout_compare(struct timeval *smaller,
586 struct timeval *bigger)
588 if ((smaller->tv_sec < bigger->tv_sec) ||
589 ((smaller->tv_sec == bigger->tv_sec) &&
590 (smaller->tv_usec < bigger->tv_usec)))