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