1 /*
2 
3   silcschedule.h
4 
5   Author: Pekka Riikonen <priikone@silcnet.org>
6 
7   Copyright (C) 1998 - 2007 Pekka Riikonen
8 
9   The contents of this file are subject to one of the Licenses specified
10   in the COPYING file;  You may not use this file except in compliance
11   with the License.
12 
13   The software distributed under the License is distributed on an "AS IS"
14   basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15   KIND, either expressed or implied.  See the COPYING file for more
16   information.
17 
18 */
19 
20 /****h* silcutil/SILC Schedule Interface
21  *
22  * DESCRIPTION
23  *
24  * The SILC Scheduler is the heart of any application. The scheduler provides
25  * the application's main loop that can handle incoming data, outgoing data,
26  * timeouts and dispatch different kind of tasks.
27  *
28  * The SILC Scheduler supports file descriptor based tasks and timeout tasks.
29  * File descriptor tasks are tasks that perform some operation over the
30  * specified file descriptor. These include network connections, for example.
31  * The timeout tasks are timeouts that are executed after the specified
32  * timeout has elapsed.
33  *
34  * The SILC Scheduler is designed to be the sole main loop of the application
35  * so that the application does not need any other main loop.  However,
36  * SILC Scheduler does support running the scheduler only once, so that the
37  * scheduler does not block, and thus providing a possiblity that some
38  * external main loop is run over the SILC Scheduler.
39  *
40  * Typical application first initializes the scheduler and then registers
41  * the very first tasks to the scheduler and then run the scheduler.  After
42  * the scheduler's run function returns the application is considered to be
43  * ended.
44  *
45  * On WIN32 systems the SILC Scheduler is too designed to work as the main
46  * loop of the GUI application. It can handle all Windows messages and
47  * it dispatches them from the scheduler, and thus makes it possible to
48  * create GUI applications. The scheduler can also handle all kinds of
49  * WIN32 handles, this includes sockets created by the SILC Net API routines,
50  * WSAEVENT handle objects created by Winsock2 routines and arbitrary
51  * WIN32 HANDLE objects.
52  *
53  * The SILC Scheduler supports multi-threads as well. The actual scheduler
54  * must be run in single-thread but other threads may register new tasks
55  * and unregister old tasks.  However, it is enforced that the actual
56  * task is always run in the main thread.  The scheduler is context based
57  * which makes it possible to allocate several schedulers for one application.
58  * Since the scheduler must be run in single-thread, a multi-threaded
59  * application could be created by allocating own scheduler for each of the
60  * worker threads.
61  *
62  ***/
63 
64 #ifndef SILCSCHEDULE_H
65 #define SILCSCHEDULE_H
66 
67 /****s* silcutil/SilcScheduleAPI/SilcSchedule
68  *
69  * NAME
70  *
71  *    typedef struct SilcScheduleStruct *SilcSchedule;
72  *
73  * DESCRIPTION
74  *
75  *    This context is the actual Scheduler and is allocated by
76  *    the silc_schedule_init funtion.  The context is given as argument
77  *    to all silc_schedule_* functions.  It must be freed by the
78  *    silc_schedule_uninit function.
79  *
80  ***/
81 typedef struct SilcScheduleStruct *SilcSchedule;
82 
83 /****s* silcutil/SilcScheduleAPI/SilcTask
84  *
85  * NAME
86  *
87  *    typedef struct SilcTaskStruct *SilcTask;
88  *
89  * DESCRIPTION
90  *
91  *    This object represents one task in the scheduler.  It is allocated
92  *    by the silc_schedule_task_add function and freed by one of the
93  *    silc_schedule_task_del* functions.
94  *
95  ***/
96 typedef struct SilcTaskStruct *SilcTask;
97 
98 /****d* silcutil/SilcScheduleAPI/SilcTaskEvent
99  *
100  * NAME
101  *
102  *    typedef enum { ... } SilcTaskEvent;
103  *
104  * DESCRIPTION
105  *
106  *    SILC Task event types.  The event type indicates the occurred
107  *    event of the task.  This type will be given as argument to the
108  *    SilcTaskCallback function to indicate the event for the caller.
109  *    The SILC_TASK_READ and SILC_TASK_WRITE may be set by the caller
110  *    of the silc_schedule_set_listen_fd, if the caller needs to control
111  *    the events for the task. The SILC_TASK_EXPIRE is set always only
112  *    by the scheduler when timeout expires for timeout task.  The
113  *    SILC_TASK_INTERRUPT is set for signal callback.
114  *
115  * SOURCE
116  */
117 typedef enum {
118   SILC_TASK_READ         = 0x0001,	         /* Reading */
119   SILC_TASK_WRITE        = 0x0002,		 /* Writing */
120   SILC_TASK_EXPIRE       = 0x0004,		 /* Timeout */
121   SILC_TASK_INTERRUPT    = 0x0008,		 /* Signal */
122 } SilcTaskEvent;
123 /***/
124 
125 /****f* silcutil/SilcScheduleAPI/SilcTaskCallback
126  *
127  * SYNOPSIS
128  *
129  *    typedef void (*SilcTaskCallback)(SilcSchedule schedule,
130  *                                     void *app_context,
131  *                                     SilcTaskEvent type, SilcUInt32 fd,
132  *                                     void *context);
133  *
134  * DESCRIPTION
135  *
136  *    The task callback function.  This function will be called by the
137  *    scheduler when some event of the task is performed.  For example,
138  *    when data is available from the connection this will be called.
139  *
140  *    The `schedule' is the scheduler context, the `type' is the indicated
141  *    event, the `fd' is the file descriptor of the task and the `context'
142  *    is a caller specified context. If multiple events occurred this
143  *    callback is called separately for all events.  The `app_context'
144  *    is application specific context that was given as argument to the
145  *    silc_schedule_init function.  If the task is timeout task then `fd'
146  *    is zero (0).
147  *
148  *    To specify task callback function in the application using the
149  *    SILC_TASK_CALLBACK macro is recommended.
150  *
151  *    The callback should not perform lenghty or blocking operations as
152  *    this would also block all other waiting tasks.  The task callback
153  *    should either handle the operation fast or issue an asynchronous
154  *    call (like to register 0 timeout task) to handle it later.
155  *
156  ***/
157 typedef void (*SilcTaskCallback)(SilcSchedule schedule, void *app_context,
158 				 SilcTaskEvent type, SilcUInt32 fd,
159 				 void *context);
160 
161 /****f* silcutil/SilcScheduleAPI/SilcTaskNotifyCb
162  *
163  * SYNOPSIS
164  *
165  *    typedef void (*SilcTaskNotifyCb)(SilcSchedule schedule,
166  *                                     SilcBool added, SilcTask task,
167  *                                     SilcBool fd_task, SilcUInt32 fd,
168  *                                     SilcTaskEvent event,
169  *                                     long seconds, long useconds,
170  *                                     void *context);
171  *
172  * DESCRIPTION
173  *
174  *    Task notify callback.  Callback of this type can be set to scheduler
175  *    by calling silc_schedule_set_notify and will be called whenever new
176  *    task is added or old task is removed.  If `added' is TRUE then `task'
177  *    is added to scheduler.  If `added' is FALSE then `task' will be removed
178  *    from the scheduler.  If `fd_task' is TRUE the `task' is file descriptor
179  *    task and has `fd' is its file descriptor.  If `fd_task' is FALSE then
180  *    the task is timeout task and `seconds' and `useconds' specify the
181  *    timeout.  The `context' is the context given to silc_schedule_set_notify.
182  *
183  * NOTES
184  *
185  *    The `schedule' is locked while this callback is called.  This means that
186  *    new tasks cannot be added or removed inside this callback.
187  *
188  *    When timeout task expires this callback is not called.  This is called
189  *    only when task is explicitly deleted from the scheduler.  Note that,
190  *    when timeout task expires it is removed from the scheduler and `task'
191  *    will become invalid.
192  *
193  *    If fd task changes its events, this will be called as if it was a new
194  *    task with different `event' mask.
195  *
196  ***/
197 typedef void (*SilcTaskNotifyCb)(SilcSchedule schedule,
198 				 SilcBool added, SilcTask task,
199 				 SilcBool fd_task, SilcUInt32 fd,
200 				 SilcTaskEvent event,
201 				 long seconds, long useconds,
202 				 void *app_context);
203 
204 /* Macros */
205 
206 /****d* silcutil/SilcScheduleAPI/SILC_ALL_TASKS
207  *
208  * NAME
209  *
210  *    #define SILC_ALL_TASKS ...
211  *
212  * DESCRIPTION
213  *
214  *    Marks for all tasks in the scheduler. This can be passed to
215  *    silc_schedule_task_del function to delete all tasks at once.
216  *
217  * SOURCE
218  */
219 #define SILC_ALL_TASKS ((SilcTask)1)
220 /***/
221 
222 /****d* silcutil/SilcScheduleAPI/SILC_TASK_CALLBACK
223  *
224  * NAME
225  *
226  *    #define SILC_TASK_CALLBACK ...
227  *
228  * DESCRIPTION
229  *
230  *    Generic macro to declare task callback functions. This defines a
231  *    function with name `func' as a task callback function.
232  *
233  * SOURCE
234  */
235 #define SILC_TASK_CALLBACK(func)					\
236 void func(SilcSchedule schedule, void *app_context, SilcTaskEvent type,	\
237 	  SilcUInt32 fd, void *context)
238 /***/
239 
240 /* Prototypes */
241 
242 #include "silcschedule_i.h"
243 
244 /****f* silcutil/SilcScheduleAPI/silc_schedule_init
245  *
246  * SYNOPSIS
247  *
248  *    SilcSchedule silc_schedule_init(int max_tasks, void *app_context);
249  *
250  * DESCRIPTION
251  *
252  *    Initializes the scheduler. This returns the scheduler context that
253  *    is given as argument usually to all silc_schedule_* functions.
254  *    The `app_context' is application specific context that is delivered
255  *    to all task callbacks. The caller must free that context.  The
256  *    'app_context' can be for example the application itself.
257  *
258  *    The `max_tasks' is the maximum number of file descriptor and socket
259  *    tasks in the scheduler.  Set value to 0 to use default.  Operating
260  *    system will enforce the final limit.  On some operating systems the
261  *    limit can be significantly increased when this function is called in
262  *    priviliged mode (as super user).
263  *
264  ***/
265 SilcSchedule silc_schedule_init(int max_tasks, void *app_context);
266 
267 /****f* silcutil/SilcScheduleAPI/silc_schedule_uninit
268  *
269  * SYNOPSIS
270  *
271  *    SilcBool silc_schedule_uninit(SilcSchedule schedule);
272  *
273  * DESCRIPTION
274  *
275  *    Uninitializes the scheduler. This is called when the program is ready
276  *    to end. This removes all tasks from the scheduler. Returns FALSE if the
277  *    scheduler could not be uninitialized. This happens when the scheduler
278  *    is still valid and silc_schedule_stop has not been called.
279  *
280  ***/
281 SilcBool silc_schedule_uninit(SilcSchedule schedule);
282 
283 /****f* silcutil/SilcScheduleAPI/silc_schedule_stop
284  *
285  * SYNOPSIS
286  *
287  *    void silc_schedule_stop(SilcSchedule schedule);
288  *
289  * DESCRIPTION
290  *
291  *    Stops the scheduler even if it is not supposed to be stopped yet.
292  *    After calling this, one must call silc_schedule_uninit (after the
293  *    silc_schedule has returned).  After this is called it is guaranteed
294  *    that next time the scheduler enters the main loop it will be stopped.
295  *    However, untill it enters the main loop it will not detect that
296  *    it is stopped for example if this is called from another thread.
297  *
298  ***/
299 void silc_schedule_stop(SilcSchedule schedule);
300 
301 /****f* silcutil/SilcScheduleAPI/silc_schedule
302  *
303  * SYNOPSIS
304  *
305  *    void silc_schedule(SilcSchedule schedule);
306  *
307  * DESCRIPTION
308  *
309  *    The SILC scheduler.  The program will run inside this function.
310  *    When this returns the program is to be ended.  Before this function
311  *    can be called, one must call silc_schedule_init function.
312  *
313  * NOTES
314  *
315  *    On Windows this will block the calling thread but will continue
316  *    to dispatch window messages, and thus can be used as the main loop
317  *    of the program.
318  *
319  *    On Symbian this will block the calling thread.  The Symbian Active
320  *    Scheduler must be running before calling this function.
321  *
322  ***/
323 void silc_schedule(SilcSchedule schedule);
324 
325 /****f* silcutil/SilcScheduleAPI/silc_schedule_one
326  *
327  * SYNOPSIS
328  *
329  *    SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs);
330  *
331  * DESCRIPTION
332  *
333  *    Same as the silc_schedule but runs the scheduler only one round
334  *    and then returns.  This function is handy when the SILC scheduler
335  *    is used inside some other external scheduler, for example.  If
336  *    the `timeout_usecs' is non-negative a timeout will be added to the
337  *    scheduler.  The function will not return in this timeout unless
338  *    some other event occurs.
339  *
340  *    Typically this would be called from a timeout or idle task
341  *    periodically (typically from 5-50 ms) to schedule SILC tasks.  In
342  *    this case the `timeout_usecs' is usually 0 to make the function
343  *    return immediately.
344  *
345  ***/
346 SilcBool silc_schedule_one(SilcSchedule schedule, int timeout_usecs);
347 
348 /****f* silcutil/SilcScheduleAPI/silc_schedule_wakeup
349  *
350  * SYNOPSIS
351  *
352  *    void silc_schedule_wakeup(SilcSchedule schedule);
353  *
354  * DESCRIPTION
355  *
356  *    Wakes up the scheduler. This is may be used in multi-threaded
357  *    environments where threads may add new tasks or remove old tasks
358  *    from the scheduler. This is called to wake up the scheduler in the
359  *    main thread so that it detects the changes in the scheduler.
360  *    If threads support is not compiled in this function has no effect.
361  *
362  ***/
363 void silc_schedule_wakeup(SilcSchedule schedule);
364 
365 /****f* silcutil/SilcScheduleAPI/silc_schedule_get_context
366  *
367  * SYNOPSIS
368  *
369  *    void *silc_schedule_get_context(SilcSchedule schedule);
370  *
371  * DESCRIPTION
372  *
373  *    Returns the application specific context that was saved into the
374  *    scheduler in silc_schedule_init function.  The context is also
375  *    returned to application in the SilcTaskCallback, but this function
376  *    may be used to get it as well if needed.
377  *
378  ***/
379 void *silc_schedule_get_context(SilcSchedule schedule);
380 
381 /****f* silcutil/SilcScheduleAPI/silc_schedule_set_notify
382  *
383  * SYNOPSIS
384  *
385  *    void silc_schedule_set_notify(SilcSchedule schedule,
386  *                                  SilcTaskNotifyCb notify, void *context);
387  *
388  * DESCRIPTION
389  *
390  *    Set notify callback to scheduler.  The `notify' will be called whenever
391  *    task is added to or deleted from scheduler.
392  *
393  ***/
394 void silc_schedule_set_notify(SilcSchedule schedule,
395 			      SilcTaskNotifyCb notify, void *context);
396 
397 /****f* silcutil/SilcScheduleAPI/silc_schedule_task_add_fd
398  *
399  * SYNOPSIS
400  *
401  *    SilcTask
402  *    silc_schedule_task_add_fd(SilcSchedule schedule, SilcUInt32 fd,
403  *                              SilcTaskCallback callback, void *context);
404  *
405  * DESCRIPTION
406  *
407  *    Add file descriptor task to scheduler.  The `fd' may be either real
408  *    file descriptor, socket or on some platforms an opaque file descriptor
409  *    handle.  To receive events for the file descriptor set the correct
410  *    request events with silc_schedule_set_listen_fd function.
411  *
412  *    The task will be initially set for SILC_TASK_READ events.  Setting that
413  *    event immediately after this call returns is not necessary.
414  *
415  *    This returns the new task or NULL on error.  If a task with `fd' has
416  *    already been added this will return the existing task pointer.
417  *
418  ***/
419 #define silc_schedule_task_add_fd(schedule, fd, callback, context)	\
420   silc_schedule_task_add(schedule, fd, callback, context, 0, 0,	SILC_TASK_FD)
421 
422 /****f* silcutil/SilcScheduleAPI/silc_schedule_task_add_timeout
423  *
424  * SYNOPSIS
425  *
426  *    SilcTask
427  *    silc_schedule_task_add_timeout(SilcSchedule schedule,
428  *                                   SilcTaskCallback callback, void *context,
429  *                                   long seconds, long useconds);
430  *
431  * DESCRIPTION
432  *
433  *    Add timeout task to scheduler.  The `callback' will be called once
434  *    the specified timeout has elapsed.  The task will be removed from the
435  *    scheduler automatically once the task expires.  The event returned
436  *    to the `callback' is SILC_TASK_EXPIRE.  A task added with zero (0)
437  *    timeout will be executed immediately next time tasks are scheduled.
438  *
439  ***/
440 #define silc_schedule_task_add_timeout(schedule, callback, context, s, u) \
441   silc_schedule_task_add(schedule, 0, callback, context, s, u,		\
442                          SILC_TASK_TIMEOUT)
443 
444 /****f* silcutil/SilcScheduleAPI/silc_schedule_task_add_signal
445  *
446  * SYNOPSIS
447  *
448  *    SilcTask
449  *    silc_schedule_task_add_signal(SilcSchedule schedule, int signal,
450  *                                  SilcTaskCallback callback, void *context);
451  *
452  * DESCRIPTION
453  *
454  *    Add platform specific process signal handler to scheduler.  On Unix
455  *    systems the `signal' is one of the signal specified in signal(7).  On
456  *    other platforms this function may not be available at all, and has no
457  *    effect when called.  The event delivered to the `callback' is
458  *    SILC_TASK_INTERRUPT.
459  *
460  * NOTES
461  *
462  *    One signal may be registered only one callback.  Adding second callback
463  *    for signal that already has one will fail.
464  *
465  *    This function always returns NULL.  To remove signal from scheduler by
466  *    the signal call silc_schedule_task_del_by_fd.
467  *
468  ***/
469 #define silc_schedule_task_add_signal(schedule, sig, callback, context) \
470   silc_schedule_task_add(schedule, sig, callback, context, 0, 0,	\
471 			 SILC_TASK_SIGNAL)
472 
473 /****f* silcutil/SilcScheduleAPI/silc_schedule_task_del
474  *
475  * SYNOPSIS
476  *
477  *    SilcBool silc_schedule_task_del(SilcSchedule schedule, SilcTask task);
478  *
479  * DESCRIPTION
480  *
481  *    Deletes the `task' from the scheduler indicated by the `schedule'.
482  *    After deleting the task it is guaranteed that the task callback
483  *    will not be called. If the `task' is SILC_ALL_TASKS then all
484  *    tasks is removed from the scheduler.  Returns always TRUE.
485  *
486  *    It is safe to call this function in any place. Tasks may be removed
487  *    in task callbacks (including in the task's own task callback) and
488  *    in multi-threaded environment in other threads as well.
489  *
490  ***/
491 SilcBool silc_schedule_task_del(SilcSchedule schedule, SilcTask task);
492 
493 /****f* silcutil/SilcScheduleAPI/silc_schedule_task_del_by_fd
494  *
495  * SYNOPSIS
496  *
497  *    SilcBool silc_schedule_task_del_by_fd(SilcSchedule schedule,
498  *                                          SilcUInt32 fd);
499  *
500  * DESCRIPTION
501  *
502  *    Deletes a task from the scheduler by the specified `fd'.  Returns
503  *    FALSE if such fd task does not exist.
504  *
505  *    It is safe to call this function in any place. Tasks may be removed
506  *    in task callbacks (including in the task's own task callback) and
507  *    in multi-threaded environment in other threads as well.
508  *
509  ***/
510 SilcBool silc_schedule_task_del_by_fd(SilcSchedule schedule, SilcUInt32 fd);
511 
512 /****f* silcutil/SilcScheduleAPI/silc_schedule_task_del_by_callback
513  *
514  * SYNOPSIS
515  *
516  *    SilcBool silc_schedule_task_del_by_callback(SilcSchedule schedule,
517  *                                                SilcTaskCallback callback);
518  *
519  * DESCRIPTION
520  *
521  *    Deletes a task from the scheduler by the specified `callback' task
522  *    callback function.  Returns FALSE if such task with such callback
523  *    does not exist.
524  *
525  *    It is safe to call this function in any place. Tasks may be removed
526  *    in task callbacks (including in the task's own task callback) and
527  *    in multi-threaded environment in other threads as well.
528  *
529  ***/
530 SilcBool silc_schedule_task_del_by_callback(SilcSchedule schedule,
531 					    SilcTaskCallback callback);
532 
533 /****f* silcutil/SilcScheduleAPI/silc_schedule_task_del_by_context
534  *
535  * SYNOPSIS
536  *
537  *    SilcBool silc_schedule_task_del_by_context(SilcSchedule schedule,
538  *                                               void *context);
539  *
540  * DESCRIPTION
541  *
542  *    Deletes a task from the scheduler by the specified `context'.  Returns
543  *    FALSE if such task with such context does not exist.
544  *
545  *    It is safe to call this function in any place. Tasks may be removed
546  *    in task callbacks (including in the task's own task callback) and
547  *    in multi-threaded environment in other threads as well.
548  *
549  ***/
550 SilcBool silc_schedule_task_del_by_context(SilcSchedule schedule,
551 					   void *context);
552 
553 /****f* silcutil/SilcScheduleAPI/silc_schedule_task_del_by_all
554  *
555  * SYNOPSIS
556  *
557  *    SilcBool silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
558  *                                           SilcTaskCallback callback,
559  *                                           void *context);
560  *
561  * DESCRIPTION
562  *
563  *    Deletes a task from the scheduler by the specified `fd', `callback'
564  *    and `context'.  Returns FALSE if such task does not exist.
565  *
566  *    It is safe to call this function in any place. Tasks may be removed
567  *    in task callbacks (including in the task's own task callback) and
568  *    in multi-threaded environment in other threads as well.
569  *
570  ***/
571 SilcBool silc_schedule_task_del_by_all(SilcSchedule schedule, int fd,
572 				       SilcTaskCallback callback,
573 				       void *context);
574 
575 /****f* silcutil/SilcScheduleAPI/silc_schedule_set_listen_fd
576  *
577  * SYNOPSIS
578  *
579  *    SilcBool silc_schedule_set_listen_fd(SilcSchedule schedule,
580  *                                         SilcUInt32 fd,
581  *                                         SilcTaskEvent mask,
582  *                                         SilcBool send_events);
583  *
584  * DESCRIPTION
585  *
586  *    Sets a file descriptor `fd' to be listened by the scheduler for
587  *    `mask' events.  To tell scheduler not to listen anymore for this
588  *    file descriptor call the silc_schedule_unset_listen_fd function.
589  *    When new task is created with silc_schedule_task_add the event
590  *    for the task's fd is initially set to SILC_TASK_READ. If you need
591  *    to control the task's fd's events you must call this function
592  *    whenever you need to change the events. This can be called multiple
593  *    times to change the events.
594  *
595  *    If the `send_events' is TRUE then this function sends the events
596  *    in `mask' to the application.  If FALSE then they are sent only
597  *    after the event occurs in reality.  In normal cases the `send_events'
598  *    is set to FALSE.
599  *
600  *    Returns FALSE if the operation could not performed and TRUE if it
601  *    was a success.
602  *
603  ***/
604 SilcBool silc_schedule_set_listen_fd(SilcSchedule schedule, SilcUInt32 fd,
605 				     SilcTaskEvent mask, SilcBool send_events);
606 
607 /****f* silcutil/SilcScheduleAPI/silc_schedule_get_fd_events
608  *
609  * SYNOPSIS
610  *
611  *    SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule,
612  *                                              SilcUInt32 fd);
613  *
614  * DESCRIPTION
615  *
616  *    Returns the file descriptor `fd' current requested events mask,
617  *    or 0 on error.
618  *
619  ***/
620 SilcTaskEvent silc_schedule_get_fd_events(SilcSchedule schedule,
621 					  SilcUInt32 fd);
622 
623 /****f* silcutil/SilcScheduleAPI/silc_schedule_unset_listen_fd
624  *
625  * SYNOPSIS
626  *
627  *    void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd);
628  *
629  * DESCRIPTION
630  *
631  *    Tells the scheduler not to listen anymore for the specified
632  *    file descriptor `fd'. No events will be detected for the `fd'
633  *    after calling this function.
634  *
635  ***/
636 void silc_schedule_unset_listen_fd(SilcSchedule schedule, SilcUInt32 fd);
637 
638 #endif
639