Comment 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) > 50)
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         if (silc_list_count(schedule->free_tasks) == 1)
236           silc_list_start(schedule->free_tasks);
237         break;
238       }
239     }
240
241     return;
242   }
243
244   /* Delete from fd queue */
245   ftask = (SilcTaskFd)task;
246   silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
247 }
248
249 #ifdef SILC_DIST_INPLACE
250 /* Print schedule statistics to stdout */
251
252 void silc_schedule_stats(SilcSchedule schedule)
253 {
254   SilcTaskFd ftask;
255   fprintf(stdout, "Schedule %p statistics:\n\n", schedule);
256   fprintf(stdout, "Num FD tasks         : %lu (%lu bytes allocated)\n",
257           silc_hash_table_count(schedule->fd_queue),
258           sizeof(*ftask) * silc_hash_table_count(schedule->fd_queue));
259   fprintf(stdout, "Num Timeout tasks    : %d (%d bytes allocated)\n",
260           silc_list_count(schedule->timeout_queue),
261           sizeof(struct SilcTaskTimeoutStruct) *
262           silc_list_count(schedule->timeout_queue));
263   fprintf(stdout, "Num Timeout freelist : %d (%d bytes allocated)\n",
264           silc_list_count(schedule->free_tasks),
265           sizeof(struct SilcTaskTimeoutStruct) *
266           silc_list_count(schedule->free_tasks));
267 }
268 #endif /* SILC_DIST_INPLACE */
269
270 /****************************** Public API **********************************/
271
272 /* Initializes the scheduler. This returns the scheduler context that
273    is given as arugment usually to all silc_schedule_* functions.
274    The `max_tasks' indicates the number of maximum tasks that the
275    scheduler can handle. The `app_context' is application specific
276    context that is delivered to task callbacks. */
277
278 SilcSchedule silc_schedule_init(int max_tasks, void *app_context)
279 {
280   SilcSchedule schedule;
281
282   SILC_LOG_DEBUG(("Initializing scheduler"));
283
284   schedule = silc_calloc(1, sizeof(*schedule));
285   if (!schedule)
286     return NULL;
287
288   schedule->fd_queue =
289     silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
290                           silc_schedule_fd_destructor, NULL, TRUE);
291   if (!schedule->fd_queue)
292     return NULL;
293
294   silc_list_init(schedule->timeout_queue, struct SilcTaskTimeoutStruct, next);
295   silc_list_init(schedule->free_tasks, struct SilcTaskTimeoutStruct, next);
296
297   schedule->app_context = app_context;
298   schedule->valid = TRUE;
299   schedule->max_tasks = max_tasks;
300
301   /* Allocate scheduler lock */
302   silc_mutex_alloc(&schedule->lock);
303
304   /* Initialize the platform specific scheduler. */
305   schedule->internal = schedule_ops.init(schedule, app_context);
306
307   return schedule;
308 }
309
310 /* Uninitializes the schedule. This is called when the program is ready
311    to end. This removes all tasks and task queues. Returns FALSE if the
312    scheduler could not be uninitialized. This happens when the scheduler
313    is still valid and silc_schedule_stop has not been called. */
314
315 SilcBool silc_schedule_uninit(SilcSchedule schedule)
316 {
317   SilcTask task;
318
319   SILC_LOG_DEBUG(("Uninitializing scheduler"));
320
321   if (schedule->valid == TRUE)
322     return FALSE;
323
324   /* Dispatch all timeouts before going away */
325   SILC_SCHEDULE_LOCK(schedule);
326   silc_schedule_dispatch_timeout(schedule, TRUE);
327   SILC_SCHEDULE_UNLOCK(schedule);
328
329   /* Deliver signals before going away */
330   if (schedule->signal_tasks) {
331     schedule_ops.signals_call(schedule, schedule->internal);
332     schedule->signal_tasks = FALSE;
333   }
334
335   /* Unregister all tasks */
336   silc_schedule_task_del(schedule, SILC_ALL_TASKS);
337   silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
338
339   /* Delete timeout task freelist */
340   silc_list_start(schedule->free_tasks);
341   while ((task = silc_list_get(schedule->free_tasks)))
342     silc_free(task);
343
344   /* Unregister all task queues */
345   silc_hash_table_free(schedule->fd_queue);
346
347   /* Uninit the platform specific scheduler. */
348   schedule_ops.uninit(schedule, schedule->internal);
349
350   silc_mutex_free(schedule->lock);
351   silc_free(schedule);
352
353   return TRUE;
354 }
355
356 /* Stops the schedule even if it is not supposed to be stopped yet.
357    After calling this, one should call silc_schedule_uninit (after the
358    silc_schedule has returned). */
359
360 void silc_schedule_stop(SilcSchedule schedule)
361 {
362   SILC_LOG_DEBUG(("Stopping scheduler"));
363   SILC_SCHEDULE_LOCK(schedule);
364   schedule->valid = FALSE;
365   SILC_SCHEDULE_UNLOCK(schedule);
366 }
367
368 /* Runs the scheduler once and then returns. */
369
370 SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
371 {
372   struct timeval timeout;
373   int ret;
374
375   SILC_LOG_DEBUG(("In scheduler loop"));
376
377   if (!schedule->is_locked)
378     SILC_SCHEDULE_LOCK(schedule);
379
380   /* Deliver signals if any has been set to be called */
381   if (schedule->signal_tasks) {
382     SILC_SCHEDULE_UNLOCK(schedule);
383     schedule_ops.signals_call(schedule, schedule->internal);
384     schedule->signal_tasks = FALSE;
385     SILC_SCHEDULE_LOCK(schedule);
386   }
387
388   /* Check if scheduler is valid */
389   if (schedule->valid == FALSE) {
390     SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
391     if (!schedule->is_locked)
392       SILC_SCHEDULE_UNLOCK(schedule);
393     return FALSE;
394   }
395
396   /* Calculate next timeout for silc_select().  This is the timeout value
397      when at earliest some of the timeout tasks expire.  This may dispatch
398      already expired timeouts. */
399   silc_schedule_select_timeout(schedule);
400
401   /* Check if scheduler is valid */
402   if (schedule->valid == FALSE) {
403     SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
404     if (!schedule->is_locked)
405       SILC_SCHEDULE_UNLOCK(schedule);
406     return FALSE;
407   }
408
409   if (timeout_usecs >= 0) {
410     timeout.tv_sec = 0;
411     timeout.tv_usec = timeout_usecs;
412     schedule->timeout = timeout;
413     schedule->has_timeout = TRUE;
414   }
415
416   /* This is the main silc_select(). The program blocks here until some
417      of the selected file descriptors change status or the selected
418      timeout expires. */
419   SILC_LOG_DEBUG(("Select"));
420   ret = schedule_ops.select(schedule, schedule->internal);
421
422   switch (ret) {
423   case 0:
424     /* Timeout */
425     SILC_LOG_DEBUG(("Running timeout tasks"));
426     silc_schedule_dispatch_timeout(schedule, FALSE);
427     break;
428   case -1:
429     /* Error */
430     if (errno == EINTR)
431       break;
432     SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
433     break;
434   default:
435     /* There is some data available now */
436     SILC_LOG_DEBUG(("Running fd tasks"));
437     silc_schedule_dispatch_fd(schedule);
438     break;
439   }
440
441   if (!schedule->is_locked)
442     SILC_SCHEDULE_UNLOCK(schedule);
443
444   return TRUE;
445 }
446
447 /* The SILC scheduler. This is actually the main routine in SILC programs.
448    When this returns the program is to be ended. Before this function can
449    be called, one must call silc_schedule_init function. */
450
451 void silc_schedule(SilcSchedule schedule)
452 {
453   SILC_LOG_DEBUG(("Running scheduler"));
454
455   if (schedule->valid == FALSE) {
456     SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
457     return;
458   }
459
460   SILC_SCHEDULE_LOCK(schedule);
461   schedule->is_locked = TRUE;
462
463   /* Start the scheduler loop */
464   while (silc_schedule_one(schedule, -1))
465     ;
466
467   SILC_SCHEDULE_UNLOCK(schedule);
468 }
469
470 /* Wakes up the scheduler. This is used only in multi-threaded
471    environments where threads may add new tasks or remove old tasks
472    from task queues. This is called to wake up the scheduler in the
473    main thread so that it detects the changes in the task queues.
474    If threads support is not compiled in this function has no effect.
475    Implementation of this function is platform specific. */
476
477 void silc_schedule_wakeup(SilcSchedule schedule)
478 {
479 #ifdef SILC_THREADS
480   SILC_LOG_DEBUG(("Wakeup scheduler"));
481   SILC_SCHEDULE_LOCK(schedule);
482   schedule_ops.wakeup(schedule, schedule->internal);
483   SILC_SCHEDULE_UNLOCK(schedule);
484 #endif
485 }
486
487 /* Returns the application specific context that was saved into the
488    scheduler in silc_schedule_init function.  The context is also
489    returned to application in task callback functions, but this function
490    may be used to get it as well if needed. */
491
492 void *silc_schedule_get_context(SilcSchedule schedule)
493 {
494   return schedule->app_context;
495 }
496
497 /* Add new task to the scheduler */
498
499 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
500                                 SilcTaskCallback callback, void *context,
501                                 long seconds, long useconds,
502                                 SilcTaskType type)
503 {
504   SilcTask task = NULL;
505
506   if (!schedule->valid)
507     return NULL;
508
509   SILC_SCHEDULE_LOCK(schedule);
510
511   if (type == SILC_TASK_TIMEOUT) {
512     SilcTaskTimeout tmp, prev, ttask;
513
514     ttask = silc_list_get(schedule->free_tasks);
515     if (!ttask) {
516       ttask = silc_calloc(1, sizeof(*ttask));
517       if (!ttask)
518         goto out;
519     }
520     silc_list_del(schedule->free_tasks, ttask);
521
522     ttask->header.type = 1;
523     ttask->header.callback = callback;
524     ttask->header.context = context;
525     ttask->header.valid = TRUE;
526
527     /* Add timeout */
528     if ((seconds + useconds) > 0) {
529       silc_gettimeofday(&ttask->timeout);
530       ttask->timeout.tv_sec += seconds + (useconds / 1000000L);
531       ttask->timeout.tv_usec += (useconds % 1000000L);
532       if (ttask->timeout.tv_usec >= 1000000L) {
533         ttask->timeout.tv_sec += 1;
534         ttask->timeout.tv_usec -= 1000000L;
535       }
536     }
537
538     SILC_LOG_DEBUG(("New timeout task %p: sec=%d, usec=%d", ttask,
539                     seconds, useconds));
540
541     /* Add task to correct spot so that the first task in the list has
542        the earliest timeout. */
543     silc_list_start(schedule->timeout_queue);
544     prev = NULL;
545     while ((tmp = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
546       /* If we have shorter timeout, we have found our spot */
547       if (silc_compare_timeval(&ttask->timeout, &tmp->timeout)) {
548         silc_list_insert(schedule->timeout_queue, prev, ttask);
549         break;
550       }
551       prev = tmp;
552     }
553     if (!tmp)
554       silc_list_add(schedule->timeout_queue, ttask);
555
556     task = (SilcTask)ttask;
557
558   } else if (type == SILC_TASK_FD) {
559     /* Check if fd is already added */
560     if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
561                              NULL, (void **)&task))
562       goto out;
563
564     /* Check max tasks */
565     if (schedule->max_tasks > 0 &&
566         silc_hash_table_count(schedule->fd_queue) >= schedule->max_tasks) {
567       SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task"));
568       goto out;
569     }
570
571     SilcTaskFd ftask = silc_calloc(1, sizeof(*ftask));
572     if (!ftask)
573       goto out;
574
575     SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd));
576
577     ftask->header.type = 0;
578     ftask->header.callback = callback;
579     ftask->header.context = context;
580     ftask->header.valid = TRUE;
581     ftask->events = SILC_TASK_READ;
582     ftask->fd = fd;
583
584     /* Add task */
585     silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask);
586
587     task = (SilcTask)ftask;
588
589   } else if (type == SILC_TASK_SIGNAL) {
590     SILC_SCHEDULE_UNLOCK(schedule);
591     schedule_ops.signal_register(schedule, schedule->internal, (int)fd,
592                                  callback, context);
593     return NULL;
594   }
595
596
597  out:
598   SILC_SCHEDULE_UNLOCK(schedule);
599   return task;
600 }
601
602 /* Invalidates task */
603
604 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
605 {
606   if (task == SILC_ALL_TASKS) {
607     SilcTask task;
608     SilcHashTableList htl;
609
610     SILC_LOG_DEBUG(("Unregister all tasks"));
611
612     SILC_SCHEDULE_LOCK(schedule);
613
614     /* Delete from fd queue */
615     silc_hash_table_list(schedule->fd_queue, &htl);
616     while (silc_hash_table_get(&htl, NULL, (void **)&task))
617       task->valid = FALSE;
618     silc_hash_table_list_reset(&htl);
619
620     /* Delete from timeout queue */
621     silc_list_start(schedule->timeout_queue);
622     while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
623            != SILC_LIST_END)
624       task->valid = FALSE;
625
626     SILC_SCHEDULE_UNLOCK(schedule);
627     return;
628   }
629
630   SILC_LOG_DEBUG(("Unregistering task %p", task));
631   SILC_SCHEDULE_LOCK(schedule);
632   task->valid = FALSE;
633   SILC_SCHEDULE_UNLOCK(schedule);
634 }
635
636 /* Invalidate task by fd */
637
638 void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
639 {
640   SilcTask task = NULL;
641
642   SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
643
644   SILC_SCHEDULE_LOCK(schedule);
645
646   /* fd is unique, so there is only one task with this fd in the table */
647   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd), NULL,
648                            (void **)&task))
649     task->valid = FALSE;
650
651   SILC_SCHEDULE_UNLOCK(schedule);
652
653   /* If it is signal, remove it */
654   if (!task)
655     schedule_ops.signal_unregister(schedule, schedule->internal, fd);
656 }
657
658 /* Invalidate task by task callback. */
659
660 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
661                                         SilcTaskCallback callback)
662 {
663   SilcTask task;
664   SilcHashTableList htl;
665
666   SILC_LOG_DEBUG(("Unregister task by callback"));
667
668   SILC_SCHEDULE_LOCK(schedule);
669
670   /* Delete from fd queue */
671   silc_hash_table_list(schedule->fd_queue, &htl);
672   while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
673     if (task->callback == callback)
674       task->valid = FALSE;
675   }
676   silc_hash_table_list_reset(&htl);
677
678   /* Delete from timeout queue */
679   silc_list_start(schedule->timeout_queue);
680   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
681          != SILC_LIST_END) {
682     if (task->callback == callback)
683       task->valid = FALSE;
684   }
685
686   SILC_SCHEDULE_UNLOCK(schedule);
687 }
688
689 /* Invalidate task by context. */
690
691 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
692 {
693   SilcTask task;
694   SilcHashTableList htl;
695
696   SILC_LOG_DEBUG(("Unregister task by context"));
697
698   SILC_SCHEDULE_LOCK(schedule);
699
700   /* Delete from fd queue */
701   silc_hash_table_list(schedule->fd_queue, &htl);
702   while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
703     if (task->context == context)
704       task->valid = FALSE;
705   }
706   silc_hash_table_list_reset(&htl);
707
708   /* Delete from timeout queue */
709   silc_list_start(schedule->timeout_queue);
710   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
711          != SILC_LIST_END) {
712     if (task->context == context)
713       task->valid = FALSE;
714   }
715
716   SILC_SCHEDULE_UNLOCK(schedule);
717 }
718
719 /* Invalidate task by all */
720
721 void silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
722                                    SilcTaskCallback callback, void *context)
723 {
724   SilcTask task;
725
726   SILC_LOG_DEBUG(("Unregister task by fd, callback and context"));
727
728   /* For fd task, callback and context is irrelevant as fd is unique */
729   if (fd)
730     silc_schedule_task_del_by_fd(schedule, fd);
731
732   SILC_SCHEDULE_LOCK(schedule);
733
734   /* Delete from timeout queue */
735   silc_list_start(schedule->timeout_queue);
736   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
737          != SILC_LIST_END) {
738     if (task->callback == callback && task->context == context)
739       task->valid = FALSE;
740   }
741
742   SILC_SCHEDULE_UNLOCK(schedule);
743 }
744
745 /* Sets a file descriptor to be listened by scheduler. One can call this
746    directly if wanted. This can be called multiple times for one file
747    descriptor to set different iomasks. */
748
749 void silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
750                                  SilcTaskEvent mask, SilcBool send_events)
751 {
752   SilcTaskFd task;
753
754   if (!schedule->valid)
755     return;
756
757   SILC_SCHEDULE_LOCK(schedule);
758
759   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
760                            NULL, (void **)&task)) {
761     task->events = mask;
762     if (send_events) {
763       task->revents = mask;
764       silc_schedule_dispatch_fd(schedule);
765     }
766   }
767
768   SILC_SCHEDULE_UNLOCK(schedule);
769 }
770
771 /* Removes a file descriptor from listen list. */
772
773 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
774 {
775   silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
776 }