eca7440929ca0a57fa195a1317c3c9457adf1136
[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.
187  *
188  * SOURCE
189  */
190 #define SILC_FSM_STATE(name)                                            \
191 SilcFSMStatus name(struct SilcFSMObject *fsm, void *fsm_context)
192 /***/
193
194 /* State function callback */
195 typedef SilcFSMStatus (*SilcFSMStateCallback)(struct SilcFSMObject *fsm,
196                                               void *fsm_context);
197
198 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL
199  *
200  * NAME
201  *
202  *    SILC_FSM_CALL(function)
203  *
204  * DESCRIPTION
205  *
206  *    Macro used to call asynchronous calls from state function.  If the
207  *    call is not really asynchronous then this will cause the machine to
208  *    directly proceed to next state.  If the call is truly asynchronous
209  *    then this will set the machine to wait state.  The silc_fsm_next
210  *    must be called before this macro, so that the next state is set.
211  *
212  * NOTES
213  *
214  *    The state function returns in this macro.
215  *
216  * EXAMPLE
217  *
218  *    // Simple example
219  *    silc_fsm_next(fsm, some_next_state);
220  *    SILC_FSM_CALL(silc_some_async_call(server, some_callback, context));
221  *
222  *    // More complex example
223  *    silc_fsm_next(fsm, some_next_state);
224  *    SILC_FSM_CALL((some_context->operation =
225  *                   silc_some_async_call(server, some_callback, context)));
226  *
227  ***/
228 #define SILC_FSM_CALL(function)                 \
229 do {                                            \
230   assert(!silc_fsm_set_call(fsm, TRUE));        \
231   function;                                     \
232   if (!silc_fsm_set_call(fsm, FALSE))           \
233     return SILC_FSM_CONTINUE;                   \
234   return SILC_FSM_WAIT;                         \
235 } while(0)
236
237 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE
238  *
239  * NAME
240  *
241  *    SILC_FSM_CALL_CONTINUE(fsm)
242  *
243  * DESCRIPTION
244  *
245  *    Macro used to proceed after asynchornous call.  This is called in the
246  *    callback of the asynchronous call to continue in the state machine.
247  *
248  * EXAMPLE
249  *
250  *    void some_callback(void *context) {
251  *      SilcFSM fsm = context;
252  *      ...
253  *      // Continue to the next state
254  *      SILC_FSM_CALL_CONTINUE(fsm);
255  *    }
256  *
257  ***/
258 #define SILC_FSM_CALL_CONTINUE(fsm)             \
259 do {                                            \
260   if (!silc_fsm_set_call(fsm, FALSE))           \
261     silc_fsm_continue(fsm);                     \
262 } while(0)
263
264 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE
265  *
266  * NAME
267  *
268  *    SILC_FSM_CALL_CONTINUE_SYNC(fsm)
269  *
270  * DESCRIPTION
271  *
272  *    Macro used to proceed after asynchornous call.  This is called in the
273  *    callback of the asynchronous call to continue in the state machine.
274  *    This continues to the next state synchronously, not through the
275  *    scheduler.
276  *
277  * EXAMPLE
278  *
279  *    void some_callback(void *context) {
280  *      SilcFSM fsm = context;
281  *      ...
282  *      // Continue to the next state immediately
283  *      SILC_FSM_CALL_CONTINUE_SYNC(fsm);
284  *    }
285  *
286  ***/
287 #define SILC_FSM_CALL_CONTINUE_SYNC(fsm)        \
288 do {                                            \
289   if (!silc_fsm_set_call(fsm, FALSE))           \
290     silc_fsm_continue_sync(fsm);                \
291 } while(0)
292
293 /****d* silcutil/SilcFSMAPI/SILC_FSM_THREAD_WAIT
294  *
295  * NAME
296  *
297  *    SILC_FSM_THREAD_WAIT(thread)
298  *
299  * DESCRIPTION
300  *
301  *    Macro used to wait for the `thread' to terminate.  The machine or
302  *    thread will be suspended while it is waiting for the thread to
303  *    terminate.
304  *
305  * NOTES
306  *
307  *    The state function returns in this macro.
308  *
309  *    This macro is the only way to safely make sure that the thread has
310  *    terminated by the time FSM continues from the waiting state.  Using
311  *    semaphores to signal from the thread before SILC_FSM_FINISH is returned
312  *    works with normal FSM threads, but especially with real system threads,
313  *    it does not guarantee that the FSM won't continue before the thread has
314  *    actually terminated.  Usually this is not a problem, but it can be a
315  *    problem if the FSM is waiting to be freed or uninitialized.  In this
316  *    case using this macro is strongly recommended.
317  *
318  ***/
319 #define SILC_FSM_THREAD_WAIT(thread)            \
320 do {                                            \
321   silc_fsm_thread_wait(fsm, thread);            \
322   return SILC_FSM_WAIT;                         \
323 } while(0)
324
325 /****f* silcutil/SilcFSMAPI/silc_fsm_alloc
326  *
327  * SYNOPSIS
328  *
329  *    SilcFSM silc_fsm_alloc(void *fsm_context,
330  *                           SilcFSMDestructor destructor,
331  *                           void *destructor_context,
332  *                           SilcSchedule schedule);
333  *
334  * DESCRIPTION
335  *
336  *    Allocates SILC Finite State Machine context.  The `destructor' with
337  *    `destructor_context' will be called when the machines finishes.  The
338  *    caller must free the returned context with silc_fsm_free.  The
339  *    `fsm_context' is delivered to every FSM state function.  The `schedule'
340  *    is the caller's scheduler and the FSM will be run in the scheduler.
341  *
342  * EXAMPLE
343  *
344  *    SilcAsyncOperation silc_async_call(Callback callback, void *cb_context)
345  *    {
346  *      SilcAsyncOperation op;
347  *      SilcFSM fsm;
348  *      ...
349  *
350  *      // Allocate async operation so that caller can control us, like abort
351  *      op = silc_async_alloc(silc_async_call_abort, NULL, ourcontext);
352  *
353  *      // Start FSM
354  *      fsm = silc_fsm_alloc(ourcontext, fsm_destructor, ourcontext,
355  *                           schedule);
356  *      silc_fsm_start(fsm, first_state);
357  *      ...
358  *
359  *      // Return async operation for upper layer
360  *      return op;
361  *    }
362  *
363  ***/
364 SilcFSM silc_fsm_alloc(void *fsm_context,
365                        SilcFSMDestructor destructor,
366                        void *destructor_context,
367                        SilcSchedule schedule);
368
369 /****f* silcutil/SilcFSMAPI/silc_fsm_init
370  *
371  * SYNOPSIS
372  *
373  *    SilcBool silc_fsm_init(SilcFSM fsm,
374  *                       void *fsm_context,
375  *                       SilcFSMDestructor destructor,
376  *                       void *destructor_context,
377  *                       SilcSchedule schedule);
378  *
379  * DESCRIPTION
380  *
381  *    Initializes a pre-allocated SilcFSM context.  This call is equivalent
382  *    to silc_fsm_alloc except that this takes the pre-allocated context
383  *    as argument.  The silc_fsm_free must not be called if this was called.
384  *    Returns TRUE if the initialization is Ok or FALSE if error occurred.
385  *    This function does not allocate any memory.  The `schedule' is the
386  *    caller's scheduler and the FSM will be run in the scheduler.
387  *
388  * EXAMPLE
389  *
390  *    SilcFSMStruct fsm;
391  *
392  *    silc_fsm_init(&fsm, application, fsm_destructor, application, schedule);
393  *    silc_fsm_start(&fsm, first_state);
394  *
395  ***/
396 SilcBool silc_fsm_init(SilcFSM fsm,
397                    void *fsm_context,
398                    SilcFSMDestructor destructor,
399                    void *destructor_context,
400                    SilcSchedule schedule);
401
402 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_alloc
403  *
404  * SYNOPSIS
405  *
406  *    SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
407  *                                        void *thread_context,
408  *                                        SilcFSMThreadDestructor destructor,
409  *                                        void *destructor_context,
410  *                                        SilcBool real_thread);
411  *
412  * DESCRIPTION
413  *
414  *    Allocates FSM thread context.  The thread will be executed in the
415  *    FSM machine indicated by `fsm'.  The caller must free the returned
416  *    thread context with silc_fsm_free.  If the 'real_thread' is TRUE
417  *    then the thread will actually be executed in real thread, if platform
418  *    supports them.  The `thread_context' is delivered to every state
419  *    function in the thread.
420  *
421  * NOTES
422  *
423  *    Note the limitations on using `real_thread' boolean to indicate running
424  *    the FSM thread in a real system thread:
425  *
426  *    If the system does not support threads, then this function will revert
427  *    back to normal FSM threads.
428  *
429  *    If the `real_thread' is TRUE then FSM will allocate new SilcSchedule
430  *    for the FSM thread.  This is done because the SilcSchedule that the
431  *    `fsm' use cannot be used in the thread.  This is limitation in the
432  *    SilcSchedule implementation.  If you need scheduler in the real thread
433  *    it is strongly recommended that you use the SilcSchedule that is
434  *    allocated for the thread.  You can retrieve the SilcSchedule from the
435  *    thread using silc_fsm_get_schedule function.  Note that, the allocated
436  *    SilcSchedule will become invalid after the thread finishes.
437  *
438  *    You may still however use the original SilcSchedule if you wish.  In
439  *    this case note its limitation: you may only add and/or remove tasks,
440  *    tasks cannot be executed in the thread.  You will need to deliver the
441  *    original SilcSchedule to the thread in the `thread_context' if you wish
442  *    to use it.
443  *
444  *    If `real_thread' is FALSE then no limitations on what can be run in
445  *    the thread exist.  In this case silc_fsm_get_schedule will return
446  *    the SilcSchedule that was originally given to silc_fsm_alloc or
447  *    silc_fsm_init.
448  *
449  * EXAMPLE
450  *
451  *    SILC_FSM_STATE(silc_foo_state)
452  *    {
453  *      SilcFSMThread thread;
454  *      ...
455  *
456  *      // Execute the route lookup in thread
457  *      thread = silc_fsm_thread_alloc(fsm, fsm_context, NULL, NULL, FALSE);
458  *      silc_fsm_start(thread, silc_route_lookup_start);
459  *
460  *      // Wait here for the thread to terminate. Set the state where to go
461  *      // after the thread has terminated.
462  *      silc_fsm_next(fsm, silc_foo_route_lookup_finished);
463  *      SILC_FSM_THREAD_WAIT(thread);
464  *    }
465  *
466  ***/
467 SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
468                                     void *thread_context,
469                                     SilcFSMThreadDestructor destructor,
470                                     void *destructor_context,
471                                     SilcBool real_thread);
472
473 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_init
474  *
475  * SYNOPSIS
476  *
477  *    SilcBool silc_fsm_thread_init(SilcFSMThread thread,
478  *                              SilcFSM fsm,
479  *                              void *thread_context,
480  *                              SilcFSMThreadDestructor destructor,
481  *                              void *destructor_context,
482  *                              SilcBool real_thread);
483  *
484  * DESCRIPTION
485  *
486  *    Initializes a pre-allocated SilcFSMThread context.  This call is
487  *    equivalent to silc_fsm_thread_alloc except that this takes the
488  *    pre-allocated context as argument.  The silc_fsm_free must not be
489  *    called if this was called.  Returns TRUE if the initialization is Ok
490  *    or FALSE if error occurred.  If the `real_thread' is TRUE then the
491  *    thread will actually be executed in real thread, if platform supports
492  *    them.
493  *
494  * NOTES
495  *
496  *    See the notes from the silc_fsm_thread_alloc.
497  *
498  * EXAMPLE
499  *
500  *    SilcFSMThreadStruct thread;
501  *
502  *    silc_fsm_thread_init(&thread, fsm, application, NULL, NULL, FALSE);
503  *    silc_fsm_start(&thread, first_state);
504  *
505  ***/
506 SilcBool silc_fsm_thread_init(SilcFSMThread thread,
507                           SilcFSM fsm,
508                           void *thread_context,
509                           SilcFSMThreadDestructor destructor,
510                           void *destructor_context,
511                           SilcBool real_thread);
512
513 /****f* silcutil/SilcFSMAPI/silc_fsm_free
514  *
515  * SYNOPSIS
516  *
517  *    void silc_fsm_free(void *fsm);
518  *
519  * DESCRIPTION
520  *
521  *    Free the SILC FSM context that was allocated with silc_fsm_alloc,
522  *    or free the SILC FSM thread context that was allocated with
523  *    silc_fsm_thread_alloc.  This function is used with both SilcFSM
524  *    and SilcFSMThread contexts.
525  *
526  * NOTES
527  *
528  *    When freeing FSM, it must not have any active threads.
529  *
530  ***/
531 void silc_fsm_free(void *fsm);
532
533 /****f* silcutil/SilcFSMAPI/silc_fsm_uninit
534  *
535  * SYNOPSIS
536  *
537  *    void silc_fsm_uninit(void *fsm);
538  *
539  * DESCRIPTION
540  *
541  *    Uninitializes a pre-allocated SilcFSM or SilcFSMThread context.
542  *    If you used the function silc_fsm_init or silc_fsm_thread_init, call
543  *    this function to uninitialize it.  This function is used with both
544  *    SilcFSMStruct and SilcFSMThreadStruct contexts.
545  *
546  * NOTES
547  *
548  *    When uninitializing FSM, it must not have any active threads.
549  *
550  ***/
551 void silc_fsm_uninit(void *fsm);
552
553 /****f* silcutil/SilcFSMAPI/silc_fsm_start
554  *
555  * SYNOPSIS
556  *
557  *    void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
558  *
559  * DESCRIPTION
560  *
561  *    This function must be called after the SILC FSM context was created.
562  *    This actually starts the state machine.  Note that, the machine is
563  *    started later after this function returns.  The `start_state' is the
564  *    state where the machine or thread is started.  This function is used
565  *    with both SilcFSM and SilcFSMThread contexts.
566  *
567  * EXAMPLE
568  *
569  *    SilcFSM fsm;
570  *
571  *    fsm = silc_fsm_alloc(context, destructor, context, schedule);
572  *    silc_fsm_start(fsm, first_state);
573  *
574  ***/
575 void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
576
577 /****f* silcutil/SilcFSMAPI/silc_fsm_start_sync
578  *
579  * SYNOPSIS
580  *
581  *    void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
582  *
583  * DESCRIPTION
584  *
585  *    This function is same as silc_fsm_start, except that the FSM will
586  *    be started immediately inside this function.  After this function
587  *    returns the `start_state' has already been executed.  If the machine
588  *    is completely synchronous (no waiting used in the machine) then
589  *    the machine will have finished once this function returns.  Also
590  *    note that if the machine is completely synchronous the destructor
591  *    will also be called from inside this function.  This function is used
592  *    with both SilcFSM and SilcFSMThread contexts.
593  *
594  ***/
595 void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
596
597 /****f* silcutil/SilcFSMAPI/silc_fsm_next
598  *
599  * SYNOPSIS
600  *
601  *    void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
602  *
603  * DESCRIPTION
604  *
605  *    Set the next state to be executed.  If the state function that
606  *    call this function returns SILC_FSM_CONTINUE, the `next_state'
607  *    will be executed immediately.  This function must always be used
608  *    to set the next state in the machine or thread.  This function is
609  *    used with both SilcFSM and SilcFSMThread contexts.
610  *
611  * EXAMPLE
612  *
613  *    // Move to next state
614  *    silc_fsm_next(fsm, next_state);
615  *    return SILC_FSM_CONTINUE;
616  *
617  ***/
618 void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
619
620 /****f* silcutil/SilcFSMAPI/silc_fsm_next_later
621  *
622  * SYNOPSIS
623  *
624  *    void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
625  *                             SilcUInt32 seconds, SilcUInt32 useconds);
626  *
627  * DESCRIPTION
628  *
629  *    Set the next state to be executed later, at the specified time.
630  *    The SILC_FSM_WAIT must be returned in the state function if this
631  *    function is called.  If any other state is returned machine operation
632  *    is undefined.  The machine or thread will move to `next_state' after
633  *    the specified timeout.  This function is used with both SilcFSM and
634  *    SilcFSMThread contexts.
635  *
636  * EXAMPLE
637  *
638  *    // Move to next state after 10 seconds
639  *    silc_fsm_next_later(fsm, next_state, 10, 0);
640  *    return SILC_FSM_WAIT;
641  *
642  ***/
643 void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
644                          SilcUInt32 seconds, SilcUInt32 useconds);
645
646 /****f* silcutil/SilcFSMAPI/silc_fsm_get_context
647  *
648  * SYNOPSIS
649  *
650  *    void *silc_fsm_get_context(void *fsm);
651  *
652  * DESCRIPTION
653  *
654  *    Returns the context associated with the `fsm'.  It is the context that
655  *    was given to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_alloc or
656  *    silc_fsm_thread_init.  This function is used with both SilcFSM and
657  *    SilcFSMThread contexts.
658  *
659  ***/
660 void *silc_fsm_get_context(void *fsm);
661
662 /****f* silcutil/SilcFSMAPI/silc_fsm_set_context
663  *
664  * SYNOPSIS
665  *
666  *    void silc_fsm_set_context(void *fsm, void *fsm_context);
667  *
668  * DESCRIPTION
669  *
670  *    Set new context for the `fsm'.  This function can be used to change
671  *    the context inside the `fsm', if needed.  This function is used with
672  *    both SilcFSM and SilcFSMThread contexts.
673  *
674  ***/
675 void silc_fsm_set_context(void *fsm, void *fsm_context);
676
677 /****f* silcutil/SilcFSMAPI/silc_fsm_get_schedule
678  *
679  * SYNOPSIS
680  *
681  *    SilcSchedule silc_fsm_get_schedule(void *fsm);
682  *
683  * DESCRIPTION
684  *
685  *    Returns the SilcSchedule that has been associated with the `fsm'.
686  *    If caller needs scheduler it may retrieve it with this function.  This
687  *    function is used with both SilcFSM and SilcFSMThread contexts.
688  *
689  *    If the `fsm' is thread and real system threads are being used, and this
690  *    is called from the thread, it will return the SilcSchedule that was
691  *    allocated by the FSM for the thread.  It is strongly recommended to
692  *    use this SilcSchedule if you are using real threads, and you need
693  *    scheduler in the thread.  Note that, once the thread finishes the
694  *    returned SilcSchedule becomes invalid.
695  *
696  *    Every other time this returns the SilcSchedule pointer that was given
697  *    to silc_fsm_alloc or silc_fsm_init.
698  *
699  ***/
700 SilcSchedule silc_fsm_get_schedule(void *fsm);
701
702
703 /* FSM Semaphores */
704
705 /****s* silcutil/SilcFSMAPI/SilcFSMSema
706  *
707  * NAME
708  *
709  *    typedef struct SilcFSMSemaObject *SilcFSMSema;
710  *
711  * DESCRIPTION
712  *
713  *    The FSM semaphore context allocated with silc_fsm_sema_alloc.  The
714  *    caller must free it with silc_fsm_sema_free.  It is also possible
715  *    to use pre-allocated SilcFSMSemaStruct instead of SilcFSMSema context.
716  *
717  ***/
718 typedef struct SilcFSMSemaObject *SilcFSMSema;
719
720 /****s* silcutil/SilcFSMAPI/SilcFSMSemaStruct
721  *
722  * NAME
723  *
724  *    typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
725  *
726  * DESCRIPTION
727  *
728  *    The FSM semaphore context that can be used as pre-allocated context.
729  *    It is initialized with silc_fsm_sema_init.  It need not be
730  *    uninitialized.
731  *
732  ***/
733 typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
734
735 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_alloc
736  *
737  * SYNOPSIS
738  *
739  *    SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
740  *
741  * DESCRIPTION
742  *
743  *    Allocates FSM semaphore with initial value of `value'.  Semaphores are
744  *    counters for resources shared between machine and threads.  Semaphores
745  *    can be waited until the semaphore value is non-zero.  The FSM will be
746  *    suspended when waiting for semaphore.  When the semaphore is incremented
747  *    all that are waiting for the semaphore will be signalled and awaken.
748  *
749  *    Semaphores can be used to wait for example when thread terminates, or
750  *    when thread moves into a specific state, or to protect critical
751  *    sections.  The FSM semaphores can be used also in FSM threads that are
752  *    executed in real system threads.
753  *
754  *    Use the macros SILC_FSM_SEMA_WAIT and SILC_FSM_SEMA_TIMEDWAIT to wait
755  *    for semaphore.  Use the SILC_FSM_SEMA_POST macro to increment the
756  *    counter and wake up all waiters.
757  *
758  *    FSM semaphores are machine specific.  The context cannot be shared
759  *    between multiple machines.  The same context naturally can be shared
760  *    between the machine and its threads.
761  *
762  ***/
763 SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
764
765 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_init
766  *
767  * SYNOPSIS
768  *
769  *    void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
770  *
771  * DESCRIPTION
772  *
773  *    Initializes a pre-allocates semaphore context.  This call is
774  *    equivalent to silc_fsm_sema_alloc except this use the pre-allocated
775  *    context.  This fuction does not allocate any memory.
776  *
777  ***/
778 void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
779
780 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_free
781  *
782  * SYNOPSIS
783  *
784  *    void silc_fsm_sema_free(SilcFSMSema sema);
785  *
786  * DESCRIPTION
787  *
788  *    Free the semaphore allocated by silc_fsm_sema_alloc function.
789  *
790  ***/
791 void silc_fsm_sema_free(SilcFSMSema sema);
792
793 /****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_WAIT
794  *
795  * NAME
796  *
797  *    SILC_FSM_SEMA_WAIT(semaphore)
798  *
799  * DESCRIPTION
800  *
801  *    Macro used to wait for the `semaphore' to become non-zero.  The
802  *    machine will be suspended while it is waiting for the semaphore.
803  *    This macro can only be used in FSM state functions.  When the
804  *    semaphore is signalled the FSM will re-enter the current state (or
805  *    state that was set with silc_fsm_next before waiting).
806  *
807  * EXAMPLE
808  *
809  *    // Signalling example
810  *    ctx->sema = silc_fsm_sema_alloc(fsm, 0);
811  *    ...
812  *
813  *    SILC_FSM_STATE(silc_foo_state)
814  *    {
815  *      ...
816  *
817  *      // Wait here for async call to complete
818  *      SILC_FSM_SEMA_WAIT(ctx->async_sema);
819  *
820  *      // Async call completed
821  *      if (ctx->async_success == FALSE)
822  *        fatal(error);
823  *      ...
824  *    }
825  *
826  *    // Mutual exclusion example
827  *    ctx->lock = silc_fsm_sema_alloc(fsm, 1);
828  *    ...
829  *
830  *    SILC_FSM_STATE(silc_foo_state)
831  *    {
832  *      ...
833  *      SILC_FSM_SEMA_WAIT(ctx->lock);
834  *      very critical stuff...
835  *      SILC_FSM_SEMA_POST(ctx->lock);
836  *      ...
837  *    }
838  *
839  ***/
840 #define SILC_FSM_SEMA_WAIT(sema)                \
841 do {                                            \
842   if (silc_fsm_sema_wait(sema, fsm) == 0)       \
843     return SILC_FSM_WAIT;                       \
844 } while(0)
845
846 /****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_TIMEDWAIT
847  *
848  * NAME
849  *
850  *    SILC_FSM_SEMA_TIMEDWAIT(semaphore, seconds, useconds)
851  *
852  * DESCRIPTION
853  *
854  *    Macro used to wait for the `semaphore' to become non-zero, or until
855  *    the timeout specified by `seconds' and `useconds' has elapsed.  If
856  *    the timeout occurs before the semaphore becomes non-zero, the machine
857  *    will wakeup.  This macro can only be used in FSM state functions.
858  *    When the semaphore is signalled or timedout the FSM will re-enter
859  *    the current state (or state that was set with silc_fsm_next before
860  *    waiting).
861  *
862  * EXAMPLE
863  *
864  *    SILC_FSM_STATE(silc_foo_state)
865  *    {
866  *      ...
867  *
868  *      // Wait here for async call to complete, or 10 seconds for timeout
869  *      SILC_FSM_SEMA_TIMEDWAIT(ctx->async_sema, 10, 0);
870  *
871  *      // Async call completed or timeout occurred
872  *      if (ctx->async_success == FALSE)
873  *        fatal(error);
874  *      ...
875  *    }
876  *
877  ***/
878 #define SILC_FSM_SEMA_TIMEDWAIT(sema, seconds, useconds)                \
879 do {                                                                    \
880   if (silc_fsm_sema_timedwait(sema, fsm, seconds, useconds) == 0)       \
881     return SILC_FSM_WAIT;                                               \
882 } while(0)
883
884 /****f* silcutil/SilcFSMAPI/SILC_FSM_SEMA_POST
885  *
886  * SYNOPSIS
887  *
888  *    SILC_FSM_SEMA_POST(semaphore)
889  *
890  * DESCRIPTION
891  *
892  *    Increases the semaphore counter and awakens everybody that are
893  *    waiting for this semaphore.  This macro never blocks.  It can be
894  *    safely called at any place in state function and in asynchronous
895  *    callbacks or other functions.
896  *
897  * EXAMPLE
898  *
899  *    SILC_FSM_STATE(silc_foo_async_completion)
900  *    {
901  *      ...
902  *
903  *      // Notify all waiters
904  *      ctx->async_success = TRUE;
905  *      SILC_FSM_SEMA_POST(ctx->async_sema);
906  *      ...
907  *    }
908  *
909  ***/
910 #define SILC_FSM_SEMA_POST(sema)                \
911 do {                                            \
912   silc_fsm_sema_post(sema);                     \
913 } while(0)
914
915 #include "silcfsm_i.h"
916
917 #endif /* SILCFSM_H */