Compilation warning fixes.
[crypto.git] / lib / silcutil / silcschedule.c
1 /*
2
3   silcschedule.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1998 - 2006 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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22
23 /************************** Types and definitions ***************************/
24
25 /* Platform specific implementation */
26 extern const SilcScheduleOps schedule_ops;
27
28 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task);
29 static void silc_schedule_dispatch_fd(SilcSchedule schedule);
30 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
31                                            SilcBool dispatch_all);
32
33
34 /************************ Static utility functions **************************/
35
36 /* Fd task hash table destructor */
37
38 static void silc_schedule_fd_destructor(void *key, void *context,
39                                         void *user_context)
40 {
41   silc_free(context);
42 }
43
44 /* Executes file descriptor tasks. Invalid tasks are removed here. */
45
46 static void silc_schedule_dispatch_fd(SilcSchedule schedule)
47 {
48   SilcHashTableList htl;
49   SilcTask t;
50   SilcTaskFd task;
51   SilcUInt32 fd;
52
53   silc_hash_table_list(schedule->fd_queue, &htl);
54   while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) {
55     t = (SilcTask)task;
56
57     if (!t->valid) {
58       silc_schedule_task_remove(schedule, t);
59       continue;
60     }
61     if (!task->events || !task->revents)
62       continue;
63
64     /* Is the task ready for reading */
65     if (task->revents & SILC_TASK_READ) {
66       SILC_SCHEDULE_UNLOCK(schedule);
67       t->callback(schedule, schedule->app_context, SILC_TASK_READ,
68                   task->fd, t->context);
69       SILC_SCHEDULE_LOCK(schedule);
70     }
71
72     /* Is the task ready for writing */
73     if (t->valid && task->revents & SILC_TASK_WRITE) {
74       SILC_SCHEDULE_UNLOCK(schedule);
75       t->callback(schedule, schedule->app_context, SILC_TASK_WRITE,
76                   task->fd, t->context);
77       SILC_SCHEDULE_LOCK(schedule);
78     }
79
80     /* Remove if task was invalidated in the task callback */
81     if (!t->valid)
82       silc_schedule_task_remove(schedule, t);
83   }
84   silc_hash_table_list_reset(&htl);
85 }
86
87 /* Executes all tasks whose timeout has expired. The task is removed from
88    the task queue after the callback function has returned. Also, invalid
89    tasks are removed here. */
90
91 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
92                                            SilcBool dispatch_all)
93 {
94   SilcTask t;
95   SilcTaskTimeout task;
96   struct timeval curtime;
97   int count = 0;
98
99   SILC_LOG_DEBUG(("Running timeout tasks"));
100
101   silc_gettimeofday(&curtime);
102
103   /* First task in the task queue has always the earliest timeout. */
104   silc_list_start(schedule->timeout_queue);
105   while ((task = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
106     t = (SilcTask)task;
107
108     /* Remove invalid task */
109     if (!t->valid) {
110       silc_schedule_task_remove(schedule, t);
111       continue;
112     }
113
114     /* Execute the task if the timeout has expired */
115     if (dispatch_all || silc_compare_timeval(&task->timeout, &curtime)) {
116       t->valid = FALSE;
117       SILC_SCHEDULE_UNLOCK(schedule);
118       t->callback(schedule, schedule->app_context, SILC_TASK_EXPIRE, 0,
119                   t->context);
120       SILC_SCHEDULE_LOCK(schedule);
121
122       /* Remove the expired task */
123       silc_schedule_task_remove(schedule, t);
124
125       /* Balance when we have lots of small timeouts */
126       if ((++count) > 40)
127         break;
128     }
129   }
130 }
131
132 /* Calculates next timeout. This is the timeout value when at earliest some
133    of the timeout tasks expire. If this is in the past, they will be
134    dispatched now. */
135
136 static void silc_schedule_select_timeout(SilcSchedule schedule)
137 {
138   SilcTask t;
139   SilcTaskTimeout task;
140   struct timeval curtime;
141   SilcBool dispatch = TRUE;
142
143   /* Get the current time */
144   silc_gettimeofday(&curtime);
145   schedule->has_timeout = FALSE;
146
147   /* First task in the task queue has always the earliest timeout. */
148   silc_list_start(schedule->timeout_queue);
149   while ((task = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
150     t = (SilcTask)task;
151
152     /* Remove invalid task */
153     if (!t->valid) {
154       silc_schedule_task_remove(schedule, t);
155       continue;
156     }
157
158     /* If the timeout is in past, we will run the task and all other
159        timeout tasks from the past. */
160     if (silc_compare_timeval(&task->timeout, &curtime) && dispatch) {
161       silc_schedule_dispatch_timeout(schedule, FALSE);
162       if (!schedule->valid)
163         return;
164
165       /* Start selecting new timeout again after dispatch */
166       silc_list_start(schedule->timeout_queue);
167       dispatch = FALSE;
168       continue;
169     }
170
171     /* Calculate the next timeout */
172     curtime.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
173     curtime.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
174     if (curtime.tv_sec < 0)
175       curtime.tv_sec = 0;
176
177     /* We wouldn't want to go under zero, check for it. */
178     if (curtime.tv_usec < 0) {
179       curtime.tv_sec -= 1;
180       if (curtime.tv_sec < 0)
181         curtime.tv_sec = 0;
182       curtime.tv_usec += 1000000L;
183     }
184
185     break;
186   }
187
188   /* Save the timeout */
189   if (task) {
190     schedule->timeout = curtime;
191     schedule->has_timeout = TRUE;
192     SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout.tv_sec,
193                     schedule->timeout.tv_usec));
194   }
195 }
196
197 /* Removes task from the scheduler.  This must be called with scheduler
198    locked. */
199
200 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
201 {
202   SilcTaskFd ftask;
203   SilcTaskTimeout ttask;
204
205   if (task == SILC_ALL_TASKS) {
206     SilcTask task;
207     SilcHashTableList htl;
208     SilcUInt32 fd;
209
210     /* Delete from fd queue */
211     silc_hash_table_list(schedule->fd_queue, &htl);
212     while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task))
213       silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd));
214     silc_hash_table_list_reset(&htl);
215
216     /* Delete from timeout queue */
217     silc_list_start(schedule->timeout_queue);
218     while ((task = silc_list_get(schedule->timeout_queue))) {
219       silc_list_del(schedule->timeout_queue, task);
220       silc_free(task);
221     }
222
223     return;
224   }
225
226   /* Delete from timeout queue */
227   if (task->type == 1) {
228     silc_list_start(schedule->timeout_queue);
229     while ((ttask = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
230       if (ttask == (SilcTaskTimeout)task) {
231         silc_list_del(schedule->timeout_queue, ttask);
232
233         /* Put to free list */
234         silc_list_add(schedule->free_tasks, ttask);
235         break;
236       }
237     }
238
239     return;
240   }
241
242   /* Delete from fd queue */
243   ftask = (SilcTaskFd)task;
244   silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
245 }
246
247 /* Timeout freelist garbage collection */
248
249 SILC_TASK_CALLBACK(silc_schedule_timeout_gc)
250 {
251   SilcTaskTimeout t;
252   int c;
253
254   if (!schedule->valid)
255     return;
256
257   SILC_LOG_DEBUG(("Timeout freelist garbage collection"));
258
259   SILC_SCHEDULE_LOCK(schedule);
260
261   if (silc_list_count(schedule->free_tasks) <= 10) {
262     SILC_SCHEDULE_UNLOCK(schedule);
263     silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
264                                    schedule, 3600, 0);
265     return;
266   }
267   if (silc_list_count(schedule->timeout_queue) >
268       silc_list_count(schedule->free_tasks)) {
269     SILC_SCHEDULE_UNLOCK(schedule);
270     silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
271                                    schedule, 3600, 0);
272     return;
273   }
274
275   c = silc_list_count(schedule->free_tasks) / 2;
276   if (c > silc_list_count(schedule->timeout_queue))
277     c = (silc_list_count(schedule->free_tasks) -
278          silc_list_count(schedule->timeout_queue));
279   if (silc_list_count(schedule->free_tasks) - c < 10)
280     c -= (10 - (silc_list_count(schedule->free_tasks) - c));
281
282   SILC_LOG_DEBUG(("Freeing %d unused tasks, leaving %d", c,
283                   silc_list_count(schedule->free_tasks) - c));
284
285   silc_list_start(schedule->free_tasks);
286   while ((t = silc_list_get(schedule->free_tasks)) && c-- > 0) {
287     silc_list_del(schedule->free_tasks, t);
288     silc_free(t);
289   }
290   silc_list_start(schedule->free_tasks);
291
292   SILC_SCHEDULE_UNLOCK(schedule);
293
294   silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
295                                  schedule, 3600, 0);
296 }
297
298 #ifdef SILC_DIST_INPLACE
299 /* Print schedule statistics to stdout */
300
301 void silc_schedule_stats(SilcSchedule schedule)
302 {
303   SilcTaskFd ftask;
304   fprintf(stdout, "Schedule %p statistics:\n\n", schedule);
305   fprintf(stdout, "Num FD tasks         : %lu (%lu bytes allocated)\n",
306           silc_hash_table_count(schedule->fd_queue),
307           sizeof(*ftask) * silc_hash_table_count(schedule->fd_queue));
308   fprintf(stdout, "Num Timeout tasks    : %d (%d bytes allocated)\n",
309           silc_list_count(schedule->timeout_queue),
310           sizeof(struct SilcTaskTimeoutStruct) *
311           silc_list_count(schedule->timeout_queue));
312   fprintf(stdout, "Num Timeout freelist : %d (%d bytes allocated)\n",
313           silc_list_count(schedule->free_tasks),
314           sizeof(struct SilcTaskTimeoutStruct) *
315           silc_list_count(schedule->free_tasks));
316 }
317 #endif /* SILC_DIST_INPLACE */
318
319 /****************************** Public API **********************************/
320
321 /* Initializes the scheduler. This returns the scheduler context that
322    is given as arugment usually to all silc_schedule_* functions.
323    The `max_tasks' indicates the number of maximum tasks that the
324    scheduler can handle. The `app_context' is application specific
325    context that is delivered to task callbacks. */
326
327 SilcSchedule silc_schedule_init(int max_tasks, void *app_context)
328 {
329   SilcSchedule schedule;
330
331   SILC_LOG_DEBUG(("Initializing scheduler"));
332
333   schedule = silc_calloc(1, sizeof(*schedule));
334   if (!schedule)
335     return NULL;
336
337   schedule->fd_queue =
338     silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
339                           silc_schedule_fd_destructor, NULL, TRUE);
340   if (!schedule->fd_queue)
341     return NULL;
342
343   silc_list_init(schedule->timeout_queue, struct SilcTaskTimeoutStruct, next);
344   silc_list_init(schedule->free_tasks, struct SilcTaskTimeoutStruct, next);
345
346   schedule->app_context = app_context;
347   schedule->valid = TRUE;
348   schedule->max_tasks = max_tasks;
349
350   /* Allocate scheduler lock */
351   silc_mutex_alloc(&schedule->lock);
352
353   /* Initialize the platform specific scheduler. */
354   schedule->internal = schedule_ops.init(schedule, app_context);
355
356   /* Timeout freelist garbage collection */
357   silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
358                                  schedule, 3600, 0);
359
360   return schedule;
361 }
362
363 /* Uninitializes the schedule. This is called when the program is ready
364    to end. This removes all tasks and task queues. Returns FALSE if the
365    scheduler could not be uninitialized. This happens when the scheduler
366    is still valid and silc_schedule_stop has not been called. */
367
368 SilcBool silc_schedule_uninit(SilcSchedule schedule)
369 {
370   SilcTask task;
371
372   SILC_LOG_DEBUG(("Uninitializing scheduler"));
373
374   if (schedule->valid == TRUE)
375     return FALSE;
376
377   /* Dispatch all timeouts before going away */
378   SILC_SCHEDULE_LOCK(schedule);
379   silc_schedule_dispatch_timeout(schedule, TRUE);
380   SILC_SCHEDULE_UNLOCK(schedule);
381
382   /* Deliver signals before going away */
383   if (schedule->signal_tasks) {
384     schedule_ops.signals_call(schedule, schedule->internal);
385     schedule->signal_tasks = FALSE;
386   }
387
388   /* Unregister all tasks */
389   silc_schedule_task_del(schedule, SILC_ALL_TASKS);
390   silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
391
392   /* Delete timeout task freelist */
393   silc_list_start(schedule->free_tasks);
394   while ((task = silc_list_get(schedule->free_tasks)))
395     silc_free(task);
396
397   /* Unregister all task queues */
398   silc_hash_table_free(schedule->fd_queue);
399
400   /* Uninit the platform specific scheduler. */
401   schedule_ops.uninit(schedule, schedule->internal);
402
403   silc_mutex_free(schedule->lock);
404   silc_free(schedule);
405
406   return TRUE;
407 }
408
409 /* Stops the schedule even if it is not supposed to be stopped yet.
410    After calling this, one should call silc_schedule_uninit (after the
411    silc_schedule has returned). */
412
413 void silc_schedule_stop(SilcSchedule schedule)
414 {
415   SILC_LOG_DEBUG(("Stopping scheduler"));
416   SILC_SCHEDULE_LOCK(schedule);
417   schedule->valid = FALSE;
418   SILC_SCHEDULE_UNLOCK(schedule);
419 }
420
421 /* Runs the scheduler once and then returns. */
422
423 SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
424 {
425   struct timeval timeout;
426   int ret;
427
428   if (!schedule->is_locked)
429     SILC_SCHEDULE_LOCK(schedule);
430
431   do {
432     SILC_LOG_DEBUG(("In scheduler loop"));
433
434     /* Deliver signals if any has been set to be called */
435     if (schedule->signal_tasks) {
436       SILC_SCHEDULE_UNLOCK(schedule);
437       schedule_ops.signals_call(schedule, schedule->internal);
438       schedule->signal_tasks = FALSE;
439       SILC_SCHEDULE_LOCK(schedule);
440     }
441
442     /* Check if scheduler is valid */
443     if (schedule->valid == FALSE) {
444       SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
445       if (!schedule->is_locked)
446         SILC_SCHEDULE_UNLOCK(schedule);
447       return FALSE;
448     }
449
450     /* Calculate next timeout for silc_select().  This is the timeout value
451        when at earliest some of the timeout tasks expire.  This may dispatch
452        already expired timeouts. */
453     silc_schedule_select_timeout(schedule);
454
455     /* Check if scheduler is valid */
456     if (schedule->valid == FALSE) {
457       SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
458       if (!schedule->is_locked)
459         SILC_SCHEDULE_UNLOCK(schedule);
460       return FALSE;
461     }
462
463     if (timeout_usecs >= 0) {
464       timeout.tv_sec = 0;
465       timeout.tv_usec = timeout_usecs;
466       schedule->timeout = timeout;
467       schedule->has_timeout = TRUE;
468     }
469
470     /* This is the main silc_select(). The program blocks here until some
471        of the selected file descriptors change status or the selected
472        timeout expires. */
473     SILC_LOG_DEBUG(("Select"));
474     ret = schedule_ops.select(schedule, schedule->internal);
475
476     switch (ret) {
477     case 0:
478       /* Timeout */
479       SILC_LOG_DEBUG(("Running timeout tasks"));
480       if (silc_list_count(schedule->timeout_queue))
481         silc_schedule_dispatch_timeout(schedule, FALSE);
482       break;
483     case -1:
484       /* Error */
485       if (errno == EINTR)
486         break;
487       SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
488       break;
489     default:
490       /* There is some data available now */
491       SILC_LOG_DEBUG(("Running fd tasks"));
492       silc_schedule_dispatch_fd(schedule);
493       break;
494     }
495   } while (timeout_usecs == -1);
496
497   if (!schedule->is_locked)
498     SILC_SCHEDULE_UNLOCK(schedule);
499
500   return TRUE;
501 }
502
503 /* The SILC scheduler. This is actually the main routine in SILC programs.
504    When this returns the program is to be ended. Before this function can
505    be called, one must call silc_schedule_init function. */
506
507 void silc_schedule(SilcSchedule schedule)
508 {
509   SILC_LOG_DEBUG(("Running scheduler"));
510
511   if (schedule->valid == FALSE) {
512     SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
513     return;
514   }
515
516   /* Start the scheduler loop */
517   SILC_SCHEDULE_LOCK(schedule);
518   schedule->is_locked = TRUE;
519   silc_schedule_one(schedule, -1);
520   SILC_SCHEDULE_UNLOCK(schedule);
521 }
522
523 /* Wakes up the scheduler. This is used only in multi-threaded
524    environments where threads may add new tasks or remove old tasks
525    from task queues. This is called to wake up the scheduler in the
526    main thread so that it detects the changes in the task queues.
527    If threads support is not compiled in this function has no effect.
528    Implementation of this function is platform specific. */
529
530 void silc_schedule_wakeup(SilcSchedule schedule)
531 {
532 #ifdef SILC_THREADS
533   SILC_LOG_DEBUG(("Wakeup scheduler"));
534   SILC_SCHEDULE_LOCK(schedule);
535   schedule_ops.wakeup(schedule, schedule->internal);
536   SILC_SCHEDULE_UNLOCK(schedule);
537 #endif
538 }
539
540 /* Returns the application specific context that was saved into the
541    scheduler in silc_schedule_init function.  The context is also
542    returned to application in task callback functions, but this function
543    may be used to get it as well if needed. */
544
545 void *silc_schedule_get_context(SilcSchedule schedule)
546 {
547   return schedule->app_context;
548 }
549
550 /* Add new task to the scheduler */
551
552 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
553                                 SilcTaskCallback callback, void *context,
554                                 long seconds, long useconds,
555                                 SilcTaskType type)
556 {
557   SilcTask task = NULL;
558
559   if (!schedule->valid)
560     return NULL;
561
562   SILC_SCHEDULE_LOCK(schedule);
563
564   if (type == SILC_TASK_TIMEOUT) {
565     SilcTaskTimeout tmp, prev, ttask;
566
567     silc_list_start(schedule->free_tasks);
568     ttask = silc_list_get(schedule->free_tasks);
569     if (!ttask) {
570       ttask = silc_calloc(1, sizeof(*ttask));
571       if (!ttask)
572         goto out;
573     }
574     silc_list_del(schedule->free_tasks, ttask);
575
576     ttask->header.type = 1;
577     ttask->header.callback = callback;
578     ttask->header.context = context;
579     ttask->header.valid = TRUE;
580
581     /* Add timeout */
582     silc_gettimeofday(&ttask->timeout);
583     if ((seconds + useconds) > 0) {
584       ttask->timeout.tv_sec += seconds + (useconds / 1000000L);
585       ttask->timeout.tv_usec += (useconds % 1000000L);
586       if (ttask->timeout.tv_usec >= 1000000L) {
587         ttask->timeout.tv_sec += 1;
588         ttask->timeout.tv_usec -= 1000000L;
589       }
590     }
591
592     SILC_LOG_DEBUG(("New timeout task %p: sec=%d, usec=%d", ttask,
593                     seconds, useconds));
594
595     /* Add task to correct spot so that the first task in the list has
596        the earliest timeout. */
597     silc_list_start(schedule->timeout_queue);
598     prev = NULL;
599     while ((tmp = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
600       /* If we have shorter timeout, we have found our spot */
601       if (silc_compare_timeval(&ttask->timeout, &tmp->timeout)) {
602         silc_list_insert(schedule->timeout_queue, prev, ttask);
603         break;
604       }
605       prev = tmp;
606     }
607     if (!tmp)
608       silc_list_add(schedule->timeout_queue, ttask);
609
610     task = (SilcTask)ttask;
611
612   } else if (type == SILC_TASK_FD) {
613     /* Check if fd is already added */
614     if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
615                              NULL, (void **)&task))
616       goto out;
617
618     /* Check max tasks */
619     if (schedule->max_tasks > 0 &&
620         silc_hash_table_count(schedule->fd_queue) >= schedule->max_tasks) {
621       SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task"));
622       goto out;
623     }
624
625     SilcTaskFd ftask = silc_calloc(1, sizeof(*ftask));
626     if (!ftask)
627       goto out;
628
629     SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd));
630
631     ftask->header.type = 0;
632     ftask->header.callback = callback;
633     ftask->header.context = context;
634     ftask->header.valid = TRUE;
635     ftask->events = SILC_TASK_READ;
636     ftask->fd = fd;
637
638     /* Add task */
639     silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask);
640
641     task = (SilcTask)ftask;
642
643   } else if (type == SILC_TASK_SIGNAL) {
644     SILC_SCHEDULE_UNLOCK(schedule);
645     schedule_ops.signal_register(schedule, schedule->internal, (int)fd,
646                                  callback, context);
647     return NULL;
648   }
649
650
651  out:
652   SILC_SCHEDULE_UNLOCK(schedule);
653   return task;
654 }
655
656 /* Invalidates task */
657
658 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
659 {
660   if (task == SILC_ALL_TASKS) {
661     SilcHashTableList htl;
662
663     SILC_LOG_DEBUG(("Unregister all tasks"));
664
665     SILC_SCHEDULE_LOCK(schedule);
666
667     /* Delete from fd queue */
668     silc_hash_table_list(schedule->fd_queue, &htl);
669     while (silc_hash_table_get(&htl, NULL, (void **)&task))
670       task->valid = FALSE;
671     silc_hash_table_list_reset(&htl);
672
673     /* Delete from timeout queue */
674     silc_list_start(schedule->timeout_queue);
675     while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
676            != SILC_LIST_END)
677       task->valid = FALSE;
678
679     SILC_SCHEDULE_UNLOCK(schedule);
680     return;
681   }
682
683   SILC_LOG_DEBUG(("Unregistering task %p", task));
684   SILC_SCHEDULE_LOCK(schedule);
685   task->valid = FALSE;
686   SILC_SCHEDULE_UNLOCK(schedule);
687 }
688
689 /* Invalidate task by fd */
690
691 void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
692 {
693   SilcTask task = NULL;
694
695   SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
696
697   SILC_SCHEDULE_LOCK(schedule);
698
699   /* fd is unique, so there is only one task with this fd in the table */
700   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd), NULL,
701                            (void **)&task))
702     task->valid = FALSE;
703
704   SILC_SCHEDULE_UNLOCK(schedule);
705
706   /* If it is signal, remove it */
707   if (!task)
708     schedule_ops.signal_unregister(schedule, schedule->internal, fd);
709 }
710
711 /* Invalidate task by task callback. */
712
713 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
714                                         SilcTaskCallback callback)
715 {
716   SilcTask task;
717   SilcHashTableList htl;
718
719   SILC_LOG_DEBUG(("Unregister task by callback"));
720
721   SILC_SCHEDULE_LOCK(schedule);
722
723   /* Delete from fd queue */
724   silc_hash_table_list(schedule->fd_queue, &htl);
725   while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
726     if (task->callback == callback)
727       task->valid = FALSE;
728   }
729   silc_hash_table_list_reset(&htl);
730
731   /* Delete from timeout queue */
732   silc_list_start(schedule->timeout_queue);
733   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
734          != SILC_LIST_END) {
735     if (task->callback == callback)
736       task->valid = FALSE;
737   }
738
739   SILC_SCHEDULE_UNLOCK(schedule);
740 }
741
742 /* Invalidate task by context. */
743
744 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
745 {
746   SilcTask task;
747   SilcHashTableList htl;
748
749   SILC_LOG_DEBUG(("Unregister task by context"));
750
751   SILC_SCHEDULE_LOCK(schedule);
752
753   /* Delete from fd queue */
754   silc_hash_table_list(schedule->fd_queue, &htl);
755   while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
756     if (task->context == context)
757       task->valid = FALSE;
758   }
759   silc_hash_table_list_reset(&htl);
760
761   /* Delete from timeout queue */
762   silc_list_start(schedule->timeout_queue);
763   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
764          != SILC_LIST_END) {
765     if (task->context == context)
766       task->valid = FALSE;
767   }
768
769   SILC_SCHEDULE_UNLOCK(schedule);
770 }
771
772 /* Invalidate task by all */
773
774 void silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
775                                    SilcTaskCallback callback, void *context)
776 {
777   SilcTask task;
778
779   SILC_LOG_DEBUG(("Unregister task by fd, callback and context"));
780
781   /* For fd task, callback and context is irrelevant as fd is unique */
782   if (fd)
783     silc_schedule_task_del_by_fd(schedule, fd);
784
785   SILC_SCHEDULE_LOCK(schedule);
786
787   /* Delete from timeout queue */
788   silc_list_start(schedule->timeout_queue);
789   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
790          != SILC_LIST_END) {
791     if (task->callback == callback && task->context == context)
792       task->valid = FALSE;
793   }
794
795   SILC_SCHEDULE_UNLOCK(schedule);
796 }
797
798 /* Sets a file descriptor to be listened by scheduler. One can call this
799    directly if wanted. This can be called multiple times for one file
800    descriptor to set different iomasks. */
801
802 void silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
803                                  SilcTaskEvent mask, SilcBool send_events)
804 {
805   SilcTaskFd task;
806
807   if (!schedule->valid)
808     return;
809
810   SILC_SCHEDULE_LOCK(schedule);
811
812   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
813                            NULL, (void **)&task)) {
814     task->events = mask;
815     if (send_events) {
816       task->revents = mask;
817       silc_schedule_dispatch_fd(schedule);
818     }
819   }
820
821   SILC_SCHEDULE_UNLOCK(schedule);
822 }
823
824 /* Removes a file descriptor from listen list. */
825
826 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
827 {
828   silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
829 }