Global cosmetic change.
[silc.git] / lib / silccore / silctask.c
1 /*
2
3   silctask.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1998 - 2000 Pekka Riikonen
8
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.
13   
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.
18
19 */
20 /*
21  * $Id$
22  * $Log$
23  * Revision 1.2  2000/07/05 06:06:35  priikone
24  *      Global cosmetic change.
25  *
26  * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
27  *      Imported from internal CVS/Added Log headers.
28  *
29  *
30  */
31
32 #include "silcincludes.h"
33
34 /* Allocates a new task queue into the Silc. If 'valid' is TRUE the
35    queue becomes valid task queue. If it is FALSE scheduler will skip
36    the queue. */
37
38 void silc_task_queue_alloc(SilcTaskQueue *new, int valid)
39 {
40
41   SILC_LOG_DEBUG(("Allocating new task queue"));
42
43   *new = silc_calloc(1, sizeof(**new));
44
45   /* Set the pointers */
46   (*new)->valid = valid;
47   (*new)->task = NULL;
48   (*new)->register_task = silc_task_register;
49   (*new)->unregister_task = silc_task_unregister;
50   (*new)->set_iotype = silc_task_set_iotype;
51   (*new)->reset_iotype = silc_task_reset_iotype;
52 }
53
54 /* Free's a task queue. */
55
56 void silc_task_queue_free(SilcTaskQueue old)
57 {
58   if (old)
59     silc_free(old);
60 }
61
62 /* Adds a non-timeout task into the task queue. This function is used
63    by silc_task_register function. Returns a pointer to the registered 
64    task. */
65
66 SilcTask silc_task_add(SilcTaskQueue queue, SilcTask new, 
67                        SilcTaskPriority priority)
68 {
69   SilcTask task, next, prev;
70
71   /* Take the first task in the queue */
72   task = queue->task;
73
74   switch(priority) {
75   case SILC_TASK_PRI_LOW:
76     /* Lowest priority. The task is added at the end of the list. */
77     prev = task->prev;
78     new->prev = prev;
79     new->next = task;
80     prev->next = new;
81     task->prev = new;
82     break;
83   case SILC_TASK_PRI_NORMAL:
84     /* Normal priority. The task is added before lower priority tasks
85        but after tasks with higher priority. */
86     prev = task->prev;
87     while(prev != task) {
88       if (prev->priority > SILC_TASK_PRI_LOW &&
89           prev->priority <= SILC_TASK_PRI_REALTIME)
90         break;
91       prev = prev->prev;
92     }
93     if (prev == task) {
94       /* There are only lower priorities in the list, we will
95          sit before them and become the first task in the queue. */
96       prev = task->prev;
97       new->prev = prev;
98       new->next = task;
99       task->prev = new;
100       prev->next = new;
101
102       /* We are now the first task in queue */
103       queue->task = new;
104     } else {
105       /* Found a spot from the list, add the task to the list. */
106       next = prev->next;
107       new->prev = prev;
108       new->next = next;
109       prev->next = new;
110       next->prev = new;
111     }
112     break;
113   case SILC_TASK_PRI_HIGH:
114     /* High priority. The task is added before lower priority tasks
115        but after tasks with higher priority. */
116     prev = task->prev;
117     while(prev != task) {
118       if (prev->priority > SILC_TASK_PRI_NORMAL &&
119           prev->priority <= SILC_TASK_PRI_REALTIME)
120         break;
121       prev = prev->prev;
122     }
123     if (prev == task) {
124       /* There are only lower priorities in the list, we will
125          sit before them and become the first task in the queue. */
126       prev = task->prev;
127       new->prev = prev;
128       new->next = task;
129       task->prev = new;
130       prev->next = new;
131
132       /* We are now the first task in queue */
133       queue->task = new;
134     } else {
135       /* Found a spot from the list, add the task to the list. */
136       next = prev->next;
137       new->prev = prev;
138       new->next = next;
139       prev->next = new;
140       next->prev = new;
141     }
142     break;
143   case SILC_TASK_PRI_REALTIME:
144     /* Highest priority. The task is added at the head of the list. 
145        The last registered task is added to the very head of the list
146        thus we get the LIFO (Last-In-First-Out) order. */
147     prev = task->prev;
148     new->prev = prev;
149     new->next = task;
150     prev->next = new;
151     task->prev = new;
152
153     /* We are the first task in the queue */
154     queue->task = new;
155     break;
156   default:
157     silc_free(new);
158     return NULL;
159   }
160
161   return new;
162 }
163
164 /* Adds a timeout task into the task queue. This function is used by
165    silc_task_register function. Returns a pointer to the registered 
166    task. Timeout tasks are sorted by their timeout value in ascending
167    order. The priority matters if there are more than one task with
168    same timeout. */
169
170 SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask new,
171                                SilcTaskPriority priority)
172 {
173   SilcTask task, prev, next;
174
175   /* Take the first task in the queue */
176   task = queue->task;
177
178   /* Take last task from the list */
179   prev = task->prev;
180     
181   switch(priority) {
182   case SILC_TASK_PRI_LOW:
183     /* Lowest priority. The task is added at the end of the list. */
184     while(prev != task) {
185
186       /* If we have longer timeout than with the task head of us
187          we have found our spot. */
188       if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
189         break;
190
191       /* If we are equal size of timeout we will be after it. */
192       if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
193         break;
194
195       /* We have shorter timeout, compare to next one. */
196       prev = prev->prev;
197     }
198     /* Found a spot from the list, add the task to the list. */
199     next = prev->next;
200     new->prev = prev;
201     new->next = next;
202     prev->next = new;
203     next->prev = new;
204     
205     if (prev == task) {
206       /* Check if we are going to be the first task in the queue */
207       if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
208         break;
209       if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
210         break;
211
212       /* We are now the first task in queue */
213       queue->task = new;
214     }
215     break;
216   case SILC_TASK_PRI_NORMAL:
217     /* Normal priority. The task is added before lower priority tasks
218        but after tasks with higher priority. */
219     while(prev != task) {
220
221       /* If we have longer timeout than with the task head of us
222          we have found our spot. */
223       if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
224         break;
225
226       /* If we are equal size of timeout, priority kicks in place. */
227       if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
228         if (prev->priority >= SILC_TASK_PRI_NORMAL)
229           break;
230
231       /* We have shorter timeout or higher priority, compare to next one. */
232       prev = prev->prev;
233     }
234     /* Found a spot from the list, add the task to the list. */
235     next = prev->next;
236     new->prev = prev;
237     new->next = next;
238     prev->next = new;
239     next->prev = new;
240     
241     if (prev == task) {
242       /* Check if we are going to be the first task in the queue */
243       if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
244         break;
245       if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
246         if (prev->priority >= SILC_TASK_PRI_NORMAL)
247           break;
248
249       /* We are now the first task in queue */
250       queue->task = new;
251     }
252     break;
253   case SILC_TASK_PRI_HIGH:
254     /* High priority. The task is added before lower priority tasks
255        but after tasks with higher priority. */
256     while(prev != task) {
257
258       /* If we have longer timeout than with the task head of us
259          we have found our spot. */
260       if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
261         break;
262
263       /* If we are equal size of timeout, priority kicks in place. */
264       if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
265         if (prev->priority >= SILC_TASK_PRI_HIGH)
266           break;
267
268       /* We have shorter timeout or higher priority, compare to next one. */
269       prev = prev->prev;
270     }
271     /* Found a spot from the list, add the task to the list. */
272     next = prev->next;
273     new->prev = prev;
274     new->next = next;
275     prev->next = new;
276     next->prev = new;
277     
278     if (prev == task) {
279       /* Check if we are going to be the first task in the queue */
280       if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
281         break;
282       if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
283         if (prev->priority >= SILC_TASK_PRI_HIGH)
284           break;
285
286       /* We are now the first task in queue */
287       queue->task = new;
288     }
289     break;
290   case SILC_TASK_PRI_REALTIME:
291     /* Highest priority. The task is added at the head of the list. 
292        The last registered task is added to the very head of the list
293        thus we get the LIFO (Last-In-First-Out) order. */
294     next = task->next;
295     while(next != task) {
296
297       /* If we have shorter timeout than the next task we've found
298          our spot. */
299       if (silc_task_timeout_compare(&new->timeout, &next->timeout))
300         break;
301
302       /* If we are equal size of timeout we will be first. */
303       if (!silc_task_timeout_compare(&next->timeout, &new->timeout))
304         break;
305
306       /* We have longer timeout, compare to next one. */
307       next = next->next;
308     }
309     /* Found a spot from the list, add the task to the list. */
310     prev = next->prev;
311     new->next = next;
312     new->prev = prev;
313     prev->next = new;
314     next->prev = new;
315     
316     if (next == task) {
317       /* Check if we are going to be the first task in the queue */
318       if (silc_task_timeout_compare(&next->timeout, &new->timeout))
319         break;
320
321       /* We are now the first task in queue */
322       queue->task = new;
323     }
324   default:
325     silc_free(new);
326     return NULL;
327   }
328
329   return new;
330 }
331
332 /* Registers a new task into the task queue. The task becomes valid
333    automatically when it is registered. Returns a pointer to the 
334    registered task. */
335
336 SilcTask silc_task_register(SilcTaskQueue queue, int fd, 
337                             SilcTaskCallback cb, void *context, 
338                             long seconds, long useconds, 
339                             SilcTaskType type, SilcTaskPriority priority)
340 {
341   SilcTask new;
342   int timeout = 0;
343
344   SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", 
345                   fd, type, priority));
346
347   /* If the task is generic task, we check whether this task has already
348      been registered. Generic tasks are registered only once and after that
349      the same task applies to all file descriptors to be registered. */
350   if ((type == SILC_TASK_GENERIC) && queue->task) {
351     SilcTask task;
352
353     task = queue->task;
354     while(1) {
355       if ((task->callback == cb) && (task->context == context)) {
356         SILC_LOG_DEBUG(("Found matching generic task, using the match"));
357
358         /* Add the fd to be listened, the task found now applies to this
359            fd as well. */
360         silc_schedule_set_listen_fd(fd, (1L << SILC_TASK_READ));
361         return task;
362       }
363
364       if (queue->task == task->next)
365         break;
366       
367       task = task->next;
368     }
369   }
370
371   new = silc_calloc(1, sizeof(*new));
372   new->fd = fd;
373   new->context = context;
374   new->callback = cb;
375   new->valid = TRUE;
376   new->priority = priority;
377   new->iomask = (1L << SILC_TASK_READ);
378   new->next = new;
379   new->prev = new;
380
381   /* If the task is non-timeout task we have to tell the scheduler that we
382      would like to have these tasks scheduled at some odd distant future. */
383   if (type != SILC_TASK_TIMEOUT)
384     silc_schedule_set_listen_fd(fd, (1L << SILC_TASK_READ));
385
386   /* Create timeout if marked to be timeout task */
387   if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
388     gettimeofday(&new->timeout, NULL);
389     new->timeout.tv_sec += seconds + (useconds / 1000000L);
390     new->timeout.tv_usec += (useconds % 1000000L);
391     if (new->timeout.tv_usec > 999999L) {
392       new->timeout.tv_sec += 1;
393       new->timeout.tv_usec -= 1000000L;
394     }
395     timeout = 1;
396   }
397
398   /* Is this first task of the queue? */
399   if (queue->task == NULL) {
400     queue->task = new;
401     return new;
402   }
403
404   if (timeout)
405     return silc_task_add_timeout(queue, new, priority);
406   else
407     return silc_task_add(queue, new, priority);
408 }
409
410 /* Removes (unregisters) a task from particular task queue. This function
411    is used internally by scheduler. One should not call this function
412    to unregister tasks, instead silc_task_unregister_task function
413    should be used. */
414
415 int silc_task_remove(SilcTaskQueue queue, SilcTask task)
416 {
417   SilcTask first, old, next;
418
419   if (!queue || !queue->task)
420     return FALSE;
421
422   first = queue->task;
423
424   /* Unregister all tasks in queue */
425   if (task == SILC_ALL_TASKS) {
426     SILC_LOG_DEBUG(("Removing all tasks at once"));
427     next = first;
428
429     while(1) {
430       next = next->next;
431       silc_free(next->prev);
432       if (next == first)
433         break;
434     }
435
436     queue->task = NULL;
437     return TRUE;
438   }
439
440   SILC_LOG_DEBUG(("Removing task"));
441
442   /* Unregister the task */
443   old = first;
444   while(1) {
445     if (old == task) {
446       SilcTask prev, next;
447
448       prev = old->prev;
449       next = old->next;
450       prev->next = next;
451       next->prev = prev;
452
453       if (prev == old && next == old)
454         queue->task = NULL;
455       if (queue->task == old)
456         queue->task = next;
457
458       silc_free(old);
459       return TRUE;
460     }
461     old = old->next;
462
463     if (old == first)
464       return FALSE;
465   }
466 }
467
468 /* Unregisters a task from the task queue. This is the unregister_task
469    function pointer in task queue object. One should use this function
470    to unregister tasks. This function invalidates the task. */
471
472 void silc_task_unregister(SilcTaskQueue queue, SilcTask task)
473 {
474
475   /* Unregister all tasks */
476   if (task == SILC_ALL_TASKS) {
477     SilcTask next;
478     SILC_LOG_DEBUG(("Unregistering all tasks at once"));
479
480     if (queue->task == NULL)
481       return;
482
483     next = queue->task;
484     
485     while(1) {
486       if (next->valid)
487         next->valid = FALSE;
488       if (queue->task == next->next)
489         break;
490       next = next->next;
491     }
492     return;
493   }
494
495   SILC_LOG_DEBUG(("Unregistering task"));
496
497   /* Unregister the specific task */
498   if (task->valid)
499     task->valid = FALSE;
500 }
501
502 /* Unregister a task by file descriptor. This invalidates the task. */
503
504 void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd)
505 {
506   SilcTask next;
507
508   SILC_LOG_DEBUG(("Unregister task by fd"));
509
510   if (queue->task == NULL)
511     return;
512
513   next = queue->task;
514
515   while(1) {
516     if (next->fd == fd)
517       next->valid = FALSE;
518     if (queue->task == next->next)
519       break;
520     next = next->next;
521   }
522 }
523
524 /* Sets the I/O mask for the task. Only one I/O type can be set at a
525    time. */
526
527 void silc_task_set_iotype(SilcTask task, int type)
528 {
529   task->iomask |= (1L << type);
530 }
531
532 /* Resets the I/O mask to the type sent as argument. */
533
534 void silc_task_reset_iotype(SilcTask task, int type)
535 {
536   task->iomask = (1L << type);
537 }
538
539 /* Compare two time values. If the first argument is smaller than the
540    second this function returns TRUE. */
541
542 int silc_task_timeout_compare(struct timeval *smaller, 
543                               struct timeval *bigger)
544 {
545   if ((smaller->tv_sec < bigger->tv_sec) ||
546       ((smaller->tv_sec == bigger->tv_sec) &&
547        (smaller->tv_usec < bigger->tv_usec)))
548     return TRUE;
549
550   return FALSE;
551 }