Fixed timeout setting when using silc_schedule_one with -1.
[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     SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout.tv_sec,
192                     schedule->timeout.tv_usec));
193   }
194 }
195
196 /* Removes task from the scheduler.  This must be called with scheduler
197    locked. */
198
199 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
200 {
201   SilcTaskFd ftask;
202   SilcTaskTimeout ttask;
203
204   if (task == SILC_ALL_TASKS) {
205     SilcTask task;
206     SilcHashTableList htl;
207     SilcUInt32 fd;
208
209     /* Delete from fd queue */
210     silc_hash_table_list(schedule->fd_queue, &htl);
211     while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task))
212       silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd));
213     silc_hash_table_list_reset(&htl);
214
215     /* Delete from timeout queue */
216     silc_list_start(schedule->timeout_queue);
217     while ((task = silc_list_get(schedule->timeout_queue))) {
218       silc_list_del(schedule->timeout_queue, task);
219       silc_free(task);
220     }
221
222     return;
223   }
224
225   /* Delete from timeout queue */
226   if (task->type == 1) {
227     silc_list_start(schedule->timeout_queue);
228     while ((ttask = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
229       if (ttask == (SilcTaskTimeout)task) {
230         silc_list_del(schedule->timeout_queue, ttask);
231
232         /* Put to free list */
233         silc_list_add(schedule->free_tasks, ttask);
234         if (silc_list_count(schedule->free_tasks) == 1)
235           silc_list_start(schedule->free_tasks);
236         break;
237       }
238     }
239
240     return;
241   }
242
243   /* Delete from fd queue */
244   ftask = (SilcTaskFd)task;
245   silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
246 }
247
248 /* Timeout freelist garbage collection */
249
250 SILC_TASK_CALLBACK(silc_schedule_timeout_gc)
251 {
252   SilcTaskTimeout t;
253   int c;
254
255   if (!schedule->valid)
256     return;
257
258   SILC_LOG_DEBUG(("Timeout freelist garbage collection"));
259
260   SILC_SCHEDULE_LOCK(schedule);
261
262   if (silc_list_count(schedule->free_tasks) <= 10) {
263     SILC_SCHEDULE_UNLOCK(schedule);
264     silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
265                                    schedule, 3600, 0);
266     return;
267   }
268   if (silc_list_count(schedule->timeout_queue) >
269       silc_list_count(schedule->free_tasks)) {
270     SILC_SCHEDULE_UNLOCK(schedule);
271     silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
272                                    schedule, 3600, 0);
273     return;
274   }
275
276   c = silc_list_count(schedule->free_tasks) / 2;
277   if (c > silc_list_count(schedule->timeout_queue))
278     c = (silc_list_count(schedule->free_tasks) -
279          silc_list_count(schedule->timeout_queue));
280   if (silc_list_count(schedule->free_tasks) - c < 10)
281     c -= (10 - (silc_list_count(schedule->free_tasks) - c));
282
283   SILC_LOG_DEBUG(("Freeing %d unused tasks, leaving %d", c,
284                   silc_list_count(schedule->free_tasks) - c));
285
286   silc_list_start(schedule->free_tasks);
287   while ((t = silc_list_get(schedule->free_tasks)) && c-- > 0) {
288     silc_list_del(schedule->free_tasks, t);
289     silc_free(t);
290   }
291   silc_list_start(schedule->free_tasks);
292
293   SILC_SCHEDULE_UNLOCK(schedule);
294
295   silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
296                                  schedule, 3600, 0);
297 }
298
299 #ifdef SILC_DIST_INPLACE
300 /* Print schedule statistics to stdout */
301
302 void silc_schedule_stats(SilcSchedule schedule)
303 {
304   SilcTaskFd ftask;
305   fprintf(stdout, "Schedule %p statistics:\n\n", schedule);
306   fprintf(stdout, "Num FD tasks         : %lu (%lu bytes allocated)\n",
307           silc_hash_table_count(schedule->fd_queue),
308           sizeof(*ftask) * silc_hash_table_count(schedule->fd_queue));
309   fprintf(stdout, "Num Timeout tasks    : %d (%d bytes allocated)\n",
310           silc_list_count(schedule->timeout_queue),
311           sizeof(struct SilcTaskTimeoutStruct) *
312           silc_list_count(schedule->timeout_queue));
313   fprintf(stdout, "Num Timeout freelist : %d (%d bytes allocated)\n",
314           silc_list_count(schedule->free_tasks),
315           sizeof(struct SilcTaskTimeoutStruct) *
316           silc_list_count(schedule->free_tasks));
317 }
318 #endif /* SILC_DIST_INPLACE */
319
320 /****************************** Public API **********************************/
321
322 /* Initializes the scheduler. This returns the scheduler context that
323    is given as arugment usually to all silc_schedule_* functions.
324    The `max_tasks' indicates the number of maximum tasks that the
325    scheduler can handle. The `app_context' is application specific
326    context that is delivered to task callbacks. */
327
328 SilcSchedule silc_schedule_init(int max_tasks, void *app_context)
329 {
330   SilcSchedule schedule;
331
332   SILC_LOG_DEBUG(("Initializing scheduler"));
333
334   schedule = silc_calloc(1, sizeof(*schedule));
335   if (!schedule)
336     return NULL;
337
338   schedule->fd_queue =
339     silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
340                           silc_schedule_fd_destructor, NULL, TRUE);
341   if (!schedule->fd_queue)
342     return NULL;
343
344   silc_list_init(schedule->timeout_queue, struct SilcTaskTimeoutStruct, next);
345   silc_list_init(schedule->free_tasks, struct SilcTaskTimeoutStruct, next);
346
347   schedule->app_context = app_context;
348   schedule->valid = TRUE;
349   schedule->max_tasks = max_tasks;
350
351   /* Allocate scheduler lock */
352   silc_mutex_alloc(&schedule->lock);
353
354   /* Initialize the platform specific scheduler. */
355   schedule->internal = schedule_ops.init(schedule, app_context);
356
357   /* Timeout freelist garbage collection */
358   silc_schedule_task_add_timeout(schedule, silc_schedule_timeout_gc,
359                                  schedule, 3600, 0);
360
361   return schedule;
362 }
363
364 /* Uninitializes the schedule. This is called when the program is ready
365    to end. This removes all tasks and task queues. Returns FALSE if the
366    scheduler could not be uninitialized. This happens when the scheduler
367    is still valid and silc_schedule_stop has not been called. */
368
369 SilcBool silc_schedule_uninit(SilcSchedule schedule)
370 {
371   SilcTask task;
372
373   SILC_LOG_DEBUG(("Uninitializing scheduler"));
374
375   if (schedule->valid == TRUE)
376     return FALSE;
377
378   /* Dispatch all timeouts before going away */
379   SILC_SCHEDULE_LOCK(schedule);
380   silc_schedule_dispatch_timeout(schedule, TRUE);
381   SILC_SCHEDULE_UNLOCK(schedule);
382
383   /* Deliver signals before going away */
384   if (schedule->signal_tasks) {
385     schedule_ops.signals_call(schedule, schedule->internal);
386     schedule->signal_tasks = FALSE;
387   }
388
389   /* Unregister all tasks */
390   silc_schedule_task_del(schedule, SILC_ALL_TASKS);
391   silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
392
393   /* Delete timeout task freelist */
394   silc_list_start(schedule->free_tasks);
395   while ((task = silc_list_get(schedule->free_tasks)))
396     silc_free(task);
397
398   /* Unregister all task queues */
399   silc_hash_table_free(schedule->fd_queue);
400
401   /* Uninit the platform specific scheduler. */
402   schedule_ops.uninit(schedule, schedule->internal);
403
404   silc_mutex_free(schedule->lock);
405   silc_free(schedule);
406
407   return TRUE;
408 }
409
410 /* Stops the schedule even if it is not supposed to be stopped yet.
411    After calling this, one should call silc_schedule_uninit (after the
412    silc_schedule has returned). */
413
414 void silc_schedule_stop(SilcSchedule schedule)
415 {
416   SILC_LOG_DEBUG(("Stopping scheduler"));
417   SILC_SCHEDULE_LOCK(schedule);
418   schedule->valid = FALSE;
419   SILC_SCHEDULE_UNLOCK(schedule);
420 }
421
422 /* Runs the scheduler once and then returns. */
423
424 SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
425 {
426   struct timeval timeout;
427   int ret;
428
429   SILC_LOG_DEBUG(("In scheduler loop"));
430
431   if (!schedule->is_locked)
432     SILC_SCHEDULE_LOCK(schedule);
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     silc_schedule_dispatch_timeout(schedule, FALSE);
481     break;
482   case -1:
483     /* Error */
484     if (errno == EINTR)
485       break;
486     SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
487     break;
488   default:
489     /* There is some data available now */
490     SILC_LOG_DEBUG(("Running fd tasks"));
491     silc_schedule_dispatch_fd(schedule);
492     break;
493   }
494
495   if (!schedule->is_locked)
496     SILC_SCHEDULE_UNLOCK(schedule);
497
498   return TRUE;
499 }
500
501 /* The SILC scheduler. This is actually the main routine in SILC programs.
502    When this returns the program is to be ended. Before this function can
503    be called, one must call silc_schedule_init function. */
504
505 void silc_schedule(SilcSchedule schedule)
506 {
507   SILC_LOG_DEBUG(("Running scheduler"));
508
509   if (schedule->valid == FALSE) {
510     SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
511     return;
512   }
513
514   SILC_SCHEDULE_LOCK(schedule);
515   schedule->is_locked = TRUE;
516
517   /* Start the scheduler loop */
518   while (silc_schedule_one(schedule, -1))
519     ;
520
521   SILC_SCHEDULE_UNLOCK(schedule);
522 }
523
524 /* Wakes up the scheduler. This is used only in multi-threaded
525    environments where threads may add new tasks or remove old tasks
526    from task queues. This is called to wake up the scheduler in the
527    main thread so that it detects the changes in the task queues.
528    If threads support is not compiled in this function has no effect.
529    Implementation of this function is platform specific. */
530
531 void silc_schedule_wakeup(SilcSchedule schedule)
532 {
533 #ifdef SILC_THREADS
534   SILC_LOG_DEBUG(("Wakeup scheduler"));
535   SILC_SCHEDULE_LOCK(schedule);
536   schedule_ops.wakeup(schedule, schedule->internal);
537   SILC_SCHEDULE_UNLOCK(schedule);
538 #endif
539 }
540
541 /* Returns the application specific context that was saved into the
542    scheduler in silc_schedule_init function.  The context is also
543    returned to application in task callback functions, but this function
544    may be used to get it as well if needed. */
545
546 void *silc_schedule_get_context(SilcSchedule schedule)
547 {
548   return schedule->app_context;
549 }
550
551 /* Add new task to the scheduler */
552
553 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
554                                 SilcTaskCallback callback, void *context,
555                                 long seconds, long useconds,
556                                 SilcTaskType type)
557 {
558   SilcTask task = NULL;
559
560   if (!schedule->valid)
561     return NULL;
562
563   SILC_SCHEDULE_LOCK(schedule);
564
565   if (type == SILC_TASK_TIMEOUT) {
566     SilcTaskTimeout tmp, prev, ttask;
567
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     if ((seconds + useconds) > 0) {
583       silc_gettimeofday(&ttask->timeout);
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 }