5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2005, 2006, 2007 Pekka Riikonen
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.
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.
20 /****h* silcutil/SILC Finite State Machine
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
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.
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
47 /****s* silcutil/SilcFSMAPI/SilcFSM
51 * typedef struct SilcFSMObject *SilcFSM;
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.
61 typedef struct SilcFSMObject *SilcFSM;
63 /****s* silcutil/SilcFSMAPI/SilcFSMStruct
67 * typedef struct SilcFSMObject SilcFSMStruct;
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.
76 typedef struct SilcFSMObject SilcFSMStruct;
78 /****s* silcutil/SilcFSMAPI/SilcFSMThread
82 * typedef struct SilcFSMObject *SilcFSMThread;
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.
95 typedef struct SilcFSMObject *SilcFSMThread;
97 /****s* silcutil/SilcFSMAPI/SilcFSMThreadStruct
101 * typedef struct SilcFSMObject SilcFSMThreadStruct;
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.
110 typedef struct SilcFSMObject SilcFSMThreadStruct;
112 /****d* silcutil/SilcFSMAPI/SILC_FSM_CONTINUE
116 * #define SILC_FSM_CONTINUE ...
120 * Moves to next state synchronously. This type is returned from state
121 * functions to immediately move to next state.
125 * SILC_FSM_STATE(silc_foo_state)
129 * // Move to next state now
130 * silc_fsm_next(fsm, silc_foo_next_state);
131 * return SILC_FSM_CONTINUE;
135 #if defined(SILC_DEBUG)
136 #define SILC_FSM_CONTINUE \
137 fsm->next_state(fsm, fsm->fsm_context, fsm->state_context);
139 #define SILC_FSM_CONTINUE SILC_FSM_ST_CONTINUE;
140 #endif /* SILC_DEBUG */
142 /****d* silcutil/SilcFSMAPI/SILC_FSM_YIELD
146 * #define SILC_FSM_YIELD ...
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.
156 #define SILC_FSM_YIELD SILC_FSM_ST_YIELD;
158 /****d* silcutil/SilcFSMAPI/SILC_FSM_WAIT
162 * #define SILC_FSM_WAIT ...
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.
171 #define SILC_FSM_WAIT SILC_FSM_ST_WAIT
173 /****d* silcutil/SilcFSMAPI/SILC_FSM_FINISH
177 * #define SILC_FSM_FINISH ...
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.
187 #define SILC_FSM_FINISH SILC_FSM_ST_FINISH
189 /****f* silcutil/SilcFSMAPI/SilcFSMDestructor
193 * typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context,
194 * void *destructor_context);
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.
206 typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context,
207 void *destructor_context);
209 /****f* silcutil/SilcFSMAPI/SilcFSMThreadDestructor
213 * typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread,
214 * void *thread_context,
215 * void *destructor_context);
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.
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
233 typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread,
234 void *thread_context,
235 void *destructor_context);
237 /****d* silcutil/SilcFSMAPI/SILC_FSM_STATE
241 * #define SILC_FSM_STATE(name)
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.
253 #define SILC_FSM_STATE(name) \
254 int name(struct SilcFSMObject *fsm, void *fsm_context, void *state_context)
257 /* State function callback */
258 typedef int (*SilcFSMStateCallback)(struct SilcFSMObject *fsm,
260 void *state_context);
262 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL
266 * SILC_FSM_CALL(function)
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.
278 * The state function returns in this macro.
283 * silc_fsm_next(fsm, some_next_state);
284 * SILC_FSM_CALL(silc_some_async_call(server, some_callback, context));
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)));
292 #define SILC_FSM_CALL(function) \
294 SILC_VERIFY(!silc_fsm_set_call(fsm, TRUE)); \
296 if (!silc_fsm_set_call(fsm, FALSE)) \
297 return SILC_FSM_CONTINUE; \
298 return SILC_FSM_WAIT; \
301 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE
305 * SILC_FSM_CALL_CONTINUE(fsm)
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.
314 * void some_callback(void *context) {
315 * SilcFSM fsm = context;
317 * // Continue to the next state
318 * SILC_FSM_CALL_CONTINUE(fsm);
322 #define SILC_FSM_CALL_CONTINUE(fsm) \
324 if (!silc_fsm_set_call(fsm, FALSE)) \
325 silc_fsm_continue(fsm); \
328 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE_SYNC
332 * SILC_FSM_CALL_CONTINUE_SYNC(fsm)
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
343 * void some_callback(void *context) {
344 * SilcFSM fsm = context;
346 * // Continue to the next state immediately
347 * SILC_FSM_CALL_CONTINUE_SYNC(fsm);
351 #define SILC_FSM_CALL_CONTINUE_SYNC(fsm) \
353 if (!silc_fsm_set_call(fsm, FALSE)) \
354 silc_fsm_continue_sync(fsm); \
357 /****d* silcutil/SilcFSMAPI/SILC_FSM_THREAD_WAIT
361 * SILC_FSM_THREAD_WAIT(thread)
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.
372 * The state function returns in this macro.
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.
384 #define SILC_FSM_THREAD_WAIT(thread) \
386 silc_fsm_thread_wait(fsm, thread); \
387 return SILC_FSM_WAIT; \
390 /****f* silcutil/SilcFSMAPI/silc_fsm_alloc
394 * SilcFSM silc_fsm_alloc(void *fsm_context,
395 * SilcFSMDestructor destructor,
396 * void *destructor_context,
397 * SilcSchedule schedule);
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 * Returns NULL if system is out of memory.
410 * SilcAsyncOperation silc_async_call(Callback callback, void *cb_context)
412 * SilcAsyncOperation op;
416 * // Allocate async operation so that caller can control us, like abort
417 * op = silc_async_alloc(silc_async_call_abort, NULL, ourcontext);
420 * fsm = silc_fsm_alloc(ourcontext, fsm_destructor, ourcontext,
422 * silc_fsm_start(fsm, first_state);
425 * // Return async operation for upper layer
430 SilcFSM silc_fsm_alloc(void *fsm_context,
431 SilcFSMDestructor destructor,
432 void *destructor_context,
433 SilcSchedule schedule);
435 /****f* silcutil/SilcFSMAPI/silc_fsm_init
439 * SilcBool silc_fsm_init(SilcFSM fsm,
441 * SilcFSMDestructor destructor,
442 * void *destructor_context,
443 * SilcSchedule schedule);
447 * Initializes a pre-allocated SilcFSM context. This call is equivalent
448 * to silc_fsm_alloc except that this takes the pre-allocated context
449 * as argument. The silc_fsm_free must not be called if this was called.
450 * Returns TRUE if the initialization is Ok or FALSE if error occurred.
451 * This function does not allocate any memory. The `schedule' is the
452 * caller's scheduler and the FSM will be run in the scheduler.
458 * silc_fsm_init(&fsm, application, fsm_destructor, application, schedule);
459 * silc_fsm_start(&fsm, first_state);
462 SilcBool silc_fsm_init(SilcFSM fsm,
464 SilcFSMDestructor destructor,
465 void *destructor_context,
466 SilcSchedule schedule);
468 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_alloc
472 * SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
473 * void *thread_context,
474 * SilcFSMThreadDestructor destructor,
475 * void *destructor_context,
476 * SilcBool real_thread);
480 * Allocates FSM thread context. The thread will be executed in the
481 * FSM machine indicated by `fsm'. The caller must free the returned
482 * thread context with silc_fsm_free. If the 'real_thread' is TRUE
483 * then the thread will actually be executed in real thread, if platform
484 * supports them. The `thread_context' is delivered to every state
485 * function in the thread. Returns NULL if the system is out of memory.
489 * If the system does not support threads, then this function will revert
490 * back to normal FSM threads.
492 * If the `real_thread' is TRUE then FSM will allocate new SilcSchedule
493 * for the FSM thread. If you need scheduler in the real thread it is
494 * strongly recommended that you use the SilcSchedule that is allocated
495 * for the thread. You can retrieve the SilcSchedule from the thread
496 * using silc_fsm_get_schedule function. Note that, the allocated
497 * SilcSchedule will become invalid after the thread finishes.
499 * If `real_thread' is FALSE the silc_fsm_get_schedule will return
500 * the SilcSchedule that was originally given to silc_fsm_alloc or
505 * SILC_FSM_STATE(silc_foo_state)
507 * SilcFSMThread thread;
510 * // Execute the route lookup in thread
511 * thread = silc_fsm_thread_alloc(fsm, fsm_context, NULL, NULL, FALSE);
512 * silc_fsm_start(thread, silc_route_lookup_start);
514 * // Wait here for the thread to terminate. Set the state where to go
515 * // after the thread has terminated.
516 * silc_fsm_next(fsm, silc_foo_route_lookup_finished);
517 * SILC_FSM_THREAD_WAIT(thread);
521 SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
522 void *thread_context,
523 SilcFSMThreadDestructor destructor,
524 void *destructor_context,
525 SilcBool real_thread);
527 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_init
531 * void silc_fsm_thread_init(SilcFSMThread thread,
533 * void *thread_context,
534 * SilcFSMThreadDestructor destructor,
535 * void *destructor_context,
536 * SilcBool real_thread);
540 * Initializes a pre-allocated SilcFSMThread context. This call is
541 * equivalent to silc_fsm_thread_alloc except that this takes the
542 * pre-allocated context as argument. The silc_fsm_free must not be
543 * called if this was called. If the `real_thread' is TRUE then the
544 * thread will actually be executed in real thread, if platform supports
549 * See the notes from the silc_fsm_thread_alloc.
553 * SilcFSMThreadStruct thread;
555 * silc_fsm_thread_init(&thread, fsm, application, NULL, NULL, FALSE);
556 * silc_fsm_start(&thread, first_state);
559 void silc_fsm_thread_init(SilcFSMThread thread,
561 void *thread_context,
562 SilcFSMThreadDestructor destructor,
563 void *destructor_context,
564 SilcBool real_thread);
566 /****f* silcutil/SilcFSMAPI/silc_fsm_free
570 * void silc_fsm_free(void *fsm);
574 * Free the SILC FSM context that was allocated with silc_fsm_alloc,
575 * or free the SILC FSM thread context that was allocated with
576 * silc_fsm_thread_alloc. This function is used with both SilcFSM
577 * and SilcFSMThread contexts.
581 * When freeing FSM, it must not have any active threads.
584 void silc_fsm_free(void *fsm);
586 /****f* silcutil/SilcFSMAPI/silc_fsm_start
590 * void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
594 * This function must be called after the SILC FSM context was created.
595 * This actually starts the state machine. Note that, the machine is
596 * started later after this function returns. The `start_state' is the
597 * state where the machine or thread is started. This function is used
598 * with both SilcFSM and SilcFSMThread contexts.
604 * fsm = silc_fsm_alloc(context, destructor, context, schedule);
605 * silc_fsm_start(fsm, first_state);
608 void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
610 /****f* silcutil/SilcFSMAPI/silc_fsm_start_sync
614 * void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
618 * This function is same as silc_fsm_start, except that the FSM will
619 * be started immediately inside this function. After this function
620 * returns the `start_state' has already been executed. If the machine
621 * is completely synchronous (no waiting used in the machine) then
622 * the machine will have finished once this function returns. Also
623 * note that if the machine is completely synchronous the destructor
624 * will also be called from inside this function. This function is used
625 * with both SilcFSM and SilcFSMThread contexts.
628 void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
630 /****f* silcutil/SilcFSMAPI/silc_fsm_next
634 * void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
638 * Set the next state to be executed. If the state function that
639 * call this function returns SILC_FSM_CONTINUE, the `next_state'
640 * will be executed immediately. If it returns SILC_FSM_YIELD it
641 * yields the thread and the `next_state' will be run after other
642 * threads have run first. This function must always be used to set
643 * the next state in the machine or thread. This function is used
644 * with both SilcFSM and SilcFSMThread contexts.
648 * // Move to next state
649 * silc_fsm_next(fsm, next_state);
650 * return SILC_FSM_CONTINUE;
653 void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
655 /****f* silcutil/SilcFSMAPI/silc_fsm_next_later
659 * void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
660 * SilcUInt32 seconds, SilcUInt32 useconds);
664 * Set the next state to be executed later, at the specified time.
665 * The SILC_FSM_WAIT must be returned in the state function if this
666 * function is called. If any other state is returned machine operation
667 * is undefined. The machine or thread will move to `next_state' after
668 * the specified timeout. This function is used with both SilcFSM and
669 * SilcFSMThread contexts.
673 * If both `seconds' and `useconds' are 0, the effect is same as calling
674 * silc_fsm_next function, and SILC_FSM_CONTINUE must be returned.
676 * If silc_fsm_continue or silc_fsm_continue_sync is called while the
677 * machine or thread is in SILC_FSM_WAIT state the timeout is automatically
678 * canceled and the state moves to the next state.
682 * // Move to next state after 10 seconds
683 * silc_fsm_next_later(fsm, next_state, 10, 0);
684 * return SILC_FSM_WAIT;
687 void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
688 SilcUInt32 seconds, SilcUInt32 useconds);
690 /****f* silcutil/SilcFSMAPI/silc_fsm_continue
694 * void silc_fsm_continue(void *fsm);
698 * Continues in the state machine from a SILC_FSM_WAIT state. This can
699 * be called from outside waiting FSM to continue to the next state.
700 * This function can be used instead of SILC_FSM_CALL_CONTINUE macro
701 * in case the SILC_FSM_CALL was not used. This must not be used if
702 * SILC_FSM_CALL was used. This function is used with both SilcFSM and
703 * SilcFSMThread contexts.
706 void silc_fsm_continue(void *fsm);
708 /****f* silcutil/SilcFSMAPI/silc_fsm_continue_sync
712 * void silc_fsm_continue_sync(void *fsm);
716 * Continues immediately in the state machine from a SILC_FSM_WAIT state.
717 * This can be called from outside waiting FSM to immediately continue to
718 * the next state. This function can be used instead of the
719 * SILC_FSM_CALL_CONTINUE_SYNC macro in case the SILC_FSM_CALL was not used.
720 * This must not be used if SILC_FSM_CALL was used. This function is used
721 * with both SilcFSM and SilcFSMThread contexts.
724 void silc_fsm_continue_sync(void *fsm);
726 /****f* silcutil/SilcFSMAPI/silc_fsm_finish
730 * void silc_fsm_finish(void *fsm);
734 * Finishes the `fsm'. This function may be used in case the FSM
735 * needs to be finished outside FSM states. Usually FSM is finished
736 * by returning SILC_FSM_FINISH from the state, but if this is not
737 * possible this function may be called. This function is used with
738 * both SilcFSM and SilcFSMThread contexts.
740 * If the `fsm' is a machine and it has running threads, the machine
741 * will fatally fail. The caller must first finish the threads and
745 void silc_fsm_finish(void *fsm);
747 /****f* silcutil/SilcFSMAPI/silc_fsm_set_context
751 * void silc_fsm_set_context(void *fsm, void *fsm_context);
755 * Set new context for the `fsm'. This function can be used to change
756 * the context inside the `fsm', if needed. This function is used with
757 * both SilcFSM and SilcFSMThread contexts. The context is the
758 * `fsm_context' in the state function (SILC_FSM_STATE).
761 void silc_fsm_set_context(void *fsm, void *fsm_context);
763 /****f* silcutil/SilcFSMAPI/silc_fsm_get_context
767 * void *silc_fsm_get_context(void *fsm);
771 * Returns the context associated with the `fsm'. It is the context that
772 * was given to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_alloc or
773 * silc_fsm_thread_init. This function is used with both SilcFSM and
774 * SilcFSMThread contexts.
777 void *silc_fsm_get_context(void *fsm);
779 /****f* silcutil/SilcFSMAPI/silc_fsm_set_state_context
783 * void silc_fsm_set_state_context(void *fsm, void *state_context);
787 * Set's a state specific context for the `fsm'. This function can be
788 * used to change the state context inside the `fsm', if needed. This
789 * function is used with both SilcFSM and SilcFSMThread contexts. The
790 * context is the `state_context' in the state function (SILC_FSM_STATE).
793 void silc_fsm_set_state_context(void *fsm, void *state_context);
795 /****f* silcutil/SilcFSMAPI/silc_fsm_get_state_context
799 * void *silc_fsm_get_state_context(void *fsm);
803 * Returns the state context associated with the `fsm'. It is the context
804 * that was set with silc_fsm_set_state_context function. This function
805 * is used with both SilcFSM and SilcFSMThread contexts.
808 void *silc_fsm_get_state_context(void *fsm);
810 /****f* silcutil/SilcFSMAPI/silc_fsm_get_schedule
814 * SilcSchedule silc_fsm_get_schedule(void *fsm);
818 * Returns the SilcSchedule that has been associated with the `fsm'.
819 * If caller needs scheduler it may retrieve it with this function. This
820 * function is used with both SilcFSM and SilcFSMThread contexts.
822 * If the `fsm' is thread and real system threads are being used, and this
823 * is called from the thread, it will return the SilcSchedule that was
824 * allocated by the FSM for the thread. It is strongly recommended to
825 * use this SilcSchedule if you are using real threads, and you need
826 * scheduler in the thread. Note that, once the thread finishes the
827 * returned SilcSchedule becomes invalid.
829 * In other times this returns the SilcSchedule pointer that was given
830 * to silc_fsm_alloc or silc_fsm_init.
833 SilcSchedule silc_fsm_get_schedule(void *fsm);
835 /****f* silcutil/SilcFSMAPI/silc_fsm_get_machine
839 * SilcFSM silc_fsm_get_machine(SilcFSMThread thread);
843 * Returns the machine from the FSM thread indicated by `thread'.
846 SilcFSM silc_fsm_get_machine(SilcFSMThread thread);
848 /****f* silcutil/SilcFSMAPI/silc_fsm_is_started
852 * SilcBool silc_fsm_is_started(void *fsm);
856 * Returns TRUE if the machine or thread `fsm' has been started and has
857 * not been finished yet. This function is used with both SilcFSM and
858 * SilcFSMThread contexts.
861 SilcBool silc_fsm_is_started(void *fsm);
865 /****s* silcutil/SilcFSMAPI/SilcFSMEvent
869 * typedef struct SilcFSMEventObject *SilcFSMEvent;
873 * The FSM event context allocated with silc_fsm_event_alloc. The
874 * caller must free it with silc_fsm_event_free. It is also possible
875 * to use pre-allocated SilcFSMEventStruct instead of SilcFSMEvent context.
878 typedef struct SilcFSMEventObject *SilcFSMEvent;
880 /****s* silcutil/SilcFSMAPI/SilcFSMEventStruct
884 * typedef struct SilcFSMEventObject SilcFSMEventStruct;
888 * The FSM event context that can be used as pre-allocated context.
889 * It is initialized with silc_fsm_event_init. It need not be
893 typedef struct SilcFSMEventObject SilcFSMEventStruct;
895 /****f* silcutil/SilcFSMAPI/silc_fsm_event_alloc
899 * SilcFSMEvent silc_fsm_event_alloc(SilcFSM fsm);
903 * Allocates asynchronous FSM event. FSM events are asynchronous events
904 * that can be waited and signalled. They can be used as condition
905 * variables and signallers. They can be used for example to wait that
906 * some event happens, some thread moves to a specific state or similar.
907 * The FSM Events may also be used in FSM threads that are executed in
908 * real system threads. It is safe to wait and signal the event from
909 * threads. The `fsm' must be the machine, not a thread. Returns NULL
910 * if system is out of memory or `fsm' is not FSM machine.
912 * Use the macros SILC_FSM_EVENT_WAIT and SILC_FSM_EVENT_TIMEDWAIT to wait
913 * for the event. Use the SILC_FSM_EVENT_SIGNAL macro to signal all the
917 SilcFSMEvent silc_fsm_event_alloc(SilcFSM fsm);
919 /****f* silcutil/SilcFSMAPI/silc_fsm_event_init
923 * void silc_fsm_event_init(SilcFSMEvent event, SilcFSM fsm);
927 * Initializes a pre-allocates FSM event context. This call is
928 * equivalent to silc_fsm_event_alloc except this use the pre-allocated
929 * context. This fuction does not allocate any memory. The `fsm'
930 * must be the machine, not a thread.
933 void silc_fsm_event_init(SilcFSMEvent event, SilcFSM fsm);
935 /****f* silcutil/SilcFSMAPI/silc_fsm_event_free
939 * void silc_fsm_event_free(SilcFSMEvent event);
943 * Free the event allocated by silc_fsm_event_alloc function.
946 void silc_fsm_event_free(SilcFSMEvent event);
948 /****d* silcutil/SilcFSMAPI/SILC_FSM_EVENT_WAIT
952 * SILC_FSM_EVENT_WAIT(event)
956 * Macro used to wait for the `event' to be signalled. The machine
957 * or thread will be suspended while it is waiting for the event.
958 * This macro can only be used in FSM state functions. When the
959 * event is signalled the FSM will re-enter the current state (or
960 * state that was set with silc_fsm_next before waiting).
964 * // Signalling example
965 * ctx->async_event = silc_fsm_event_alloc(fsm);
968 * SILC_FSM_STATE(silc_foo_state)
972 * // Wait here for async call to complete
973 * SILC_FSM_EVENT_WAIT(ctx->async_event);
975 * // Async call completed
976 * if (ctx->async_success == FALSE)
982 #define SILC_FSM_EVENT_WAIT(event) \
984 if (silc_fsm_event_wait(event, fsm) == 0) \
985 return SILC_FSM_WAIT; \
988 /****d* silcutil/SilcFSMAPI/SILC_FSM_EVENT_TIMEDWAIT
992 * SILC_FSM_EVENT_TIMEDWAIT(event, seconds, useconds, timedout)
996 * Macro used to wait for the `event' to be signalled, or until
997 * the timeout specified by `seconds' and `useconds' has elapsed. If
998 * the timeout occurs before the event is signalled, the machine
999 * will wakeup. The `timedout' is SilcBool pointer and if it is
1000 * non-NULL indication of whether timeout occurred or not is saved to
1001 * the pointer. This macro can only be used in FSM state functions.
1002 * When the event is signalled or timedout the FSM will re-enter
1003 * the current state (or state that was set with silc_fsm_next before
1008 * SILC_FSM_STATE(silc_foo_state)
1010 * SilcBool timedout;
1013 * // Wait here for async call to complete, or 10 seconds for timeout
1014 * SILC_FSM_EVENT_TIMEDWAIT(ctx->async_event, 10, 0, &timedout);
1016 * // See if timeout occurred
1017 * if (timedout == TRUE)
1020 * // Async call completed
1021 * if (ctx->async_success == FALSE)
1027 #define SILC_FSM_EVENT_TIMEDWAIT(event, seconds, useconds, ret_to) \
1029 if (silc_fsm_event_timedwait(event, fsm, seconds, useconds, ret_to) == 0) \
1030 return SILC_FSM_WAIT; \
1033 /****f* silcutil/SilcFSMAPI/SILC_FSM_EVENT_SIGNAL
1037 * SILC_FSM_EVENT_SIGNAL(event)
1041 * Signals the `event' and awakens everybody that are waiting for this
1042 * event. This macro never blocks. It can be safely called at any place
1043 * in state function and in asynchronous callbacks or other functions.
1047 * SILC_FSM_STATE(silc_foo_async_completion)
1051 * // Notify all waiters
1052 * ctx->async_success = TRUE;
1053 * SILC_FSM_EVENT_SIGNAL(ctx->async_event);
1058 #define SILC_FSM_EVENT_SIGNAL(event) \
1060 silc_fsm_event_signal(event); \
1063 #include "silcfsm_i.h"
1065 #endif /* SILCFSM_H */