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