Added SILC Server library.
[silc.git] / lib / silcutil / silcfsm.h
1 /*
2
3   silcfsm.h
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 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
20 /****h* silcutil/SILC Finite State Machine
21  *
22  * DESCRIPTION
23  *
24  * SILC FSM Interface implements a finite state machine.  The FSM can be
25  * used to implement all kinds of machines and protocols.  The FSM supports
26  * also threads and can be synchronized by using FSM semaphores.  The FSM
27  * also supports real system threads.  It is possible to create new FSM
28  * thread and then execute in real system thread, if platform supports
29  * threads.
30  *
31  * The FSM provides semaphores because of their versatility.  The FSM
32  * semaphores can be used as a conditional variables and signallers, and
33  * also as a mutual exclusion locks to protect critical sections.  The FSM
34  * semaphores can safely be used to synchronize also FSM threads that are
35  * executed in real system threads.  This makes SILC FSM very effective
36  * tool to implement complex machines whether they are executed in single
37  * thread or in multiple threads.
38  *
39  ***/
40
41 #ifndef SILCFSM_H
42 #define SILCFSM_H
43
44 /****s* silcutil/SilcFSMAPI/SilcFSM
45  *
46  * NAME
47  *
48  *    typedef struct SilcFSMObject *SilcFSM;
49  *
50  * DESCRIPTION
51  *
52  *    The actual FSM context and is allocated with silc_fsm_alloc and
53  *    given as argument to all silc_fsm_* functions.  It is freed by
54  *    silc_fsm_free function.  It is also possible to use pre-allocated
55  *    FSM context by using SilcFSMStruct instead of SilcFSM.
56  *
57  ***/
58 typedef struct SilcFSMObject *SilcFSM;
59
60 /****s* silcutil/SilcFSMAPI/SilcFSMStruct
61  *
62  * NAME
63  *
64  *    typedef struct SilcFSMObject SilcFSMStruct;
65  *
66  * DESCRIPTION
67  *
68  *    The actual FSM context and can be used as pre-allocated FSM context,
69  *    instead of SilcFSM context.  This context is initialized with the
70  *    silc_fsm_init function.  It is uninitialized with silc_fsm_uninit.
71  *
72  ***/
73 typedef struct SilcFSMObject SilcFSMStruct;
74
75 /****s* silcutil/SilcFSMAPI/SilcFSMThread
76  *
77  * NAME
78  *
79  *    typedef struct SilcFSMObject *SilcFSMThread;
80  *
81  * DESCRIPTION
82  *
83  *    FSM thread context.  The SILC FSM supports threads, virtual machine
84  *    threads (inside FSM) and actual real system threads if platorm
85  *    supports them.  In a complex machine certain complex operations may
86  *    be desired to execute in a thread.  The SilcFSMThread is allocated
87  *    by silc_fsm_thread_alloc and feed by silc_fsm_free.  It is also
88  *    possible to use pre-allocated thread by using SilcFSMThreadStruct
89  *    instead of SilcFSMThread.
90  *
91  ***/
92 typedef struct SilcFSMObject *SilcFSMThread;
93
94 /****s* silcutil/SilcFSMAPI/SilcFSM
95  *
96  * NAME
97  *
98  *    typedef struct SilcFSMObject SilcFSMThreadStruct;
99  *
100  * DESCRIPTION
101  *
102  *    FSM thread context and can be used as a pre-allocated FSM thread context,
103  *    instead of SilcFSMThread context.  This context is initialized with the
104  *    silc_fsm_thread_init function.  It is uninitialized with the
105  *    silc_fsm_uninit function.
106  *
107  ***/
108 typedef struct SilcFSMObject SilcFSMThreadStruct;
109
110 /****d* silcutil/SilcFSMAPI/SilcFSMStatus
111  *
112  * NAME
113  *
114  *    typedef enum { ... } SilcFSMStatus;
115  *
116  * DESCRIPTION
117  *
118  *    Status values that the FSM state functions return.
119  *
120  * SOURCE
121  */
122 typedef enum {
123   SILC_FSM_CONTINUE,         /* Continue immediately to next state. */
124   SILC_FSM_WAIT,             /* Wait for some async call or timeout */
125   SILC_FSM_FINISH,           /* Finish state machine and call destructor
126                                 through scheduler */
127 } SilcFSMStatus;
128 /***/
129
130 /****f* silcutil/SilcFSMAPI/SilcFSMDestructor
131  *
132  * SYNOPSIS
133  *
134  *    typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context,
135  *                                      void *destructor_context);
136  *
137  * DESCRIPTION
138  *
139  *    The destructor callback that was set in silc_fsm_alloc or in
140  *    silc_fsm_init function.  It will be called when a state function
141  *    returns SILC_FSM_FINISH.  This function will be called through
142  *    the scheduler; it will not be called immediately after the state
143  *    function returns SILC_FSM_FINISH, but will be called later.
144  *    The `fsm' may be freed or uninitialized in this function.
145  *
146  ***/
147 typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context,
148                                   void *destructor_context);
149
150 /****f* silcutil/SilcFSMAPI/SilcFSMThreadDestructor
151  *
152  * SYNOPSIS
153  *
154  *    typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread,
155  *                                            void *thread_context,
156  *                                            void *destructor_context);
157  *
158  * DESCRIPTION
159  *
160  *    The destructor callback that was set in silc_fsm_thread_alloc or in
161  *    silc_fsm_thread_init function.  It will be called when a state function
162  *    returns SILC_FSM_FINISH.  This function will be called through
163  *    the scheduler; it will not be called immediately after the state
164  *    function returns SILC_FSM_FINISH, but will be called later.  The
165  *    `thread' may be freed or uninitialized in this function.
166  *
167  * NOTES
168  *
169  *    Even if the `thread' was executed in real system thread, this callback
170  *    is always received in the main machine thread, not in the created
171  *    thread.
172  *
173  ***/
174 typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread,
175                                         void *thread_context,
176                                         void *destructor_context);
177
178 /****d* silcutil/SilcFSMAPI/SILC_FSM_STATE
179  *
180  * NAME
181  *
182  *    #define SILC_FSM_STATE(name)
183  *
184  * DESCRIPTION
185  *
186  *    This macro is used to declare a FSM state function.  The `fsm' is
187  *    the SilcFSM or SilcFSMThread context, the `fsm_context' is the context
188  *    given as argument to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_init,
189  *    or silc_fsm_thread_alloc function.  The `state_context' is the optional
190  *    state specific context set with silc_fsm_set_state_context function.
191  *
192  * SOURCE
193  */
194 #define SILC_FSM_STATE(name)                                            \
195 SilcFSMStatus name(struct SilcFSMObject *fsm, void *fsm_context,        \
196                    void *state_context)
197 /***/
198
199 /* State function callback */
200 typedef SilcFSMStatus (*SilcFSMStateCallback)(struct SilcFSMObject *fsm,
201                                               void *fsm_context,
202                                               void *state_context);
203
204 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL
205  *
206  * NAME
207  *
208  *    SILC_FSM_CALL(function)
209  *
210  * DESCRIPTION
211  *
212  *    Macro used to call asynchronous calls from state function.  If the
213  *    call is not really asynchronous then this will cause the machine to
214  *    directly proceed to next state.  If the call is truly asynchronous
215  *    then this will set the machine to wait state.  The silc_fsm_next
216  *    must be called before this macro, so that the next state is set.
217  *
218  * NOTES
219  *
220  *    The state function returns in this macro.
221  *
222  * EXAMPLE
223  *
224  *    // Simple example
225  *    silc_fsm_next(fsm, some_next_state);
226  *    SILC_FSM_CALL(silc_some_async_call(server, some_callback, context));
227  *
228  *    // More complex example
229  *    silc_fsm_next(fsm, some_next_state);
230  *    SILC_FSM_CALL((some_context->operation =
231  *                   silc_some_async_call(server, some_callback, context)));
232  *
233  ***/
234 #define SILC_FSM_CALL(function)                 \
235 do {                                            \
236   assert(!silc_fsm_set_call(fsm, TRUE));        \
237   function;                                     \
238   if (!silc_fsm_set_call(fsm, FALSE))           \
239     return SILC_FSM_CONTINUE;                   \
240   return SILC_FSM_WAIT;                         \
241 } while(0)
242
243 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE
244  *
245  * NAME
246  *
247  *    SILC_FSM_CALL_CONTINUE(fsm)
248  *
249  * DESCRIPTION
250  *
251  *    Macro used to proceed after asynchornous call.  This is called in the
252  *    callback of the asynchronous call to continue in the state machine.
253  *
254  * EXAMPLE
255  *
256  *    void some_callback(void *context) {
257  *      SilcFSM fsm = context;
258  *      ...
259  *      // Continue to the next state
260  *      SILC_FSM_CALL_CONTINUE(fsm);
261  *    }
262  *
263  ***/
264 #define SILC_FSM_CALL_CONTINUE(fsm)             \
265 do {                                            \
266   if (!silc_fsm_set_call(fsm, FALSE))           \
267     silc_fsm_continue(fsm);                     \
268 } while(0)
269
270 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE_SYNC
271  *
272  * NAME
273  *
274  *    SILC_FSM_CALL_CONTINUE_SYNC(fsm)
275  *
276  * DESCRIPTION
277  *
278  *    Macro used to proceed after asynchornous call.  This is called in the
279  *    callback of the asynchronous call to continue in the state machine.
280  *    This continues to the next state synchronously, not through the
281  *    scheduler.
282  *
283  * EXAMPLE
284  *
285  *    void some_callback(void *context) {
286  *      SilcFSM fsm = context;
287  *      ...
288  *      // Continue to the next state immediately
289  *      SILC_FSM_CALL_CONTINUE_SYNC(fsm);
290  *    }
291  *
292  ***/
293 #define SILC_FSM_CALL_CONTINUE_SYNC(fsm)        \
294 do {                                            \
295   if (!silc_fsm_set_call(fsm, FALSE))           \
296     silc_fsm_continue_sync(fsm);                \
297 } while(0)
298
299 /****d* silcutil/SilcFSMAPI/SILC_FSM_THREAD_WAIT
300  *
301  * NAME
302  *
303  *    SILC_FSM_THREAD_WAIT(thread)
304  *
305  * DESCRIPTION
306  *
307  *    Macro used to wait for the `thread' to terminate.  The machine or
308  *    thread will be suspended while it is waiting for the thread to
309  *    terminate.
310  *
311  * NOTES
312  *
313  *    The state function returns in this macro.
314  *
315  *    This macro is the only way to safely make sure that the thread has
316  *    terminated by the time FSM continues from the waiting state.  Using
317  *    semaphores to signal from the thread before SILC_FSM_FINISH is returned
318  *    works with normal FSM threads, but especially with real system threads
319  *    it does not guarantee that the FSM won't continue before the thread has
320  *    actually terminated.  Usually this is not a problem, but it can be a
321  *    problem if the FSM is waiting to be freed or uninitialized.  In this
322  *    case using this macro is strongly recommended.
323  *
324  ***/
325 #define SILC_FSM_THREAD_WAIT(thread)            \
326 do {                                            \
327   silc_fsm_thread_wait(fsm, thread);            \
328   return SILC_FSM_WAIT;                         \
329 } while(0)
330
331 /****f* silcutil/SilcFSMAPI/silc_fsm_alloc
332  *
333  * SYNOPSIS
334  *
335  *    SilcFSM silc_fsm_alloc(void *fsm_context,
336  *                           SilcFSMDestructor destructor,
337  *                           void *destructor_context,
338  *                           SilcSchedule schedule);
339  *
340  * DESCRIPTION
341  *
342  *    Allocates SILC Finite State Machine context.  The `destructor' with
343  *    `destructor_context' will be called when the machines finishes.  The
344  *    caller must free the returned context with silc_fsm_free.  The
345  *    `fsm_context' is delivered to every FSM state function.  The `schedule'
346  *    is the caller's scheduler and the FSM will be run in the scheduler.
347  *
348  * EXAMPLE
349  *
350  *    SilcAsyncOperation silc_async_call(Callback callback, void *cb_context)
351  *    {
352  *      SilcAsyncOperation op;
353  *      SilcFSM fsm;
354  *      ...
355  *
356  *      // Allocate async operation so that caller can control us, like abort
357  *      op = silc_async_alloc(silc_async_call_abort, NULL, ourcontext);
358  *
359  *      // Start FSM
360  *      fsm = silc_fsm_alloc(ourcontext, fsm_destructor, ourcontext,
361  *                           schedule);
362  *      silc_fsm_start(fsm, first_state);
363  *      ...
364  *
365  *      // Return async operation for upper layer
366  *      return op;
367  *    }
368  *
369  ***/
370 SilcFSM silc_fsm_alloc(void *fsm_context,
371                        SilcFSMDestructor destructor,
372                        void *destructor_context,
373                        SilcSchedule schedule);
374
375 /****f* silcutil/SilcFSMAPI/silc_fsm_init
376  *
377  * SYNOPSIS
378  *
379  *    SilcBool silc_fsm_init(SilcFSM fsm,
380  *                           void *fsm_context,
381  *                           SilcFSMDestructor destructor,
382  *                           void *destructor_context,
383  *                           SilcSchedule schedule);
384  *
385  * DESCRIPTION
386  *
387  *    Initializes a pre-allocated SilcFSM context.  This call is equivalent
388  *    to silc_fsm_alloc except that this takes the pre-allocated context
389  *    as argument.  The silc_fsm_free must not be called if this was called.
390  *    Returns TRUE if the initialization is Ok or FALSE if error occurred.
391  *    This function does not allocate any memory.  The `schedule' is the
392  *    caller's scheduler and the FSM will be run in the scheduler.
393  *
394  * EXAMPLE
395  *
396  *    SilcFSMStruct fsm;
397  *
398  *    silc_fsm_init(&fsm, application, fsm_destructor, application, schedule);
399  *    silc_fsm_start(&fsm, first_state);
400  *
401  ***/
402 SilcBool silc_fsm_init(SilcFSM fsm,
403                        void *fsm_context,
404                        SilcFSMDestructor destructor,
405                        void *destructor_context,
406                        SilcSchedule schedule);
407
408 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_alloc
409  *
410  * SYNOPSIS
411  *
412  *    SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
413  *                                        void *thread_context,
414  *                                        SilcFSMThreadDestructor destructor,
415  *                                        void *destructor_context,
416  *                                        SilcBool real_thread);
417  *
418  * DESCRIPTION
419  *
420  *    Allocates FSM thread context.  The thread will be executed in the
421  *    FSM machine indicated by `fsm'.  The caller must free the returned
422  *    thread context with silc_fsm_free.  If the 'real_thread' is TRUE
423  *    then the thread will actually be executed in real thread, if platform
424  *    supports them.  The `thread_context' is delivered to every state
425  *    function in the thread.
426  *
427  * NOTES
428  *
429  *    Note the limitations on using `real_thread' boolean to indicate running
430  *    the FSM thread in a real system thread:
431  *
432  *    If the system does not support threads, then this function will revert
433  *    back to normal FSM threads.
434  *
435  *    If the `real_thread' is TRUE then FSM will allocate new SilcSchedule
436  *    for the FSM thread.  This is done because the SilcSchedule that the
437  *    `fsm' use cannot be used in the thread.  This is limitation in the
438  *    SilcSchedule implementation.  If you need scheduler in the real thread
439  *    it is strongly recommended that you use the SilcSchedule that is
440  *    allocated for the thread.  You can retrieve the SilcSchedule from the
441  *    thread using silc_fsm_get_schedule function.  Note that, the allocated
442  *    SilcSchedule will become invalid after the thread finishes.
443  *
444  *    You may still however use the original SilcSchedule if you wish.  In
445  *    this case note its limitation: you may only add and/or remove tasks,
446  *    tasks cannot be executed in the thread.  You will need to deliver the
447  *    original SilcSchedule to the thread in the `thread_context' if you wish
448  *    to use it.
449  *
450  *    If `real_thread' is FALSE then no limitations on what can be run in
451  *    the thread exist.  In this case silc_fsm_get_schedule will return
452  *    the SilcSchedule that was originally given to silc_fsm_alloc or
453  *    silc_fsm_init.
454  *
455  * EXAMPLE
456  *
457  *    SILC_FSM_STATE(silc_foo_state)
458  *    {
459  *      SilcFSMThread thread;
460  *      ...
461  *
462  *      // Execute the route lookup in thread
463  *      thread = silc_fsm_thread_alloc(fsm, fsm_context, NULL, NULL, FALSE);
464  *      silc_fsm_start(thread, silc_route_lookup_start);
465  *
466  *      // Wait here for the thread to terminate. Set the state where to go
467  *      // after the thread has terminated.
468  *      silc_fsm_next(fsm, silc_foo_route_lookup_finished);
469  *      SILC_FSM_THREAD_WAIT(thread);
470  *    }
471  *
472  ***/
473 SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
474                                     void *thread_context,
475                                     SilcFSMThreadDestructor destructor,
476                                     void *destructor_context,
477                                     SilcBool real_thread);
478
479 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_init
480  *
481  * SYNOPSIS
482  *
483  *    void silc_fsm_thread_init(SilcFSMThread thread,
484  *                              SilcFSM fsm,
485  *                              void *thread_context,
486  *                              SilcFSMThreadDestructor destructor,
487  *                              void *destructor_context,
488  *                              SilcBool real_thread);
489  *
490  * DESCRIPTION
491  *
492  *    Initializes a pre-allocated SilcFSMThread context.  This call is
493  *    equivalent to silc_fsm_thread_alloc except that this takes the
494  *    pre-allocated context as argument.  The silc_fsm_free must not be
495  *    called if this was called.  Returns TRUE if the initialization is Ok
496  *    or FALSE if error occurred.  If the `real_thread' is TRUE then the
497  *    thread will actually be executed in real thread, if platform supports
498  *    them.
499  *
500  * NOTES
501  *
502  *    See the notes from the silc_fsm_thread_alloc.
503  *
504  * EXAMPLE
505  *
506  *    SilcFSMThreadStruct thread;
507  *
508  *    silc_fsm_thread_init(&thread, fsm, application, NULL, NULL, FALSE);
509  *    silc_fsm_start(&thread, first_state);
510  *
511  ***/
512 void silc_fsm_thread_init(SilcFSMThread thread,
513                           SilcFSM fsm,
514                           void *thread_context,
515                           SilcFSMThreadDestructor destructor,
516                           void *destructor_context,
517                           SilcBool real_thread);
518
519 /****f* silcutil/SilcFSMAPI/silc_fsm_free
520  *
521  * SYNOPSIS
522  *
523  *    void silc_fsm_free(void *fsm);
524  *
525  * DESCRIPTION
526  *
527  *    Free the SILC FSM context that was allocated with silc_fsm_alloc,
528  *    or free the SILC FSM thread context that was allocated with
529  *    silc_fsm_thread_alloc.  This function is used with both SilcFSM
530  *    and SilcFSMThread contexts.
531  *
532  * NOTES
533  *
534  *    When freeing FSM, it must not have any active threads.
535  *
536  ***/
537 void silc_fsm_free(void *fsm);
538
539 /****f* silcutil/SilcFSMAPI/silc_fsm_uninit
540  *
541  * SYNOPSIS
542  *
543  *    void silc_fsm_uninit(void *fsm);
544  *
545  * DESCRIPTION
546  *
547  *    Uninitializes a pre-allocated SilcFSM or SilcFSMThread context.
548  *    If you used the function silc_fsm_init or silc_fsm_thread_init, call
549  *    this function to uninitialize it.  This function is used with both
550  *    SilcFSMStruct and SilcFSMThreadStruct contexts.
551  *
552  * NOTES
553  *
554  *    When uninitializing FSM, it must not have any active threads.
555  *
556  ***/
557 void silc_fsm_uninit(void *fsm);
558
559 /****f* silcutil/SilcFSMAPI/silc_fsm_start
560  *
561  * SYNOPSIS
562  *
563  *    void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
564  *
565  * DESCRIPTION
566  *
567  *    This function must be called after the SILC FSM context was created.
568  *    This actually starts the state machine.  Note that, the machine is
569  *    started later after this function returns.  The `start_state' is the
570  *    state where the machine or thread is started.  This function is used
571  *    with both SilcFSM and SilcFSMThread contexts.
572  *
573  * EXAMPLE
574  *
575  *    SilcFSM fsm;
576  *
577  *    fsm = silc_fsm_alloc(context, destructor, context, schedule);
578  *    silc_fsm_start(fsm, first_state);
579  *
580  ***/
581 void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
582
583 /****f* silcutil/SilcFSMAPI/silc_fsm_start_sync
584  *
585  * SYNOPSIS
586  *
587  *    void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
588  *
589  * DESCRIPTION
590  *
591  *    This function is same as silc_fsm_start, except that the FSM will
592  *    be started immediately inside this function.  After this function
593  *    returns the `start_state' has already been executed.  If the machine
594  *    is completely synchronous (no waiting used in the machine) then
595  *    the machine will have finished once this function returns.  Also
596  *    note that if the machine is completely synchronous the destructor
597  *    will also be called from inside this function.  This function is used
598  *    with both SilcFSM and SilcFSMThread contexts.
599  *
600  ***/
601 void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
602
603 /****f* silcutil/SilcFSMAPI/silc_fsm_next
604  *
605  * SYNOPSIS
606  *
607  *    void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
608  *
609  * DESCRIPTION
610  *
611  *    Set the next state to be executed.  If the state function that
612  *    call this function returns SILC_FSM_CONTINUE, the `next_state'
613  *    will be executed immediately.  This function must always be used
614  *    to set the next state in the machine or thread.  This function is
615  *    used with both SilcFSM and SilcFSMThread contexts.
616  *
617  * EXAMPLE
618  *
619  *    // Move to next state
620  *    silc_fsm_next(fsm, next_state);
621  *    return SILC_FSM_CONTINUE;
622  *
623  ***/
624 void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
625
626 /****f* silcutil/SilcFSMAPI/silc_fsm_next_later
627  *
628  * SYNOPSIS
629  *
630  *    void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
631  *                             SilcUInt32 seconds, SilcUInt32 useconds);
632  *
633  * DESCRIPTION
634  *
635  *    Set the next state to be executed later, at the specified time.
636  *    The SILC_FSM_WAIT must be returned in the state function if this
637  *    function is called.  If any other state is returned machine operation
638  *    is undefined.  The machine or thread will move to `next_state' after
639  *    the specified timeout.  This function is used with both SilcFSM and
640  *    SilcFSMThread contexts.
641  *
642  * NOTES
643  *
644  *    If both `seconds' and `useconds' are 0, the effect is same as calling
645  *    silc_fsm_next function, and SILC_FSM_CONTINUE must be returned.
646  *
647  * EXAMPLE
648  *
649  *    // Move to next state after 10 seconds
650  *    silc_fsm_next_later(fsm, next_state, 10, 0);
651  *    return SILC_FSM_WAIT;
652  *
653  ***/
654 void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
655                          SilcUInt32 seconds, SilcUInt32 useconds);
656
657 /****f* silcutil/SilcFSMAPI/silc_fsm_continue
658  *
659  * SYNOPSIS
660  *
661  *    void silc_fsm_continue(void *fsm);
662  *
663  * DESCRIPTION
664  *
665  *    Continues in the state machine from a SILC_FSM_WAIT state.  This can
666  *    be called from outside waiting FSM to continue to the next state.
667  *    This function can be used instead of SILC_FSM_CALL_CONTINUE macro
668  *    in case the SILC_FSM_CALL was not used.  This must not be used if
669  *    SILC_FSM_CALL was used.  This function is used with both SilcFSM and
670  *    SilcFSMThread contexts.
671  *
672  ***/
673 void silc_fsm_continue(void *fsm);
674
675 /****f* silcutil/SilcFSMAPI/silc_fsm_continue_sync
676  *
677  * SYNOPSIS
678  *
679  *    void silc_fsm_continue_sync(void *fsm);
680  *
681  * DESCRIPTION
682  *
683  *    Continues immediately in the state machine from a SILC_FSM_WAIT state.
684  *    This can be called from outside waiting FSM to immediately continue to
685  *    the next state.  This function can be used instead of the
686  *    SILC_FSM_CALL_CONTINUE_SYNC macro in case the SILC_FSM_CALL was not used.
687  *    This must not be used if SILC_FSM_CALL was used.  This function is used
688  *    with both SilcFSM and SilcFSMThread contexts.
689  *
690  ***/
691 void silc_fsm_continue_sync(void *fsm);
692
693 /****f* silcutil/SilcFSMAPI/silc_fsm_set_context
694  *
695  * SYNOPSIS
696  *
697  *    void silc_fsm_set_context(void *fsm, void *fsm_context);
698  *
699  * DESCRIPTION
700  *
701  *    Set new context for the `fsm'.  This function can be used to change
702  *    the context inside the `fsm', if needed.  This function is used with
703  *    both SilcFSM and SilcFSMThread contexts.  The context is the
704  *    `fsm_context' in the state function (SILC_FSM_STATE).
705  *
706  ***/
707 void silc_fsm_set_context(void *fsm, void *fsm_context);
708
709 /****f* silcutil/SilcFSMAPI/silc_fsm_get_context
710  *
711  * SYNOPSIS
712  *
713  *    void *silc_fsm_get_context(void *fsm);
714  *
715  * DESCRIPTION
716  *
717  *    Returns the context associated with the `fsm'.  It is the context that
718  *    was given to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_alloc or
719  *    silc_fsm_thread_init.  This function is used with both SilcFSM and
720  *    SilcFSMThread contexts.
721  *
722  ***/
723 void *silc_fsm_get_context(void *fsm);
724
725 /****f* silcutil/SilcFSMAPI/silc_fsm_set_state_context
726  *
727  * SYNOPSIS
728  *
729  *    void silc_fsm_set_state_context(void *fsm, void *state_context);
730  *
731  * DESCRIPTION
732  *
733  *    Set's a state specific context for the `fsm'.  This function can be
734  *    used to change the state context inside the `fsm', if needed.  This
735  *    function is used with both SilcFSM and SilcFSMThread contexts.  The
736  *    context is the `state_context' in the state function (SILC_FSM_STATE).
737  *
738  ***/
739 void silc_fsm_set_state_context(void *fsm, void *state_context);
740
741 /****f* silcutil/SilcFSMAPI/silc_fsm_get_state_context
742  *
743  * SYNOPSIS
744  *
745  *    void *silc_fsm_get_state_context(void *fsm);
746  *
747  * DESCRIPTION
748  *
749  *    Returns the state context associated with the `fsm'.  It is the context
750  *    that was set with silc_fsm_set_state_context function.  This function
751  *    is used with both SilcFSM and SilcFSMThread contexts.
752  *
753  ***/
754 void *silc_fsm_get_state_context(void *fsm);
755
756 /****f* silcutil/SilcFSMAPI/silc_fsm_get_schedule
757  *
758  * SYNOPSIS
759  *
760  *    SilcSchedule silc_fsm_get_schedule(void *fsm);
761  *
762  * DESCRIPTION
763  *
764  *    Returns the SilcSchedule that has been associated with the `fsm'.
765  *    If caller needs scheduler it may retrieve it with this function.  This
766  *    function is used with both SilcFSM and SilcFSMThread contexts.
767  *
768  *    If the `fsm' is thread and real system threads are being used, and this
769  *    is called from the thread, it will return the SilcSchedule that was
770  *    allocated by the FSM for the thread.  It is strongly recommended to
771  *    use this SilcSchedule if you are using real threads, and you need
772  *    scheduler in the thread.  Note that, once the thread finishes the
773  *    returned SilcSchedule becomes invalid.
774  *
775  *    In other times this returns the SilcSchedule pointer that was given
776  *    to silc_fsm_alloc or silc_fsm_init.
777  *
778  ***/
779 SilcSchedule silc_fsm_get_schedule(void *fsm);
780
781 /****f* silcutil/SilcFSMAPI/silc_fsm_get_machine
782  *
783  * SYNOPSIS
784  *
785  *    SilcFSM silc_fsm_get_machine(SilcFSMThread thread);
786  *
787  * DESCRIPTION
788  *
789  *    Returns the machine from the FSM thread indicated by `thread'.
790  *
791  ***/
792 SilcFSM silc_fsm_get_machine(SilcFSMThread thread);
793
794
795 /* FSM Semaphores */
796
797 /****s* silcutil/SilcFSMAPI/SilcFSMSema
798  *
799  * NAME
800  *
801  *    typedef struct SilcFSMSemaObject *SilcFSMSema;
802  *
803  * DESCRIPTION
804  *
805  *    The FSM semaphore context allocated with silc_fsm_sema_alloc.  The
806  *    caller must free it with silc_fsm_sema_free.  It is also possible
807  *    to use pre-allocated SilcFSMSemaStruct instead of SilcFSMSema context.
808  *
809  ***/
810 typedef struct SilcFSMSemaObject *SilcFSMSema;
811
812 /****s* silcutil/SilcFSMAPI/SilcFSMSemaStruct
813  *
814  * NAME
815  *
816  *    typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
817  *
818  * DESCRIPTION
819  *
820  *    The FSM semaphore context that can be used as pre-allocated context.
821  *    It is initialized with silc_fsm_sema_init.  It need not be
822  *    uninitialized.
823  *
824  ***/
825 typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
826
827 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_alloc
828  *
829  * SYNOPSIS
830  *
831  *    SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
832  *
833  * DESCRIPTION
834  *
835  *    Allocates FSM semaphore with initial value of `value'.  Semaphores are
836  *    counters for resources shared between machine and threads.  Semaphores
837  *    can be waited until the semaphore value is non-zero.  The FSM will be
838  *    suspended when waiting for semaphore.  When the semaphore is incremented
839  *    all that are waiting for the semaphore will be signalled and awaken.
840  *
841  *    Semaphores can be used to wait for example when thread terminates, or
842  *    when thread moves into a specific state, or to protect critical
843  *    sections.  The FSM semaphores can be used also in FSM threads that are
844  *    executed in real system threads.
845  *
846  *    Use the macros SILC_FSM_SEMA_WAIT and SILC_FSM_SEMA_TIMEDWAIT to wait
847  *    for semaphore.  Use the SILC_FSM_SEMA_POST macro to increment the
848  *    counter and wake up all waiters.
849  *
850  ***/
851 SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
852
853 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_init
854  *
855  * SYNOPSIS
856  *
857  *    void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
858  *
859  * DESCRIPTION
860  *
861  *    Initializes a pre-allocates semaphore context.  This call is
862  *    equivalent to silc_fsm_sema_alloc except this use the pre-allocated
863  *    context.  This fuction does not allocate any memory.
864  *
865  ***/
866 void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
867
868 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_free
869  *
870  * SYNOPSIS
871  *
872  *    void silc_fsm_sema_free(SilcFSMSema sema);
873  *
874  * DESCRIPTION
875  *
876  *    Free the semaphore allocated by silc_fsm_sema_alloc function.
877  *
878  ***/
879 void silc_fsm_sema_free(SilcFSMSema sema);
880
881 /****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_WAIT
882  *
883  * NAME
884  *
885  *    SILC_FSM_SEMA_WAIT(semaphore)
886  *
887  * DESCRIPTION
888  *
889  *    Macro used to wait for the `semaphore' to become non-zero.  The
890  *    machine will be suspended while it is waiting for the semaphore.
891  *    This macro can only be used in FSM state functions.  When the
892  *    semaphore is signalled the FSM will re-enter the current state (or
893  *    state that was set with silc_fsm_next before waiting).
894  *
895  * EXAMPLE
896  *
897  *    // Signalling example
898  *    ctx->sema = silc_fsm_sema_alloc(fsm, 0);
899  *    ...
900  *
901  *    SILC_FSM_STATE(silc_foo_state)
902  *    {
903  *      ...
904  *
905  *      // Wait here for async call to complete
906  *      SILC_FSM_SEMA_WAIT(ctx->async_sema);
907  *
908  *      // Async call completed
909  *      if (ctx->async_success == FALSE)
910  *        fatal(error);
911  *      ...
912  *    }
913  *
914  *    // Mutual exclusion example
915  *    ctx->lock = silc_fsm_sema_alloc(fsm, 1);
916  *    ...
917  *
918  *    SILC_FSM_STATE(silc_foo_state)
919  *    {
920  *      ...
921  *      SILC_FSM_SEMA_WAIT(ctx->lock);
922  *      very critical stuff...
923  *      SILC_FSM_SEMA_POST(ctx->lock);
924  *      ...
925  *    }
926  *
927  ***/
928 #define SILC_FSM_SEMA_WAIT(sema)                \
929 do {                                            \
930   if (silc_fsm_sema_wait(sema, fsm) == 0)       \
931     return SILC_FSM_WAIT;                       \
932 } while(0)
933
934 /****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_TIMEDWAIT
935  *
936  * NAME
937  *
938  *    SILC_FSM_SEMA_TIMEDWAIT(semaphore, seconds, useconds, timedout)
939  *
940  * DESCRIPTION
941  *
942  *    Macro used to wait for the `semaphore' to become non-zero, or until
943  *    the timeout specified by `seconds' and `useconds' has elapsed.  If
944  *    the timeout occurs before the semaphore becomes non-zero, the machine
945  *    will wakeup.  The `timedout' is SilcBool pointer and if it is
946  *    non-NULL indication of whether timeout occurred or not is saved to
947  *    the pointer.  This macro can only be used in FSM state functions.
948  *    When the semaphore is signalled or timedout the FSM will re-enter
949  *    the current state (or state that was set with silc_fsm_next before
950  *    waiting).
951  *
952  * EXAMPLE
953  *
954  *    SILC_FSM_STATE(silc_foo_state)
955  *    {
956  *      SilcBool timedout;
957  *      ...
958  *
959  *
960  *      // Wait here for async call to complete, or 10 seconds for timeout
961  *      SILC_FSM_SEMA_TIMEDWAIT(ctx->async_sema, 10, 0, &timedout);
962  *
963  *      // See if timeout occurred
964  *      if (timedout == TRUE)
965  *        fatal(error);
966  *
967  *      // Async call completed
968  *      if (ctx->async_success == FALSE)
969  *        fatal(error);
970  *      ...
971  *    }
972  *
973  ***/
974 #define SILC_FSM_SEMA_TIMEDWAIT(sema, seconds, useconds, ret_to)          \
975 do {                                                                      \
976   if (silc_fsm_sema_timedwait(sema, fsm, seconds, useconds, ret_to) == 0) \
977     return SILC_FSM_WAIT;                                                 \
978 } while(0)
979
980 /****f* silcutil/SilcFSMAPI/SILC_FSM_SEMA_POST
981  *
982  * SYNOPSIS
983  *
984  *    SILC_FSM_SEMA_POST(semaphore)
985  *
986  * DESCRIPTION
987  *
988  *    Increases the semaphore counter and awakens everybody that are
989  *    waiting for this semaphore.  This macro never blocks.  It can be
990  *    safely called at any place in state function and in asynchronous
991  *    callbacks or other functions.
992  *
993  * EXAMPLE
994  *
995  *    SILC_FSM_STATE(silc_foo_async_completion)
996  *    {
997  *      ...
998  *
999  *      // Notify all waiters
1000  *      ctx->async_success = TRUE;
1001  *      SILC_FSM_SEMA_POST(ctx->async_sema);
1002  *      ...
1003  *    }
1004  *
1005  ***/
1006 #define SILC_FSM_SEMA_POST(sema)                \
1007 do {                                            \
1008   silc_fsm_sema_post(sema);                     \
1009 } while(0)
1010
1011 #include "silcfsm_i.h"
1012
1013 #endif /* SILCFSM_H */