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