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