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