5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2005 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 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
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.
44 /****s* silcutil/SilcFSMAPI/SilcFSM
48 * typedef struct SilcFSMObject *SilcFSM;
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.
58 typedef struct SilcFSMObject *SilcFSM;
60 /****s* silcutil/SilcFSMAPI/SilcFSMStruct
64 * typedef struct SilcFSMObject SilcFSMStruct;
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.
73 typedef struct SilcFSMObject SilcFSMStruct;
75 /****s* silcutil/SilcFSMAPI/SilcFSMThread
79 * typedef struct SilcFSMObject *SilcFSMThread;
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.
92 typedef struct SilcFSMObject *SilcFSMThread;
94 /****s* silcutil/SilcFSMAPI/SilcFSM
98 * typedef struct SilcFSMObject SilcFSMThreadStruct;
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.
108 typedef struct SilcFSMObject SilcFSMThreadStruct;
110 /****d* silcutil/SilcFSMAPI/SilcFSMStatus
114 * typedef enum { ... } SilcFSMStatus;
118 * Status values that the FSM state functions return.
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
130 /****f* silcutil/SilcFSMAPI/SilcFSMDestructor
134 * typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context,
135 * void *destructor_context);
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.
147 typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context,
148 void *destructor_context);
150 /****f* silcutil/SilcFSMAPI/SilcFSMThreadDestructor
154 * typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread,
155 * void *thread_context,
156 * void *destructor_context);
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.
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
174 typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread,
175 void *thread_context,
176 void *destructor_context);
178 /****d* silcutil/SilcFSMAPI/SILC_FSM_STATE
182 * #define SILC_FSM_STATE(name)
186 * This macro is used to declare a FSM state function. The `fsm' is
187 * the SilcFSM or SilcFSMThread context, the `fsm_context' is the context
188 * given as argument to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_init,
189 * or silc_fsm_thread_alloc function. The `state_context' is the optional
190 * state specific context set with silc_fsm_set_state_context function.
194 #define SILC_FSM_STATE(name) \
195 SilcFSMStatus name(struct SilcFSMObject *fsm, void *fsm_context, \
199 /* State function callback */
200 typedef SilcFSMStatus (*SilcFSMStateCallback)(struct SilcFSMObject *fsm,
202 void *state_context);
204 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL
208 * SILC_FSM_CALL(function)
212 * Macro used to call asynchronous calls from state function. If the
213 * call is not really asynchronous then this will cause the machine to
214 * directly proceed to next state. If the call is truly asynchronous
215 * then this will set the machine to wait state. The silc_fsm_next
216 * must be called before this macro, so that the next state is set.
220 * The state function returns in this macro.
225 * silc_fsm_next(fsm, some_next_state);
226 * SILC_FSM_CALL(silc_some_async_call(server, some_callback, context));
228 * // More complex example
229 * silc_fsm_next(fsm, some_next_state);
230 * SILC_FSM_CALL((some_context->operation =
231 * silc_some_async_call(server, some_callback, context)));
234 #define SILC_FSM_CALL(function) \
236 assert(!silc_fsm_set_call(fsm, TRUE)); \
238 if (!silc_fsm_set_call(fsm, FALSE)) \
239 return SILC_FSM_CONTINUE; \
240 return SILC_FSM_WAIT; \
243 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE
247 * SILC_FSM_CALL_CONTINUE(fsm)
251 * Macro used to proceed after asynchornous call. This is called in the
252 * callback of the asynchronous call to continue in the state machine.
256 * void some_callback(void *context) {
257 * SilcFSM fsm = context;
259 * // Continue to the next state
260 * SILC_FSM_CALL_CONTINUE(fsm);
264 #define SILC_FSM_CALL_CONTINUE(fsm) \
266 if (!silc_fsm_set_call(fsm, FALSE)) \
267 silc_fsm_continue(fsm); \
270 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE_SYNC
274 * SILC_FSM_CALL_CONTINUE_SYNC(fsm)
278 * Macro used to proceed after asynchornous call. This is called in the
279 * callback of the asynchronous call to continue in the state machine.
280 * This continues to the next state synchronously, not through the
285 * void some_callback(void *context) {
286 * SilcFSM fsm = context;
288 * // Continue to the next state immediately
289 * SILC_FSM_CALL_CONTINUE_SYNC(fsm);
293 #define SILC_FSM_CALL_CONTINUE_SYNC(fsm) \
295 if (!silc_fsm_set_call(fsm, FALSE)) \
296 silc_fsm_continue_sync(fsm); \
299 /****d* silcutil/SilcFSMAPI/SILC_FSM_THREAD_WAIT
303 * SILC_FSM_THREAD_WAIT(thread)
307 * Macro used to wait for the `thread' to terminate. The machine or
308 * thread will be suspended while it is waiting for the thread to
313 * The state function returns in this macro.
315 * This macro is the only way to safely make sure that the thread has
316 * terminated by the time FSM continues from the waiting state. Using
317 * semaphores to signal from the thread before SILC_FSM_FINISH is returned
318 * works with normal FSM threads, but especially with real system threads
319 * it does not guarantee that the FSM won't continue before the thread has
320 * actually terminated. Usually this is not a problem, but it can be a
321 * problem if the FSM is waiting to be freed or uninitialized. In this
322 * case using this macro is strongly recommended.
325 #define SILC_FSM_THREAD_WAIT(thread) \
327 silc_fsm_thread_wait(fsm, thread); \
328 return SILC_FSM_WAIT; \
331 /****f* silcutil/SilcFSMAPI/silc_fsm_alloc
335 * SilcFSM silc_fsm_alloc(void *fsm_context,
336 * SilcFSMDestructor destructor,
337 * void *destructor_context,
338 * SilcSchedule schedule);
342 * Allocates SILC Finite State Machine context. The `destructor' with
343 * `destructor_context' will be called when the machines finishes. The
344 * caller must free the returned context with silc_fsm_free. The
345 * `fsm_context' is delivered to every FSM state function. The `schedule'
346 * is the caller's scheduler and the FSM will be run in the scheduler.
350 * SilcAsyncOperation silc_async_call(Callback callback, void *cb_context)
352 * SilcAsyncOperation op;
356 * // Allocate async operation so that caller can control us, like abort
357 * op = silc_async_alloc(silc_async_call_abort, NULL, ourcontext);
360 * fsm = silc_fsm_alloc(ourcontext, fsm_destructor, ourcontext,
362 * silc_fsm_start(fsm, first_state);
365 * // Return async operation for upper layer
370 SilcFSM silc_fsm_alloc(void *fsm_context,
371 SilcFSMDestructor destructor,
372 void *destructor_context,
373 SilcSchedule schedule);
375 /****f* silcutil/SilcFSMAPI/silc_fsm_init
379 * SilcBool silc_fsm_init(SilcFSM fsm,
381 * SilcFSMDestructor destructor,
382 * void *destructor_context,
383 * SilcSchedule schedule);
387 * Initializes a pre-allocated SilcFSM context. This call is equivalent
388 * to silc_fsm_alloc except that this takes the pre-allocated context
389 * as argument. The silc_fsm_free must not be called if this was called.
390 * Returns TRUE if the initialization is Ok or FALSE if error occurred.
391 * This function does not allocate any memory. The `schedule' is the
392 * caller's scheduler and the FSM will be run in the scheduler.
398 * silc_fsm_init(&fsm, application, fsm_destructor, application, schedule);
399 * silc_fsm_start(&fsm, first_state);
402 SilcBool silc_fsm_init(SilcFSM fsm,
404 SilcFSMDestructor destructor,
405 void *destructor_context,
406 SilcSchedule schedule);
408 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_alloc
412 * SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
413 * void *thread_context,
414 * SilcFSMThreadDestructor destructor,
415 * void *destructor_context,
416 * SilcBool real_thread);
420 * Allocates FSM thread context. The thread will be executed in the
421 * FSM machine indicated by `fsm'. The caller must free the returned
422 * thread context with silc_fsm_free. If the 'real_thread' is TRUE
423 * then the thread will actually be executed in real thread, if platform
424 * supports them. The `thread_context' is delivered to every state
425 * function in the thread.
429 * Note the limitations on using `real_thread' boolean to indicate running
430 * the FSM thread in a real system thread:
432 * If the system does not support threads, then this function will revert
433 * back to normal FSM threads.
435 * If the `real_thread' is TRUE then FSM will allocate new SilcSchedule
436 * for the FSM thread. This is done because the SilcSchedule that the
437 * `fsm' use cannot be used in the thread. This is limitation in the
438 * SilcSchedule implementation. If you need scheduler in the real thread
439 * it is strongly recommended that you use the SilcSchedule that is
440 * allocated for the thread. You can retrieve the SilcSchedule from the
441 * thread using silc_fsm_get_schedule function. Note that, the allocated
442 * SilcSchedule will become invalid after the thread finishes.
444 * You may still however use the original SilcSchedule if you wish. In
445 * this case note its limitation: you may only add and/or remove tasks,
446 * tasks cannot be executed in the thread. You will need to deliver the
447 * original SilcSchedule to the thread in the `thread_context' if you wish
450 * If `real_thread' is FALSE then no limitations on what can be run in
451 * the thread exist. In this case silc_fsm_get_schedule will return
452 * the SilcSchedule that was originally given to silc_fsm_alloc or
457 * SILC_FSM_STATE(silc_foo_state)
459 * SilcFSMThread thread;
462 * // Execute the route lookup in thread
463 * thread = silc_fsm_thread_alloc(fsm, fsm_context, NULL, NULL, FALSE);
464 * silc_fsm_start(thread, silc_route_lookup_start);
466 * // Wait here for the thread to terminate. Set the state where to go
467 * // after the thread has terminated.
468 * silc_fsm_next(fsm, silc_foo_route_lookup_finished);
469 * SILC_FSM_THREAD_WAIT(thread);
473 SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
474 void *thread_context,
475 SilcFSMThreadDestructor destructor,
476 void *destructor_context,
477 SilcBool real_thread);
479 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_init
483 * void silc_fsm_thread_init(SilcFSMThread thread,
485 * void *thread_context,
486 * SilcFSMThreadDestructor destructor,
487 * void *destructor_context,
488 * SilcBool real_thread);
492 * Initializes a pre-allocated SilcFSMThread context. This call is
493 * equivalent to silc_fsm_thread_alloc except that this takes the
494 * pre-allocated context as argument. The silc_fsm_free must not be
495 * called if this was called. Returns TRUE if the initialization is Ok
496 * or FALSE if error occurred. If the `real_thread' is TRUE then the
497 * thread will actually be executed in real thread, if platform supports
502 * See the notes from the silc_fsm_thread_alloc.
506 * SilcFSMThreadStruct thread;
508 * silc_fsm_thread_init(&thread, fsm, application, NULL, NULL, FALSE);
509 * silc_fsm_start(&thread, first_state);
512 void silc_fsm_thread_init(SilcFSMThread thread,
514 void *thread_context,
515 SilcFSMThreadDestructor destructor,
516 void *destructor_context,
517 SilcBool real_thread);
519 /****f* silcutil/SilcFSMAPI/silc_fsm_free
523 * void silc_fsm_free(void *fsm);
527 * Free the SILC FSM context that was allocated with silc_fsm_alloc,
528 * or free the SILC FSM thread context that was allocated with
529 * silc_fsm_thread_alloc. This function is used with both SilcFSM
530 * and SilcFSMThread contexts.
534 * When freeing FSM, it must not have any active threads.
537 void silc_fsm_free(void *fsm);
539 /****f* silcutil/SilcFSMAPI/silc_fsm_uninit
543 * void silc_fsm_uninit(void *fsm);
547 * Uninitializes a pre-allocated SilcFSM or SilcFSMThread context.
548 * If you used the function silc_fsm_init or silc_fsm_thread_init, call
549 * this function to uninitialize it. This function is used with both
550 * SilcFSMStruct and SilcFSMThreadStruct contexts.
554 * When uninitializing FSM, it must not have any active threads.
557 void silc_fsm_uninit(void *fsm);
559 /****f* silcutil/SilcFSMAPI/silc_fsm_start
563 * void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
567 * This function must be called after the SILC FSM context was created.
568 * This actually starts the state machine. Note that, the machine is
569 * started later after this function returns. The `start_state' is the
570 * state where the machine or thread is started. This function is used
571 * with both SilcFSM and SilcFSMThread contexts.
577 * fsm = silc_fsm_alloc(context, destructor, context, schedule);
578 * silc_fsm_start(fsm, first_state);
581 void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
583 /****f* silcutil/SilcFSMAPI/silc_fsm_start_sync
587 * void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
591 * This function is same as silc_fsm_start, except that the FSM will
592 * be started immediately inside this function. After this function
593 * returns the `start_state' has already been executed. If the machine
594 * is completely synchronous (no waiting used in the machine) then
595 * the machine will have finished once this function returns. Also
596 * note that if the machine is completely synchronous the destructor
597 * will also be called from inside this function. This function is used
598 * with both SilcFSM and SilcFSMThread contexts.
601 void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
603 /****f* silcutil/SilcFSMAPI/silc_fsm_next
607 * void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
611 * Set the next state to be executed. If the state function that
612 * call this function returns SILC_FSM_CONTINUE, the `next_state'
613 * will be executed immediately. This function must always be used
614 * to set the next state in the machine or thread. This function is
615 * used with both SilcFSM and SilcFSMThread contexts.
619 * // Move to next state
620 * silc_fsm_next(fsm, next_state);
621 * return SILC_FSM_CONTINUE;
624 void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
626 /****f* silcutil/SilcFSMAPI/silc_fsm_next_later
630 * void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
631 * SilcUInt32 seconds, SilcUInt32 useconds);
635 * Set the next state to be executed later, at the specified time.
636 * The SILC_FSM_WAIT must be returned in the state function if this
637 * function is called. If any other state is returned machine operation
638 * is undefined. The machine or thread will move to `next_state' after
639 * the specified timeout. This function is used with both SilcFSM and
640 * SilcFSMThread contexts.
644 * If both `seconds' and `useconds' are 0, the effect is same as calling
645 * silc_fsm_next function, and SILC_FSM_CONTINUE must be returned.
649 * // Move to next state after 10 seconds
650 * silc_fsm_next_later(fsm, next_state, 10, 0);
651 * return SILC_FSM_WAIT;
654 void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
655 SilcUInt32 seconds, SilcUInt32 useconds);
657 /****f* silcutil/SilcFSMAPI/silc_fsm_continue
661 * void silc_fsm_continue(void *fsm);
665 * Continues in the state machine from a SILC_FSM_WAIT state. This can
666 * be called from outside waiting FSM to continue to the next state.
667 * This function can be used instead of SILC_FSM_CALL_CONTINUE macro
668 * in case the SILC_FSM_CALL was not used. This must not be used if
669 * SILC_FSM_CALL was used. This function is used with both SilcFSM and
670 * SilcFSMThread contexts.
673 void silc_fsm_continue(void *fsm);
675 /****f* silcutil/SilcFSMAPI/silc_fsm_continue_sync
679 * void silc_fsm_continue_sync(void *fsm);
683 * Continues immediately in the state machine from a SILC_FSM_WAIT state.
684 * This can be called from outside waiting FSM to immediately continue to
685 * the next state. This function can be used instead of the
686 * SILC_FSM_CALL_CONTINUE_SYNC macro in case the SILC_FSM_CALL was not used.
687 * This must not be used if SILC_FSM_CALL was used. This function is used
688 * with both SilcFSM and SilcFSMThread contexts.
691 void silc_fsm_continue_sync(void *fsm);
693 /****f* silcutil/SilcFSMAPI/silc_fsm_set_context
697 * void silc_fsm_set_context(void *fsm, void *fsm_context);
701 * Set new context for the `fsm'. This function can be used to change
702 * the context inside the `fsm', if needed. This function is used with
703 * both SilcFSM and SilcFSMThread contexts. The context is the
704 * `fsm_context' in the state function (SILC_FSM_STATE).
707 void silc_fsm_set_context(void *fsm, void *fsm_context);
709 /****f* silcutil/SilcFSMAPI/silc_fsm_get_context
713 * void *silc_fsm_get_context(void *fsm);
717 * Returns the context associated with the `fsm'. It is the context that
718 * was given to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_alloc or
719 * silc_fsm_thread_init. This function is used with both SilcFSM and
720 * SilcFSMThread contexts.
723 void *silc_fsm_get_context(void *fsm);
725 /****f* silcutil/SilcFSMAPI/silc_fsm_set_state_context
729 * void silc_fsm_set_state_context(void *fsm, void *state_context);
733 * Set's a state specific context for the `fsm'. This function can be
734 * used to change the state context inside the `fsm', if needed. This
735 * function is used with both SilcFSM and SilcFSMThread contexts. The
736 * context is the `state_context' in the state function (SILC_FSM_STATE).
739 void silc_fsm_set_state_context(void *fsm, void *state_context);
741 /****f* silcutil/SilcFSMAPI/silc_fsm_get_state_context
745 * void *silc_fsm_get_state_context(void *fsm);
749 * Returns the state context associated with the `fsm'. It is the context
750 * that was set with silc_fsm_set_state_context function. This function
751 * is used with both SilcFSM and SilcFSMThread contexts.
754 void *silc_fsm_get_state_context(void *fsm);
756 /****f* silcutil/SilcFSMAPI/silc_fsm_get_schedule
760 * SilcSchedule silc_fsm_get_schedule(void *fsm);
764 * Returns the SilcSchedule that has been associated with the `fsm'.
765 * If caller needs scheduler it may retrieve it with this function. This
766 * function is used with both SilcFSM and SilcFSMThread contexts.
768 * If the `fsm' is thread and real system threads are being used, and this
769 * is called from the thread, it will return the SilcSchedule that was
770 * allocated by the FSM for the thread. It is strongly recommended to
771 * use this SilcSchedule if you are using real threads, and you need
772 * scheduler in the thread. Note that, once the thread finishes the
773 * returned SilcSchedule becomes invalid.
775 * In other times this returns the SilcSchedule pointer that was given
776 * to silc_fsm_alloc or silc_fsm_init.
779 SilcSchedule silc_fsm_get_schedule(void *fsm);
781 /****f* silcutil/SilcFSMAPI/silc_fsm_get_machine
785 * SilcFSM silc_fsm_get_machine(SilcFSMThread thread);
789 * Returns the machine from the FSM thread indicated by `thread'.
792 SilcFSM silc_fsm_get_machine(SilcFSMThread thread);
797 /****s* silcutil/SilcFSMAPI/SilcFSMSema
801 * typedef struct SilcFSMSemaObject *SilcFSMSema;
805 * The FSM semaphore context allocated with silc_fsm_sema_alloc. The
806 * caller must free it with silc_fsm_sema_free. It is also possible
807 * to use pre-allocated SilcFSMSemaStruct instead of SilcFSMSema context.
810 typedef struct SilcFSMSemaObject *SilcFSMSema;
812 /****s* silcutil/SilcFSMAPI/SilcFSMSemaStruct
816 * typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
820 * The FSM semaphore context that can be used as pre-allocated context.
821 * It is initialized with silc_fsm_sema_init. It need not be
825 typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
827 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_alloc
831 * SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
835 * Allocates FSM semaphore with initial value of `value'. Semaphores are
836 * counters for resources shared between machine and threads. Semaphores
837 * can be waited until the semaphore value is non-zero. The FSM will be
838 * suspended when waiting for semaphore. When the semaphore is incremented
839 * all that are waiting for the semaphore will be signalled and awaken.
841 * Semaphores can be used to wait for example when thread terminates, or
842 * when thread moves into a specific state, or to protect critical
843 * sections. The FSM semaphores can be used also in FSM threads that are
844 * executed in real system threads.
846 * Use the macros SILC_FSM_SEMA_WAIT and SILC_FSM_SEMA_TIMEDWAIT to wait
847 * for semaphore. Use the SILC_FSM_SEMA_POST macro to increment the
848 * counter and wake up all waiters.
851 SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
853 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_init
857 * void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
861 * Initializes a pre-allocates semaphore context. This call is
862 * equivalent to silc_fsm_sema_alloc except this use the pre-allocated
863 * context. This fuction does not allocate any memory.
866 void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
868 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_free
872 * void silc_fsm_sema_free(SilcFSMSema sema);
876 * Free the semaphore allocated by silc_fsm_sema_alloc function.
879 void silc_fsm_sema_free(SilcFSMSema sema);
881 /****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_WAIT
885 * SILC_FSM_SEMA_WAIT(semaphore)
889 * Macro used to wait for the `semaphore' to become non-zero. The
890 * machine will be suspended while it is waiting for the semaphore.
891 * This macro can only be used in FSM state functions. When the
892 * semaphore is signalled the FSM will re-enter the current state (or
893 * state that was set with silc_fsm_next before waiting).
897 * // Signalling example
898 * ctx->sema = silc_fsm_sema_alloc(fsm, 0);
901 * SILC_FSM_STATE(silc_foo_state)
905 * // Wait here for async call to complete
906 * SILC_FSM_SEMA_WAIT(ctx->async_sema);
908 * // Async call completed
909 * if (ctx->async_success == FALSE)
914 * // Mutual exclusion example
915 * ctx->lock = silc_fsm_sema_alloc(fsm, 1);
918 * SILC_FSM_STATE(silc_foo_state)
921 * SILC_FSM_SEMA_WAIT(ctx->lock);
922 * very critical stuff...
923 * SILC_FSM_SEMA_POST(ctx->lock);
928 #define SILC_FSM_SEMA_WAIT(sema) \
930 if (silc_fsm_sema_wait(sema, fsm) == 0) \
931 return SILC_FSM_WAIT; \
934 /****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_TIMEDWAIT
938 * SILC_FSM_SEMA_TIMEDWAIT(semaphore, seconds, useconds, timedout)
942 * Macro used to wait for the `semaphore' to become non-zero, or until
943 * the timeout specified by `seconds' and `useconds' has elapsed. If
944 * the timeout occurs before the semaphore becomes non-zero, the machine
945 * will wakeup. The `timedout' is SilcBool pointer and if it is
946 * non-NULL indication of whether timeout occurred or not is saved to
947 * the pointer. This macro can only be used in FSM state functions.
948 * When the semaphore is signalled or timedout the FSM will re-enter
949 * the current state (or state that was set with silc_fsm_next before
954 * SILC_FSM_STATE(silc_foo_state)
960 * // Wait here for async call to complete, or 10 seconds for timeout
961 * SILC_FSM_SEMA_TIMEDWAIT(ctx->async_sema, 10, 0, &timedout);
963 * // See if timeout occurred
964 * if (timedout == TRUE)
967 * // Async call completed
968 * if (ctx->async_success == FALSE)
974 #define SILC_FSM_SEMA_TIMEDWAIT(sema, seconds, useconds, ret_to) \
976 if (silc_fsm_sema_timedwait(sema, fsm, seconds, useconds, ret_to) == 0) \
977 return SILC_FSM_WAIT; \
980 /****f* silcutil/SilcFSMAPI/SILC_FSM_SEMA_POST
984 * SILC_FSM_SEMA_POST(semaphore)
988 * Increases the semaphore counter and awakens everybody that are
989 * waiting for this semaphore. This macro never blocks. It can be
990 * safely called at any place in state function and in asynchronous
991 * callbacks or other functions.
995 * SILC_FSM_STATE(silc_foo_async_completion)
999 * // Notify all waiters
1000 * ctx->async_success = TRUE;
1001 * SILC_FSM_SEMA_POST(ctx->async_sema);
1006 #define SILC_FSM_SEMA_POST(sema) \
1008 silc_fsm_sema_post(sema); \
1011 #include "silcfsm_i.h"
1013 #endif /* SILCFSM_H */