Added SILC Server library.
[silc.git] / lib / silcutil / silcschedule.c
1 /*
2
3   silcschedule.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1998 - 2005 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 /* Platform specific implementation */
24 extern const SilcScheduleOps schedule_ops;
25
26 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task);
27 static void silc_schedule_dispatch_fd(SilcSchedule schedule);
28 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
29                                            SilcBool dispatch_all);
30
31 /* Fd task hash table destructor */
32
33 static void silc_schedule_fd_destructor(void *key, void *context,
34                                         void *user_context)
35 {
36   silc_free(context);
37 }
38
39 /* Initializes the scheduler. This returns the scheduler context that
40    is given as arugment usually to all silc_schedule_* functions.
41    The `max_tasks' indicates the number of maximum tasks that the
42    scheduler can handle. The `app_context' is application specific
43    context that is delivered to task callbacks. */
44
45 SilcSchedule silc_schedule_init(int max_tasks, void *app_context)
46 {
47   SilcSchedule schedule;
48
49   SILC_LOG_DEBUG(("Initializing scheduler"));
50
51   schedule = silc_calloc(1, sizeof(*schedule));
52   if (!schedule)
53     return NULL;
54
55   schedule->fd_queue =
56     silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
57                           silc_schedule_fd_destructor, NULL, TRUE);
58   if (!schedule->fd_queue)
59     return NULL;
60
61   silc_list_init(schedule->timeout_queue, struct SilcTaskTimeoutStruct, next);
62
63   schedule->app_context = app_context;
64   schedule->valid = TRUE;
65   schedule->max_tasks = max_tasks;
66
67   /* Allocate scheduler lock */
68   silc_mutex_alloc(&schedule->lock);
69
70   /* Initialize the platform specific scheduler. */
71   schedule->internal = schedule_ops.init(schedule, app_context);
72
73   return schedule;
74 }
75
76 /* Uninitializes the schedule. This is called when the program is ready
77    to end. This removes all tasks and task queues. Returns FALSE if the
78    scheduler could not be uninitialized. This happens when the scheduler
79    is still valid and silc_schedule_stop has not been called. */
80
81 SilcBool silc_schedule_uninit(SilcSchedule schedule)
82 {
83   SILC_LOG_DEBUG(("Uninitializing scheduler"));
84
85   if (schedule->valid == TRUE)
86     return FALSE;
87
88   /* Dispatch all timeouts before going away */
89   SILC_SCHEDULE_LOCK(schedule);
90   silc_schedule_dispatch_timeout(schedule, TRUE);
91   SILC_SCHEDULE_UNLOCK(schedule);
92
93   /* Deliver signals before going away */
94   if (schedule->signal_tasks) {
95     schedule_ops.signals_call(schedule, schedule->internal);
96     schedule->signal_tasks = FALSE;
97   }
98
99   /* Unregister all tasks */
100   silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
101   silc_schedule_task_remove(schedule, SILC_ALL_TASKS);
102
103   /* Unregister all task queues */
104   silc_hash_table_free(schedule->fd_queue);
105
106   /* Uninit the platform specific scheduler. */
107   schedule_ops.uninit(schedule, schedule->internal);
108
109   silc_mutex_free(schedule->lock);
110   silc_free(schedule);
111
112   return TRUE;
113 }
114
115 /* Stops the schedule even if it is not supposed to be stopped yet.
116    After calling this, one should call silc_schedule_uninit (after the
117    silc_schedule has returned). */
118
119 void silc_schedule_stop(SilcSchedule schedule)
120 {
121   SILC_LOG_DEBUG(("Stopping scheduler"));
122   SILC_SCHEDULE_LOCK(schedule);
123   schedule->valid = FALSE;
124   SILC_SCHEDULE_UNLOCK(schedule);
125 }
126
127 /* Executes file descriptor tasks. Invalid tasks are removed here. */
128
129 static void silc_schedule_dispatch_fd(SilcSchedule schedule)
130 {
131   SilcHashTableList htl;
132   SilcTask t;
133   SilcTaskFd task;
134   SilcUInt32 fd;
135
136   silc_hash_table_list(schedule->fd_queue, &htl);
137   while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task)) {
138     t = (SilcTask)task;
139
140     if (!t->valid) {
141       silc_schedule_task_remove(schedule, t);
142       continue;
143     }
144     if (!task->events || !task->revents)
145       continue;
146
147     /* Is the task ready for reading */
148     if (task->revents & SILC_TASK_READ) {
149       SILC_SCHEDULE_UNLOCK(schedule);
150       t->callback(schedule, schedule->app_context, SILC_TASK_READ,
151                   task->fd, t->context);
152       SILC_SCHEDULE_LOCK(schedule);
153     }
154
155     /* Is the task ready for writing */
156     if (t->valid && task->revents & SILC_TASK_WRITE) {
157       SILC_SCHEDULE_UNLOCK(schedule);
158       t->callback(schedule, schedule->app_context, SILC_TASK_WRITE,
159                   task->fd, t->context);
160       SILC_SCHEDULE_LOCK(schedule);
161     }
162
163     /* Remove if task was invalidated in the task callback */
164     if (!t->valid)
165       silc_schedule_task_remove(schedule, t);
166   }
167   silc_hash_table_list_reset(&htl);
168 }
169
170 /* Executes all tasks whose timeout has expired. The task is removed from
171    the task queue after the callback function has returned. Also, invalid
172    tasks are removed here. */
173
174 static void silc_schedule_dispatch_timeout(SilcSchedule schedule,
175                                            SilcBool dispatch_all)
176 {
177   SilcTask t;
178   SilcTaskTimeout task;
179   struct timeval curtime;
180   int count = 0;
181
182   SILC_LOG_DEBUG(("Running timeout tasks"));
183
184   silc_gettimeofday(&curtime);
185
186   /* First task in the task queue has always the earliest timeout. */
187   silc_list_start(schedule->timeout_queue);
188   while ((task = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
189     t = (SilcTask)task;
190
191     /* Remove invalid task */
192     if (!t->valid) {
193       silc_schedule_task_remove(schedule, t);
194       continue;
195     }
196
197     /* Execute the task if the timeout has expired */
198     if (dispatch_all || silc_compare_timeval(&task->timeout, &curtime)) {
199       SILC_SCHEDULE_UNLOCK(schedule);
200       t->callback(schedule, schedule->app_context, SILC_TASK_EXPIRE, 0,
201                   t->context);
202       SILC_SCHEDULE_LOCK(schedule);
203
204       /* Remove the expired task */
205       silc_schedule_task_remove(schedule, t);
206
207       /* Balance when we have lots of small timeouts */
208       if ((++count) > 50)
209         break;
210     }
211   }
212 }
213
214 /* Calculates next timeout. This is the timeout value when at earliest some
215    of the timeout tasks expire. If this is in the past, they will be
216    dispatched now. */
217
218 static void silc_schedule_select_timeout(SilcSchedule schedule)
219 {
220   SilcTask t;
221   SilcTaskTimeout task;
222   struct timeval curtime;
223   SilcBool dispatch = TRUE;
224
225   /* Get the current time */
226   silc_gettimeofday(&curtime);
227   schedule->has_timeout = FALSE;
228
229   /* First task in the task queue has always the earliest timeout. */
230   silc_list_start(schedule->timeout_queue);
231   while ((task = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
232     t = (SilcTask)task;
233
234     /* Remove invalid task */
235     if (!t->valid) {
236       silc_schedule_task_remove(schedule, t);
237       continue;
238     }
239
240     /* If the timeout is in past, we will run the task and all other
241        timeout tasks from the past. */
242     if (silc_compare_timeval(&task->timeout, &curtime) && dispatch) {
243       silc_schedule_dispatch_timeout(schedule, FALSE);
244       if (!schedule->valid)
245         return;
246
247       /* Start selecting new timeout again after dispatch */
248       silc_list_start(schedule->timeout_queue);
249       dispatch = FALSE;
250       continue;
251     }
252
253     /* Calculate the next timeout */
254     curtime.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
255     curtime.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
256     if (curtime.tv_sec < 0)
257       curtime.tv_sec = 0;
258
259     /* We wouldn't want to go under zero, check for it. */
260     if (curtime.tv_usec < 0) {
261       curtime.tv_sec -= 1;
262       if (curtime.tv_sec < 0)
263         curtime.tv_sec = 0;
264       curtime.tv_usec += 1000000L;
265     }
266
267     break;
268   }
269
270   /* Save the timeout */
271   if (task) {
272     schedule->timeout = curtime;
273     schedule->has_timeout = TRUE;
274     SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout.tv_sec,
275                     schedule->timeout.tv_usec));
276   }
277 }
278
279 /* Runs the scheduler once and then returns. */
280
281 SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
282 {
283   struct timeval timeout;
284   int ret;
285
286   SILC_LOG_DEBUG(("In scheduler loop"));
287
288   if (!schedule->is_locked)
289     SILC_SCHEDULE_LOCK(schedule);
290
291   /* Deliver signals if any has been set to be called */
292   if (schedule->signal_tasks) {
293     SILC_SCHEDULE_UNLOCK(schedule);
294     schedule_ops.signals_call(schedule, schedule->internal);
295     schedule->signal_tasks = FALSE;
296     SILC_SCHEDULE_LOCK(schedule);
297   }
298
299   /* Check if scheduler is valid */
300   if (schedule->valid == FALSE) {
301     SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
302     if (!schedule->is_locked)
303       SILC_SCHEDULE_UNLOCK(schedule);
304     return FALSE;
305   }
306
307   /* Calculate next timeout for silc_select().  This is the timeout value
308      when at earliest some of the timeout tasks expire.  This may dispatch
309      already expired timeouts. */
310   silc_schedule_select_timeout(schedule);
311
312   /* Check if scheduler is valid */
313   if (schedule->valid == FALSE) {
314     SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
315     if (!schedule->is_locked)
316       SILC_SCHEDULE_UNLOCK(schedule);
317     return FALSE;
318   }
319
320   if (timeout_usecs >= 0) {
321     timeout.tv_sec = 0;
322     timeout.tv_usec = timeout_usecs;
323     schedule->timeout = timeout;
324     schedule->has_timeout = TRUE;
325   }
326
327   /* This is the main silc_select(). The program blocks here until some
328      of the selected file descriptors change status or the selected
329      timeout expires. */
330   SILC_LOG_DEBUG(("Select"));
331   ret = schedule_ops.select(schedule, schedule->internal);
332
333   switch (ret) {
334   case 0:
335     /* Timeout */
336     SILC_LOG_DEBUG(("Running timeout tasks"));
337     silc_schedule_dispatch_timeout(schedule, FALSE);
338     break;
339   case -1:
340     /* Error */
341     if (errno == EINTR)
342       break;
343     SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
344     break;
345   default:
346     /* There is some data available now */
347     SILC_LOG_DEBUG(("Running fd tasks"));
348     silc_schedule_dispatch_fd(schedule);
349     break;
350   }
351
352   if (!schedule->is_locked)
353     SILC_SCHEDULE_UNLOCK(schedule);
354
355   return TRUE;
356 }
357
358 /* The SILC scheduler. This is actually the main routine in SILC programs.
359    When this returns the program is to be ended. Before this function can
360    be called, one must call silc_schedule_init function. */
361
362 void silc_schedule(SilcSchedule schedule)
363 {
364   SILC_LOG_DEBUG(("Running scheduler"));
365
366   if (schedule->valid == FALSE) {
367     SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
368     return;
369   }
370
371   SILC_SCHEDULE_LOCK(schedule);
372   schedule->is_locked = TRUE;
373
374   /* Start the scheduler loop */
375   while (silc_schedule_one(schedule, -1))
376     ;
377
378   SILC_SCHEDULE_UNLOCK(schedule);
379 }
380
381 /* Wakes up the scheduler. This is used only in multi-threaded
382    environments where threads may add new tasks or remove old tasks
383    from task queues. This is called to wake up the scheduler in the
384    main thread so that it detects the changes in the task queues.
385    If threads support is not compiled in this function has no effect.
386    Implementation of this function is platform specific. */
387
388 void silc_schedule_wakeup(SilcSchedule schedule)
389 {
390 #ifdef SILC_THREADS
391   SILC_LOG_DEBUG(("Wakeup scheduler"));
392   SILC_SCHEDULE_LOCK(schedule);
393   schedule_ops.wakeup(schedule, schedule->internal);
394   SILC_SCHEDULE_UNLOCK(schedule);
395 #endif
396 }
397
398 /* Returns the application specific context that was saved into the
399    scheduler in silc_schedule_init function.  The context is also
400    returned to application in task callback functions, but this function
401    may be used to get it as well if needed. */
402
403 void *silc_schedule_get_context(SilcSchedule schedule)
404 {
405   return schedule->app_context;
406 }
407
408 /* Add new task to the scheduler */
409
410 SilcTask silc_schedule_task_add(SilcSchedule schedule, SilcUInt32 fd,
411                                 SilcTaskCallback callback, void *context,
412                                 long seconds, long useconds,
413                                 SilcTaskType type)
414 {
415   SilcTask task = NULL;
416
417   if (!schedule->valid)
418     return NULL;
419
420   SILC_SCHEDULE_LOCK(schedule);
421
422   if (type == SILC_TASK_TIMEOUT) {
423     SilcTaskTimeout tmp, prev, ttask = silc_calloc(1, sizeof(*ttask));
424     if (!ttask)
425       goto out;
426
427     ttask->header.type = 1;
428     ttask->header.callback = callback;
429     ttask->header.context = context;
430     ttask->header.valid = TRUE;
431
432     /* Add timeout */
433     if ((seconds + useconds) > 0) {
434       silc_gettimeofday(&ttask->timeout);
435       ttask->timeout.tv_sec += seconds + (useconds / 1000000L);
436       ttask->timeout.tv_usec += (useconds % 1000000L);
437       if (ttask->timeout.tv_usec >= 1000000L) {
438         ttask->timeout.tv_sec += 1;
439         ttask->timeout.tv_usec -= 1000000L;
440       }
441     }
442
443     SILC_LOG_DEBUG(("New timeout task %p: sec=%d, usec=%d", ttask,
444                     seconds, useconds));
445
446     /* Add task to correct spot so that the first task in the list has
447        the earliest timeout. */
448     silc_list_start(schedule->timeout_queue);
449     prev = NULL;
450     while ((tmp = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
451       /* If we have shorter timeout, we have found our spot */
452       if (silc_compare_timeval(&ttask->timeout, &tmp->timeout)) {
453         silc_list_insert(schedule->timeout_queue, prev, ttask);
454         break;
455       }
456       prev = tmp;
457     }
458     if (!tmp)
459       silc_list_add(schedule->timeout_queue, ttask);
460
461     task = (SilcTask)ttask;
462   } else {
463     /* Check if fd is already added */
464     if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
465                              NULL, (void **)&task))
466       goto out;
467
468     /* Check max tasks */
469     if (schedule->max_tasks > 0 &&
470         silc_hash_table_count(schedule->fd_queue) >= schedule->max_tasks) {
471       SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task"));
472       goto out;
473     }
474
475     SilcTaskFd ftask = silc_calloc(1, sizeof(*ftask));
476     if (!ftask)
477       goto out;
478
479     SILC_LOG_DEBUG(("New fd task %p fd=%d", ftask, fd));
480
481     ftask->header.type = 0;
482     ftask->header.callback = callback;
483     ftask->header.context = context;
484     ftask->header.valid = TRUE;
485     ftask->events = SILC_TASK_READ;
486     ftask->fd = fd;
487
488     /* Add task */
489     silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask);
490
491     task = (SilcTask)ftask;
492   }
493
494  out:
495   SILC_SCHEDULE_UNLOCK(schedule);
496   return task;
497 }
498
499 /* Invalidates task */
500
501 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
502 {
503   if (task == SILC_ALL_TASKS) {
504     SilcTask task;
505     SilcHashTableList htl;
506
507     SILC_LOG_DEBUG(("Unregister all tasks"));
508
509     SILC_SCHEDULE_LOCK(schedule);
510
511     /* Delete from fd queue */
512     silc_hash_table_list(schedule->fd_queue, &htl);
513     while (silc_hash_table_get(&htl, NULL, (void **)&task))
514       task->valid = FALSE;
515     silc_hash_table_list_reset(&htl);
516
517     /* Delete from timeout queue */
518     silc_list_start(schedule->timeout_queue);
519     while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
520            != SILC_LIST_END)
521       task->valid = FALSE;
522
523     SILC_SCHEDULE_UNLOCK(schedule);
524     return;
525   }
526
527   SILC_LOG_DEBUG(("Unregistering task %p", task));
528   SILC_SCHEDULE_LOCK(schedule);
529   task->valid = FALSE;
530   SILC_SCHEDULE_UNLOCK(schedule);
531 }
532
533 /* Invalidate task by fd */
534
535 void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
536 {
537   SilcTask task;
538
539   SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
540
541   SILC_SCHEDULE_LOCK(schedule);
542
543   /* fd is unique, so there is only one task with this fd in the table */
544   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd), NULL,
545                            (void **)&task))
546     task->valid = FALSE;
547
548   SILC_SCHEDULE_UNLOCK(schedule);
549 }
550
551 /* Invalidate task by task callback. */
552
553 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
554                                         SilcTaskCallback callback)
555 {
556   SilcTask task;
557   SilcHashTableList htl;
558
559   SILC_LOG_DEBUG(("Unregister task by callback"));
560
561   SILC_SCHEDULE_LOCK(schedule);
562
563   /* Delete from fd queue */
564   silc_hash_table_list(schedule->fd_queue, &htl);
565   while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
566     if (task->callback == callback)
567       task->valid = FALSE;
568   }
569   silc_hash_table_list_reset(&htl);
570
571   /* Delete from timeout queue */
572   silc_list_start(schedule->timeout_queue);
573   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
574          != SILC_LIST_END) {
575     if (task->callback == callback)
576       task->valid = FALSE;
577   }
578
579   SILC_SCHEDULE_UNLOCK(schedule);
580 }
581
582 /* Invalidate task by context. */
583
584 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
585 {
586   SilcTask task;
587   SilcHashTableList htl;
588
589   SILC_LOG_DEBUG(("Unregister task by context"));
590
591   SILC_SCHEDULE_LOCK(schedule);
592
593   /* Delete from fd queue */
594   silc_hash_table_list(schedule->fd_queue, &htl);
595   while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
596     if (task->context == context)
597       task->valid = FALSE;
598   }
599   silc_hash_table_list_reset(&htl);
600
601   /* Delete from timeout queue */
602   silc_list_start(schedule->timeout_queue);
603   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
604          != SILC_LIST_END) {
605     if (task->context == context)
606       task->valid = FALSE;
607   }
608
609   SILC_SCHEDULE_UNLOCK(schedule);
610 }
611
612 /* Invalidate task by all */
613
614 void silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
615                                    SilcTaskCallback callback, void *context)
616 {
617   SilcTask task;
618
619   SILC_LOG_DEBUG(("Unregister task by fd, callback and context"));
620
621   /* For fd task, callback and context is irrelevant as fd is unique */
622   if (fd)
623     silc_schedule_task_del_by_fd(schedule, fd);
624
625   SILC_SCHEDULE_LOCK(schedule);
626
627   /* Delete from timeout queue */
628   silc_list_start(schedule->timeout_queue);
629   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
630          != SILC_LIST_END) {
631     if (task->callback == callback && task->context == context)
632       task->valid = FALSE;
633   }
634
635   SILC_SCHEDULE_UNLOCK(schedule);
636 }
637
638 /* Removes task from the scheduler.  This must be called with scheduler
639    locked. */
640
641 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
642 {
643   SilcTaskFd ftask;
644   SilcTaskTimeout ttask;
645
646   if (task == SILC_ALL_TASKS) {
647     SilcTask task;
648     SilcHashTableList htl;
649     SilcUInt32 fd;
650
651     /* Delete from fd queue */
652     silc_hash_table_list(schedule->fd_queue, &htl);
653     while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task))
654       silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd));
655     silc_hash_table_list_reset(&htl);
656
657     /* Delete from timeout queue */
658     silc_list_start(schedule->timeout_queue);
659     while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
660            != SILC_LIST_END) {
661       silc_list_del(schedule->timeout_queue, task);
662       silc_free(task);
663     }
664
665     return;
666   }
667
668   /* Delete from timeout queue */
669   if (task->type == 1) {
670     silc_list_start(schedule->timeout_queue);
671     while ((ttask = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
672       if (ttask == (SilcTaskTimeout)task) {
673         silc_list_del(schedule->timeout_queue, ttask);
674         silc_free(ttask);
675         break;
676       }
677     }
678
679     return;
680   }
681
682   /* Delete from fd queue */
683   ftask = (SilcTaskFd)task;
684   silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
685 }
686
687 /* Sets a file descriptor to be listened by scheduler. One can call this
688    directly if wanted. This can be called multiple times for one file
689    descriptor to set different iomasks. */
690
691 void silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
692                                  SilcTaskEvent mask, SilcBool send_events)
693 {
694   SilcTaskFd task;
695
696   if (!schedule->valid)
697     return;
698
699   SILC_SCHEDULE_LOCK(schedule);
700
701   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
702                            NULL, (void **)&task)) {
703     task->events = mask;
704     if (send_events) {
705       task->revents = mask;
706       silc_schedule_dispatch_fd(schedule);
707     }
708   }
709
710   SILC_SCHEDULE_UNLOCK(schedule);
711 }
712
713 /* Removes a file descriptor from listen list. */
714
715 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
716 {
717   silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
718 }
719
720 /* Register a new signal */
721
722 void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal,
723                                    SilcTaskCallback callback, void *context)
724 {
725   schedule_ops.signal_register(schedule, schedule->internal, signal,
726                                 callback, context);
727 }
728
729 /* Unregister a new signal */
730
731 void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal,
732                                      SilcTaskCallback callback, void *context)
733 {
734   schedule_ops.signal_unregister(schedule, schedule->internal, signal,
735                                   callback, context);
736 }
737
738 /* Call signal indicated by `signal'. */
739
740 void silc_schedule_signal_call(SilcSchedule schedule, SilcUInt32 signal)
741 {
742   /* Mark that signals needs to be delivered later. */
743   schedule_ops.signal_call(schedule, schedule->internal, signal);
744   schedule->signal_tasks = TRUE;
745 }