c7ce66dcc190ef795fbfb96ebc93db1c18476345
[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 "silcincludes.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 -1:
335     /* Error */
336     if (errno == EINTR)
337       break;
338     SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
339     break;
340   case 0:
341     /* Timeout */
342     SILC_LOG_DEBUG(("Running timeout tasks"));
343     silc_schedule_dispatch_timeout(schedule, FALSE);
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     SILC_LOG_DEBUG(("Registering new timeout task %p", ttask));
428
429     ttask->header.type = 1;
430     ttask->header.callback = callback;
431     ttask->header.context = context;
432     ttask->header.valid = TRUE;
433
434     /* Add timeout */
435     if ((seconds + useconds) > 0) {
436       silc_gettimeofday(&ttask->timeout);
437       ttask->timeout.tv_sec += seconds + (useconds / 1000000L);
438       ttask->timeout.tv_usec += (useconds % 1000000L);
439       if (ttask->timeout.tv_usec > 999999L) {
440         ttask->timeout.tv_sec += 1;
441         ttask->timeout.tv_usec -= 1000000L;
442       }
443     }
444
445     /* Add task to correct spot so that the first task in the list has
446        the earliest timeout. */
447     silc_list_start(schedule->timeout_queue);
448     prev = NULL;
449     while ((tmp = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
450       /* If we have shorter timeout, we have found our spot */
451       if (silc_compare_timeval(&ttask->timeout, &tmp->timeout)) {
452         silc_list_insert(schedule->timeout_queue, prev, ttask);
453         break;
454       }
455       prev = tmp;
456     }
457     if (!tmp)
458       silc_list_add(schedule->timeout_queue, ttask);
459
460     task = (SilcTask)ttask;
461   } else {
462     /* Check if fd is already added */
463     if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
464                              NULL, NULL))
465       goto out;
466
467     /* Check max tasks */
468     if (schedule->max_tasks > 0 &&
469         silc_hash_table_count(schedule->fd_queue) >= schedule->max_tasks) {
470       SILC_LOG_WARNING(("Scheduler task limit reached: cannot add new task"));
471       goto out;
472     }
473
474     SilcTaskFd ftask = silc_calloc(1, sizeof(*ftask));
475     if (!ftask)
476       goto out;
477
478     SILC_LOG_DEBUG(("Registering new fd task %p fd=%d", ftask, fd));
479
480     ftask->header.type = 0;
481     ftask->header.callback = callback;
482     ftask->header.context = context;
483     ftask->header.valid = TRUE;
484     ftask->events = SILC_TASK_READ;
485     ftask->fd = fd;
486
487     /* Add task */
488     silc_hash_table_add(schedule->fd_queue, SILC_32_TO_PTR(fd), ftask);
489
490     task = (SilcTask)ftask;
491   }
492
493  out:
494   SILC_SCHEDULE_UNLOCK(schedule);
495   return task;
496 }
497
498 /* Invalidates task */
499
500 void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
501 {
502   if (task == SILC_ALL_TASKS) {
503     SilcTask task;
504     SilcHashTableList htl;
505
506     SILC_LOG_DEBUG(("Unregister all tasks"));
507
508     SILC_SCHEDULE_LOCK(schedule);
509
510     /* Delete from fd queue */
511     silc_hash_table_list(schedule->fd_queue, &htl);
512     while (silc_hash_table_get(&htl, NULL, (void **)&task))
513       task->valid = FALSE;
514     silc_hash_table_list_reset(&htl);
515
516     /* Delete from timeout queue */
517     silc_list_start(schedule->timeout_queue);
518     while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
519            != SILC_LIST_END)
520       task->valid = FALSE;
521
522     SILC_SCHEDULE_UNLOCK(schedule);
523     return;
524   }
525
526   SILC_LOG_DEBUG(("Unregistering task %p", task));
527   SILC_SCHEDULE_LOCK(schedule);
528   task->valid = FALSE;
529   SILC_SCHEDULE_UNLOCK(schedule);
530 }
531
532 /* Invalidate task by fd */
533
534 void silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd)
535 {
536   SilcTask task;
537
538   SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
539
540   SILC_SCHEDULE_LOCK(schedule);
541
542   /* fd is unique, so there is only one task with this fd in the table */
543   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd), NULL,
544                            (void **)&task))
545     task->valid = FALSE;
546
547   SILC_SCHEDULE_UNLOCK(schedule);
548 }
549
550 /* Invalidate task by task callback. */
551
552 void silc_schedule_task_del_by_callback(SilcSchedule schedule,
553                                         SilcTaskCallback callback)
554 {
555   SilcTask task;
556   SilcHashTableList htl;
557
558   SILC_LOG_DEBUG(("Unregister task by callback"));
559
560   SILC_SCHEDULE_LOCK(schedule);
561
562   /* Delete from fd queue */
563   silc_hash_table_list(schedule->fd_queue, &htl);
564   while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
565     if (task->callback == callback)
566       task->valid = FALSE;
567   }
568   silc_hash_table_list_reset(&htl);
569
570   /* Delete from timeout queue */
571   silc_list_start(schedule->timeout_queue);
572   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
573          != SILC_LIST_END) {
574     if (task->callback == callback)
575       task->valid = FALSE;
576   }
577
578   SILC_SCHEDULE_UNLOCK(schedule);
579 }
580
581 /* Invalidate task by context. */
582
583 void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
584 {
585   SilcTask task;
586   SilcHashTableList htl;
587
588   SILC_LOG_DEBUG(("Unregister task by context"));
589
590   SILC_SCHEDULE_LOCK(schedule);
591
592   /* Delete from fd queue */
593   silc_hash_table_list(schedule->fd_queue, &htl);
594   while (silc_hash_table_get(&htl, NULL, (void **)&task)) {
595     if (task->context == context)
596       task->valid = FALSE;
597   }
598   silc_hash_table_list_reset(&htl);
599
600   /* Delete from timeout queue */
601   silc_list_start(schedule->timeout_queue);
602   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
603          != SILC_LIST_END) {
604     if (task->context == context)
605       task->valid = FALSE;
606   }
607
608   SILC_SCHEDULE_UNLOCK(schedule);
609 }
610
611 /* Invalidate task by all */
612
613 void silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
614                                    SilcTaskCallback callback, void *context)
615 {
616   SilcTask task;
617
618   SILC_LOG_DEBUG(("Unregister task by fd, callback and context"));
619
620   /* For fd task, callback and context is irrelevant as fd is unique */
621   if (fd)
622     silc_schedule_task_del_by_fd(schedule, fd);
623
624   SILC_SCHEDULE_LOCK(schedule);
625
626   /* Delete from timeout queue */
627   silc_list_start(schedule->timeout_queue);
628   while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
629          != SILC_LIST_END) {
630     if (task->callback == callback && task->context == context)
631       task->valid = FALSE;
632   }
633
634   SILC_SCHEDULE_UNLOCK(schedule);
635 }
636
637 /* Removes task from the scheduler.  This must be called with scheduler
638    locked. */
639
640 static void silc_schedule_task_remove(SilcSchedule schedule, SilcTask task)
641 {
642   SilcTaskFd ftask;
643   SilcTaskTimeout ttask;
644
645   if (task == SILC_ALL_TASKS) {
646     SilcTask task;
647     SilcHashTableList htl;
648     SilcUInt32 fd;
649
650     /* Delete from fd queue */
651     silc_hash_table_list(schedule->fd_queue, &htl);
652     while (silc_hash_table_get(&htl, (void **)&fd, (void **)&task))
653       silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(fd));
654     silc_hash_table_list_reset(&htl);
655
656     /* Delete from timeout queue */
657     silc_list_start(schedule->timeout_queue);
658     while ((task = (SilcTask)silc_list_get(schedule->timeout_queue))
659            != SILC_LIST_END) {
660       silc_list_del(schedule->timeout_queue, task);
661       silc_free(task);
662     }
663
664     return;
665   }
666
667   /* Delete from timeout queue */
668   if (task->type == 1) {
669     silc_list_start(schedule->timeout_queue);
670     while ((ttask = silc_list_get(schedule->timeout_queue)) != SILC_LIST_END) {
671       if (ttask == (SilcTaskTimeout)task) {
672         silc_list_del(schedule->timeout_queue, ttask);
673         silc_free(ttask);
674         break;
675       }
676     }
677
678     return;
679   }
680
681   /* Delete from fd queue */
682   ftask = (SilcTaskFd)task;
683   silc_hash_table_del(schedule->fd_queue, SILC_32_TO_PTR(ftask->fd));
684 }
685
686 /* Sets a file descriptor to be listened by scheduler. One can call this
687    directly if wanted. This can be called multiple times for one file
688    descriptor to set different iomasks. */
689
690 void silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
691                                  SilcTaskEvent mask, SilcBool send_events)
692 {
693   SilcTaskFd task;
694
695   if (!schedule->valid)
696     return;
697
698   SILC_SCHEDULE_LOCK(schedule);
699
700   if (silc_hash_table_find(schedule->fd_queue, SILC_32_TO_PTR(fd),
701                            NULL, (void **)&task)) {
702     task->events = mask;
703     if (send_events) {
704       task->revents = mask;
705       silc_schedule_dispatch_fd(schedule);
706     }
707   }
708
709   SILC_SCHEDULE_UNLOCK(schedule);
710 }
711
712 /* Removes a file descriptor from listen list. */
713
714 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd)
715 {
716   silc_schedule_set_listen_fd(schedule, fd, 0, FALSE);
717 }
718
719 /* Register a new signal */
720
721 void silc_schedule_signal_register(SilcSchedule schedule, SilcUInt32 signal,
722                                    SilcTaskCallback callback, void *context)
723 {
724   schedule_ops.signal_register(schedule, schedule->internal, signal,
725                                 callback, context);
726 }
727
728 /* Unregister a new signal */
729
730 void silc_schedule_signal_unregister(SilcSchedule schedule, SilcUInt32 signal,
731                                      SilcTaskCallback callback, void *context)
732 {
733   schedule_ops.signal_unregister(schedule, schedule->internal, signal,
734                                   callback, context);
735 }
736
737 /* Call signal indicated by `signal'. */
738
739 void silc_schedule_signal_call(SilcSchedule schedule, SilcUInt32 signal)
740 {
741   /* Mark that signals needs to be delivered later. */
742   schedule_ops.signal_call(schedule, schedule->internal, signal);
743   schedule->signal_tasks = TRUE;
744 }