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.
190 #define SILC_FSM_STATE(name) \
191 SilcFSMStatus name(struct SilcFSMObject *fsm, void *fsm_context)
194 /* State function callback */
195 typedef SilcFSMStatus (*SilcFSMStateCallback)(struct SilcFSMObject *fsm,
198 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL
202 * SILC_FSM_CALL(function)
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.
214 * The state function returns in this macro.
219 * silc_fsm_next(fsm, some_next_state);
220 * SILC_FSM_CALL(silc_some_async_call(server, some_callback, context));
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)));
228 #define SILC_FSM_CALL(function) \
230 assert(!silc_fsm_set_call(fsm, TRUE)); \
232 if (!silc_fsm_set_call(fsm, FALSE)) \
233 return SILC_FSM_CONTINUE; \
234 return SILC_FSM_WAIT; \
237 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE
241 * SILC_FSM_CALL_CONTINUE(fsm)
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.
250 * void some_callback(void *context) {
251 * SilcFSM fsm = context;
253 * // Continue to the next state
254 * SILC_FSM_CALL_CONTINUE(fsm);
258 #define SILC_FSM_CALL_CONTINUE(fsm) \
260 if (!silc_fsm_set_call(fsm, FALSE)) \
261 silc_fsm_continue(fsm); \
264 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE
268 * SILC_FSM_CALL_CONTINUE_SYNC(fsm)
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
279 * void some_callback(void *context) {
280 * SilcFSM fsm = context;
282 * // Continue to the next state immediately
283 * SILC_FSM_CALL_CONTINUE_SYNC(fsm);
287 #define SILC_FSM_CALL_CONTINUE_SYNC(fsm) \
289 if (!silc_fsm_set_call(fsm, FALSE)) \
290 silc_fsm_continue_sync(fsm); \
293 /****d* silcutil/SilcFSMAPI/SILC_FSM_THREAD_WAIT
297 * SILC_FSM_THREAD_WAIT(thread)
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
307 * The state function returns in this macro.
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.
319 #define SILC_FSM_THREAD_WAIT(thread) \
321 silc_fsm_thread_wait(fsm, thread); \
322 return SILC_FSM_WAIT; \
325 /****f* silcutil/SilcFSMAPI/silc_fsm_alloc
329 * SilcFSM silc_fsm_alloc(void *fsm_context,
330 * SilcFSMDestructor destructor,
331 * void *destructor_context,
332 * SilcSchedule schedule);
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.
344 * SilcAsyncOperation silc_async_call(Callback callback, void *cb_context)
346 * SilcAsyncOperation op;
350 * // Allocate async operation so that caller can control us, like abort
351 * op = silc_async_alloc(silc_async_call_abort, NULL, ourcontext);
354 * fsm = silc_fsm_alloc(ourcontext, fsm_destructor, ourcontext,
356 * silc_fsm_start(fsm, first_state);
359 * // Return async operation for upper layer
364 SilcFSM silc_fsm_alloc(void *fsm_context,
365 SilcFSMDestructor destructor,
366 void *destructor_context,
367 SilcSchedule schedule);
369 /****f* silcutil/SilcFSMAPI/silc_fsm_init
373 * SilcBool silc_fsm_init(SilcFSM fsm,
375 * SilcFSMDestructor destructor,
376 * void *destructor_context,
377 * SilcSchedule schedule);
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.
392 * silc_fsm_init(&fsm, application, fsm_destructor, application, schedule);
393 * silc_fsm_start(&fsm, first_state);
396 SilcBool silc_fsm_init(SilcFSM fsm,
398 SilcFSMDestructor destructor,
399 void *destructor_context,
400 SilcSchedule schedule);
402 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_alloc
406 * SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
407 * void *thread_context,
408 * SilcFSMThreadDestructor destructor,
409 * void *destructor_context,
410 * SilcBool real_thread);
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.
423 * Note the limitations on using `real_thread' boolean to indicate running
424 * the FSM thread in a real system thread:
426 * If the system does not support threads, then this function will revert
427 * back to normal FSM threads.
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.
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
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
451 * SILC_FSM_STATE(silc_foo_state)
453 * SilcFSMThread thread;
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);
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);
467 SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
468 void *thread_context,
469 SilcFSMThreadDestructor destructor,
470 void *destructor_context,
471 SilcBool real_thread);
473 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_init
477 * SilcBool silc_fsm_thread_init(SilcFSMThread thread,
479 * void *thread_context,
480 * SilcFSMThreadDestructor destructor,
481 * void *destructor_context,
482 * SilcBool real_thread);
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
496 * See the notes from the silc_fsm_thread_alloc.
500 * SilcFSMThreadStruct thread;
502 * silc_fsm_thread_init(&thread, fsm, application, NULL, NULL, FALSE);
503 * silc_fsm_start(&thread, first_state);
506 SilcBool silc_fsm_thread_init(SilcFSMThread thread,
508 void *thread_context,
509 SilcFSMThreadDestructor destructor,
510 void *destructor_context,
511 SilcBool real_thread);
513 /****f* silcutil/SilcFSMAPI/silc_fsm_free
517 * void silc_fsm_free(void *fsm);
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.
528 * When freeing FSM, it must not have any active threads.
531 void silc_fsm_free(void *fsm);
533 /****f* silcutil/SilcFSMAPI/silc_fsm_uninit
537 * void silc_fsm_uninit(void *fsm);
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.
548 * When uninitializing FSM, it must not have any active threads.
551 void silc_fsm_uninit(void *fsm);
553 /****f* silcutil/SilcFSMAPI/silc_fsm_start
557 * void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
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.
571 * fsm = silc_fsm_alloc(context, destructor, context, schedule);
572 * silc_fsm_start(fsm, first_state);
575 void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
577 /****f* silcutil/SilcFSMAPI/silc_fsm_start_sync
581 * void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
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.
595 void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
597 /****f* silcutil/SilcFSMAPI/silc_fsm_next
601 * void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
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.
613 * // Move to next state
614 * silc_fsm_next(fsm, next_state);
615 * return SILC_FSM_CONTINUE;
618 void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
620 /****f* silcutil/SilcFSMAPI/silc_fsm_next_later
624 * void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
625 * SilcUInt32 seconds, SilcUInt32 useconds);
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.
638 * // Move to next state after 10 seconds
639 * silc_fsm_next_later(fsm, next_state, 10, 0);
640 * return SILC_FSM_WAIT;
643 void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
644 SilcUInt32 seconds, SilcUInt32 useconds);
646 /****f* silcutil/SilcFSMAPI/silc_fsm_get_context
650 * void *silc_fsm_get_context(void *fsm);
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.
660 void *silc_fsm_get_context(void *fsm);
662 /****f* silcutil/SilcFSMAPI/silc_fsm_set_context
666 * void silc_fsm_set_context(void *fsm, void *fsm_context);
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.
675 void silc_fsm_set_context(void *fsm, void *fsm_context);
677 /****f* silcutil/SilcFSMAPI/silc_fsm_get_schedule
681 * SilcSchedule silc_fsm_get_schedule(void *fsm);
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.
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.
696 * Every other time this returns the SilcSchedule pointer that was given
697 * to silc_fsm_alloc or silc_fsm_init.
700 SilcSchedule silc_fsm_get_schedule(void *fsm);
705 /****s* silcutil/SilcFSMAPI/SilcFSMSema
709 * typedef struct SilcFSMSemaObject *SilcFSMSema;
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.
718 typedef struct SilcFSMSemaObject *SilcFSMSema;
720 /****s* silcutil/SilcFSMAPI/SilcFSMSemaStruct
724 * typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
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
733 typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
735 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_alloc
739 * SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
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.
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.
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.
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.
763 SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
765 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_init
769 * void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
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.
778 void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
780 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_free
784 * void silc_fsm_sema_free(SilcFSMSema sema);
788 * Free the semaphore allocated by silc_fsm_sema_alloc function.
791 void silc_fsm_sema_free(SilcFSMSema sema);
793 /****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_WAIT
797 * SILC_FSM_SEMA_WAIT(semaphore)
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).
809 * // Signalling example
810 * ctx->sema = silc_fsm_sema_alloc(fsm, 0);
813 * SILC_FSM_STATE(silc_foo_state)
817 * // Wait here for async call to complete
818 * SILC_FSM_SEMA_WAIT(ctx->async_sema);
820 * // Async call completed
821 * if (ctx->async_success == FALSE)
826 * // Mutual exclusion example
827 * ctx->lock = silc_fsm_sema_alloc(fsm, 1);
830 * SILC_FSM_STATE(silc_foo_state)
833 * SILC_FSM_SEMA_WAIT(ctx->lock);
834 * very critical stuff...
835 * SILC_FSM_SEMA_POST(ctx->lock);
840 #define SILC_FSM_SEMA_WAIT(sema) \
842 if (silc_fsm_sema_wait(sema, fsm) == 0) \
843 return SILC_FSM_WAIT; \
846 /****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_TIMEDWAIT
850 * SILC_FSM_SEMA_TIMEDWAIT(semaphore, seconds, useconds)
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
864 * SILC_FSM_STATE(silc_foo_state)
868 * // Wait here for async call to complete, or 10 seconds for timeout
869 * SILC_FSM_SEMA_TIMEDWAIT(ctx->async_sema, 10, 0);
871 * // Async call completed or timeout occurred
872 * if (ctx->async_success == FALSE)
878 #define SILC_FSM_SEMA_TIMEDWAIT(sema, seconds, useconds) \
880 if (silc_fsm_sema_timedwait(sema, fsm, seconds, useconds) == 0) \
881 return SILC_FSM_WAIT; \
884 /****f* silcutil/SilcFSMAPI/SILC_FSM_SEMA_POST
888 * SILC_FSM_SEMA_POST(semaphore)
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.
899 * SILC_FSM_STATE(silc_foo_async_completion)
903 * // Notify all waiters
904 * ctx->async_success = TRUE;
905 * SILC_FSM_SEMA_POST(ctx->async_sema);
910 #define SILC_FSM_SEMA_POST(sema) \
912 silc_fsm_sema_post(sema); \
915 #include "silcfsm_i.h"
917 #endif /* SILCFSM_H */