5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2005, 2006 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 mutual exclusion locks to protect critical
33 * sections, and as conditional variables and signallers. 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 need not be uninitialized.
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 need not be uninitialized.
107 typedef struct SilcFSMObject SilcFSMThreadStruct;
109 /****d* silcutil/SilcFSMAPI/SilcFSMStatus
113 * typedef enum { ... } SilcFSMStatus;
117 * Status values that the FSM state functions return. They dicatate
118 * how the machine will behave after returning from the state function.
120 * The SILC_FSM_CONTINUE always moves to the next state synchronously.
122 * The SILC_FSM_YIELD always moves to the next state through the
123 * scheduler. Other threads will get running time with SILC_FSM_YIELD.
124 * When using real threads, using SILC_FSM_YIELD is usually unnecessary.
126 * The SILC_FSM_WAIT will suspend the machine until it is awaken.
128 * The SILC_FSM_FINISH finished the machine or thread and calls its
129 * destructor, if defined. If the machine is finished when it has
130 * running threads the machine will fatally fail. User must always
131 * finish the threads before finishing the machine.
136 SILC_FSM_CONTINUE, /* Continue immediately to next state */
137 SILC_FSM_YIELD, /* Continue to next state through scheduler */
138 SILC_FSM_WAIT, /* Wait for some async call or timeout */
139 SILC_FSM_FINISH, /* Finish state machine and call destructor
144 /****f* silcutil/SilcFSMAPI/SilcFSMDestructor
148 * typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context,
149 * void *destructor_context);
153 * The destructor callback that was set in silc_fsm_alloc or in
154 * silc_fsm_init function. It will be called when a state function
155 * returns SILC_FSM_FINISH. This function will be called through
156 * the scheduler; it will not be called immediately after the state
157 * function returns SILC_FSM_FINISH, but will be called later.
158 * The `fsm' may be freed in this function.
161 typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context,
162 void *destructor_context);
164 /****f* silcutil/SilcFSMAPI/SilcFSMThreadDestructor
168 * typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread,
169 * void *thread_context,
170 * void *destructor_context);
174 * The destructor callback that was set in silc_fsm_thread_alloc or in
175 * silc_fsm_thread_init function. It will be called when a state function
176 * returns SILC_FSM_FINISH. This function will be called through
177 * the scheduler; it will not be called immediately after the state
178 * function returns SILC_FSM_FINISH, but will be called later. The
179 * `thread' may be freed in this function.
183 * Even if the `thread' was executed in real system thread, this callback
184 * is always received in the main machine thread, not in the created
188 typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread,
189 void *thread_context,
190 void *destructor_context);
192 /****d* silcutil/SilcFSMAPI/SILC_FSM_STATE
196 * #define SILC_FSM_STATE(name)
200 * This macro is used to declare a FSM state function. The `fsm' is
201 * the SilcFSM or SilcFSMThread context, the `fsm_context' is the context
202 * given as argument to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_init,
203 * or silc_fsm_thread_alloc function. The `state_context' is the optional
204 * state specific context set with silc_fsm_set_state_context function.
208 #define SILC_FSM_STATE(name) \
209 SilcFSMStatus name(struct SilcFSMObject *fsm, void *fsm_context, \
213 /* State function callback */
214 typedef SilcFSMStatus (*SilcFSMStateCallback)(struct SilcFSMObject *fsm,
216 void *state_context);
218 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL
222 * SILC_FSM_CALL(function)
226 * Macro used to call asynchronous calls from state function. If the
227 * call is not really asynchronous then this will cause the machine to
228 * directly proceed to next state. If the call is truly asynchronous
229 * then this will set the machine to wait state. The silc_fsm_next
230 * must be called before this macro, so that the next state is set.
234 * The state function returns in this macro.
239 * silc_fsm_next(fsm, some_next_state);
240 * SILC_FSM_CALL(silc_some_async_call(server, some_callback, context));
242 * // More complex example
243 * silc_fsm_next(fsm, some_next_state);
244 * SILC_FSM_CALL((some_context->operation =
245 * silc_some_async_call(server, some_callback, context)));
248 #define SILC_FSM_CALL(function) \
250 assert(!silc_fsm_set_call(fsm, TRUE)); \
252 if (!silc_fsm_set_call(fsm, FALSE)) \
253 return SILC_FSM_CONTINUE; \
254 return SILC_FSM_WAIT; \
257 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE
261 * SILC_FSM_CALL_CONTINUE(fsm)
265 * Macro used to proceed after asynchornous call. This is called in the
266 * callback of the asynchronous call to continue in the state machine.
270 * void some_callback(void *context) {
271 * SilcFSM fsm = context;
273 * // Continue to the next state
274 * SILC_FSM_CALL_CONTINUE(fsm);
278 #define SILC_FSM_CALL_CONTINUE(fsm) \
280 if (!silc_fsm_set_call(fsm, FALSE)) \
281 silc_fsm_continue(fsm); \
284 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE_SYNC
288 * SILC_FSM_CALL_CONTINUE_SYNC(fsm)
292 * Macro used to proceed after asynchornous call. This is called in the
293 * callback of the asynchronous call to continue in the state machine.
294 * This continues to the next state synchronously, not through the
299 * void some_callback(void *context) {
300 * SilcFSM fsm = context;
302 * // Continue to the next state immediately
303 * SILC_FSM_CALL_CONTINUE_SYNC(fsm);
307 #define SILC_FSM_CALL_CONTINUE_SYNC(fsm) \
309 if (!silc_fsm_set_call(fsm, FALSE)) \
310 silc_fsm_continue_sync(fsm); \
313 /****d* silcutil/SilcFSMAPI/SILC_FSM_THREAD_WAIT
317 * SILC_FSM_THREAD_WAIT(thread)
321 * Macro used to wait for the `thread' to terminate. The machine or
322 * thread will be suspended while it is waiting for the thread to
327 * The state function returns in this macro.
329 * This macro is the only way to safely make sure that the thread has
330 * terminated by the time FSM continues from the waiting state. Using
331 * semaphores to signal from the thread before SILC_FSM_FINISH is returned
332 * works with normal FSM threads, but especially with real system threads
333 * it does not guarantee that the FSM won't continue before the thread has
334 * actually terminated. Usually this is not a problem, but it can be a
335 * problem if the FSM is waiting to be freed. In this case using this
336 * macro is strongly recommended.
339 #define SILC_FSM_THREAD_WAIT(thread) \
341 silc_fsm_thread_wait(fsm, thread); \
342 return SILC_FSM_WAIT; \
345 /****f* silcutil/SilcFSMAPI/silc_fsm_alloc
349 * SilcFSM silc_fsm_alloc(void *fsm_context,
350 * SilcFSMDestructor destructor,
351 * void *destructor_context,
352 * SilcSchedule schedule);
356 * Allocates SILC Finite State Machine context. The `destructor' with
357 * `destructor_context' will be called when the machines finishes. The
358 * caller must free the returned context with silc_fsm_free. The
359 * `fsm_context' is delivered to every FSM state function. The `schedule'
360 * is the caller's scheduler and the FSM will be run in the scheduler.
364 * SilcAsyncOperation silc_async_call(Callback callback, void *cb_context)
366 * SilcAsyncOperation op;
370 * // Allocate async operation so that caller can control us, like abort
371 * op = silc_async_alloc(silc_async_call_abort, NULL, ourcontext);
374 * fsm = silc_fsm_alloc(ourcontext, fsm_destructor, ourcontext,
376 * silc_fsm_start(fsm, first_state);
379 * // Return async operation for upper layer
384 SilcFSM silc_fsm_alloc(void *fsm_context,
385 SilcFSMDestructor destructor,
386 void *destructor_context,
387 SilcSchedule schedule);
389 /****f* silcutil/SilcFSMAPI/silc_fsm_init
393 * SilcBool silc_fsm_init(SilcFSM fsm,
395 * SilcFSMDestructor destructor,
396 * void *destructor_context,
397 * SilcSchedule schedule);
401 * Initializes a pre-allocated SilcFSM context. This call is equivalent
402 * to silc_fsm_alloc except that this takes the pre-allocated context
403 * as argument. The silc_fsm_free must not be called if this was called.
404 * Returns TRUE if the initialization is Ok or FALSE if error occurred.
405 * This function does not allocate any memory. The `schedule' is the
406 * caller's scheduler and the FSM will be run in the scheduler.
412 * silc_fsm_init(&fsm, application, fsm_destructor, application, schedule);
413 * silc_fsm_start(&fsm, first_state);
416 SilcBool silc_fsm_init(SilcFSM fsm,
418 SilcFSMDestructor destructor,
419 void *destructor_context,
420 SilcSchedule schedule);
422 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_alloc
426 * SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
427 * void *thread_context,
428 * SilcFSMThreadDestructor destructor,
429 * void *destructor_context,
430 * SilcBool real_thread);
434 * Allocates FSM thread context. The thread will be executed in the
435 * FSM machine indicated by `fsm'. The caller must free the returned
436 * thread context with silc_fsm_free. If the 'real_thread' is TRUE
437 * then the thread will actually be executed in real thread, if platform
438 * supports them. The `thread_context' is delivered to every state
439 * function in the thread.
443 * Note the limitations on using `real_thread' boolean to indicate running
444 * the FSM thread in a real system thread:
446 * If the system does not support threads, then this function will revert
447 * back to normal FSM threads.
449 * If the `real_thread' is TRUE then FSM will allocate new SilcSchedule
450 * for the FSM thread. This is done because the SilcSchedule that the
451 * `fsm' use cannot be used in the thread. This is limitation in the
452 * SilcSchedule implementation. If you need scheduler in the real thread
453 * it is strongly recommended that you use the SilcSchedule that is
454 * allocated for the thread. You can retrieve the SilcSchedule from the
455 * thread using silc_fsm_get_schedule function. Note that, the allocated
456 * SilcSchedule will become invalid after the thread finishes.
458 * You may still however use the original SilcSchedule if you wish. In
459 * this case note its limitation: you may only add and/or remove tasks,
460 * tasks cannot be executed in the thread. You will need to deliver the
461 * original SilcSchedule to the thread in the `thread_context' if you wish
464 * If `real_thread' is FALSE then no limitations on what can be run in
465 * the thread exist. In this case silc_fsm_get_schedule will return
466 * the SilcSchedule that was originally given to silc_fsm_alloc or
471 * SILC_FSM_STATE(silc_foo_state)
473 * SilcFSMThread thread;
476 * // Execute the route lookup in thread
477 * thread = silc_fsm_thread_alloc(fsm, fsm_context, NULL, NULL, FALSE);
478 * silc_fsm_start(thread, silc_route_lookup_start);
480 * // Wait here for the thread to terminate. Set the state where to go
481 * // after the thread has terminated.
482 * silc_fsm_next(fsm, silc_foo_route_lookup_finished);
483 * SILC_FSM_THREAD_WAIT(thread);
487 SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
488 void *thread_context,
489 SilcFSMThreadDestructor destructor,
490 void *destructor_context,
491 SilcBool real_thread);
493 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_init
497 * void silc_fsm_thread_init(SilcFSMThread thread,
499 * void *thread_context,
500 * SilcFSMThreadDestructor destructor,
501 * void *destructor_context,
502 * SilcBool real_thread);
506 * Initializes a pre-allocated SilcFSMThread context. This call is
507 * equivalent to silc_fsm_thread_alloc except that this takes the
508 * pre-allocated context as argument. The silc_fsm_free must not be
509 * called if this was called. If the `real_thread' is TRUE then the
510 * thread will actually be executed in real thread, if platform supports
515 * See the notes from the silc_fsm_thread_alloc.
519 * SilcFSMThreadStruct thread;
521 * silc_fsm_thread_init(&thread, fsm, application, NULL, NULL, FALSE);
522 * silc_fsm_start(&thread, first_state);
525 void silc_fsm_thread_init(SilcFSMThread thread,
527 void *thread_context,
528 SilcFSMThreadDestructor destructor,
529 void *destructor_context,
530 SilcBool real_thread);
532 /****f* silcutil/SilcFSMAPI/silc_fsm_free
536 * void silc_fsm_free(void *fsm);
540 * Free the SILC FSM context that was allocated with silc_fsm_alloc,
541 * or free the SILC FSM thread context that was allocated with
542 * silc_fsm_thread_alloc. This function is used with both SilcFSM
543 * and SilcFSMThread contexts.
547 * When freeing FSM, it must not have any active threads.
550 void silc_fsm_free(void *fsm);
552 /****f* silcutil/SilcFSMAPI/silc_fsm_start
556 * void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
560 * This function must be called after the SILC FSM context was created.
561 * This actually starts the state machine. Note that, the machine is
562 * started later after this function returns. The `start_state' is the
563 * state where the machine or thread is started. This function is used
564 * with both SilcFSM and SilcFSMThread contexts.
570 * fsm = silc_fsm_alloc(context, destructor, context, schedule);
571 * silc_fsm_start(fsm, first_state);
574 void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
576 /****f* silcutil/SilcFSMAPI/silc_fsm_start_sync
580 * void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
584 * This function is same as silc_fsm_start, except that the FSM will
585 * be started immediately inside this function. After this function
586 * returns the `start_state' has already been executed. If the machine
587 * is completely synchronous (no waiting used in the machine) then
588 * the machine will have finished once this function returns. Also
589 * note that if the machine is completely synchronous the destructor
590 * will also be called from inside this function. This function is used
591 * with both SilcFSM and SilcFSMThread contexts.
594 void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
596 /****f* silcutil/SilcFSMAPI/silc_fsm_next
600 * void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
604 * Set the next state to be executed. If the state function that
605 * call this function returns SILC_FSM_CONTINUE, the `next_state'
606 * will be executed immediately. If it returns SILC_FSM_YIELD it
607 * yields the thread and the `next_state' will be run after other
608 * threads have run first. This function must always be used to set
609 * the next state in the machine or thread. This function is used
610 * with both SilcFSM and SilcFSMThread contexts.
614 * // Move to next state
615 * silc_fsm_next(fsm, next_state);
616 * return SILC_FSM_CONTINUE;
619 void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
621 /****f* silcutil/SilcFSMAPI/silc_fsm_next_later
625 * void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
626 * SilcUInt32 seconds, SilcUInt32 useconds);
630 * Set the next state to be executed later, at the specified time.
631 * The SILC_FSM_WAIT must be returned in the state function if this
632 * function is called. If any other state is returned machine operation
633 * is undefined. The machine or thread will move to `next_state' after
634 * the specified timeout. This function is used with both SilcFSM and
635 * SilcFSMThread contexts.
639 * If both `seconds' and `useconds' are 0, the effect is same as calling
640 * silc_fsm_next function, and SILC_FSM_CONTINUE must be returned.
642 * If silc_fsm_continue or silc_fsm_continue_sync is called while the
643 * machine or thread is in SILC_FSM_WAIT state the timeout is automatically
644 * canceled and the state moves to the next state.
648 * // Move to next state after 10 seconds
649 * silc_fsm_next_later(fsm, next_state, 10, 0);
650 * return SILC_FSM_WAIT;
653 void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
654 SilcUInt32 seconds, SilcUInt32 useconds);
656 /****f* silcutil/SilcFSMAPI/silc_fsm_continue
660 * void silc_fsm_continue(void *fsm);
664 * Continues in the state machine from a SILC_FSM_WAIT state. This can
665 * be called from outside waiting FSM to continue to the next state.
666 * This function can be used instead of SILC_FSM_CALL_CONTINUE macro
667 * in case the SILC_FSM_CALL was not used. This must not be used if
668 * SILC_FSM_CALL was used. This function is used with both SilcFSM and
669 * SilcFSMThread contexts.
672 void silc_fsm_continue(void *fsm);
674 /****f* silcutil/SilcFSMAPI/silc_fsm_continue_sync
678 * void silc_fsm_continue_sync(void *fsm);
682 * Continues immediately in the state machine from a SILC_FSM_WAIT state.
683 * This can be called from outside waiting FSM to immediately continue to
684 * the next state. This function can be used instead of the
685 * SILC_FSM_CALL_CONTINUE_SYNC macro in case the SILC_FSM_CALL was not used.
686 * This must not be used if SILC_FSM_CALL was used. This function is used
687 * with both SilcFSM and SilcFSMThread contexts.
690 void silc_fsm_continue_sync(void *fsm);
692 /****f* silcutil/SilcFSMAPI/silc_fsm_finish
696 * void silc_fsm_finish(void *fsm);
700 * Finishes the `fsm'. This function may be used in case the FSM
701 * needs to be finished outside FSM states. Usually FSM is finished
702 * by returning SILC_FSM_FINISH from the state, but if this is not
703 * possible this function may be called. This function is used with
704 * both SilcFSM and SilcFSMThread contexts.
706 * If the `fsm' is a machine and it has running threads, the machine
707 * will fatally fail. The caller must first finish the threads and
711 void silc_fsm_finish(void *fsm);
713 /****f* silcutil/SilcFSMAPI/silc_fsm_set_context
717 * void silc_fsm_set_context(void *fsm, void *fsm_context);
721 * Set new context for the `fsm'. This function can be used to change
722 * the context inside the `fsm', if needed. This function is used with
723 * both SilcFSM and SilcFSMThread contexts. The context is the
724 * `fsm_context' in the state function (SILC_FSM_STATE).
727 void silc_fsm_set_context(void *fsm, void *fsm_context);
729 /****f* silcutil/SilcFSMAPI/silc_fsm_get_context
733 * void *silc_fsm_get_context(void *fsm);
737 * Returns the context associated with the `fsm'. It is the context that
738 * was given to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_alloc or
739 * silc_fsm_thread_init. This function is used with both SilcFSM and
740 * SilcFSMThread contexts.
743 void *silc_fsm_get_context(void *fsm);
745 /****f* silcutil/SilcFSMAPI/silc_fsm_set_state_context
749 * void silc_fsm_set_state_context(void *fsm, void *state_context);
753 * Set's a state specific context for the `fsm'. This function can be
754 * used to change the state context inside the `fsm', if needed. This
755 * function is used with both SilcFSM and SilcFSMThread contexts. The
756 * context is the `state_context' in the state function (SILC_FSM_STATE).
759 void silc_fsm_set_state_context(void *fsm, void *state_context);
761 /****f* silcutil/SilcFSMAPI/silc_fsm_get_state_context
765 * void *silc_fsm_get_state_context(void *fsm);
769 * Returns the state context associated with the `fsm'. It is the context
770 * that was set with silc_fsm_set_state_context function. This function
771 * is used with both SilcFSM and SilcFSMThread contexts.
774 void *silc_fsm_get_state_context(void *fsm);
776 /****f* silcutil/SilcFSMAPI/silc_fsm_get_schedule
780 * SilcSchedule silc_fsm_get_schedule(void *fsm);
784 * Returns the SilcSchedule that has been associated with the `fsm'.
785 * If caller needs scheduler it may retrieve it with this function. This
786 * function is used with both SilcFSM and SilcFSMThread contexts.
788 * If the `fsm' is thread and real system threads are being used, and this
789 * is called from the thread, it will return the SilcSchedule that was
790 * allocated by the FSM for the thread. It is strongly recommended to
791 * use this SilcSchedule if you are using real threads, and you need
792 * scheduler in the thread. Note that, once the thread finishes the
793 * returned SilcSchedule becomes invalid.
795 * In other times this returns the SilcSchedule pointer that was given
796 * to silc_fsm_alloc or silc_fsm_init.
799 SilcSchedule silc_fsm_get_schedule(void *fsm);
801 /****f* silcutil/SilcFSMAPI/silc_fsm_get_machine
805 * SilcFSM silc_fsm_get_machine(SilcFSMThread thread);
809 * Returns the machine from the FSM thread indicated by `thread'.
812 SilcFSM silc_fsm_get_machine(SilcFSMThread thread);
814 /****f* silcutil/SilcFSMAPI/silc_fsm_is_started
818 * SilcBool silc_fsm_is_started(void *fsm);
822 * Returns TRUE if the machine or thread `fsm' has been started and has
823 * not been finished yet. This function is used with both SilcFSM and
824 * SilcFSMThread contexts.
827 SilcBool silc_fsm_is_started(void *fsm);
831 /****s* silcutil/SilcFSMAPI/SilcFSMSema
835 * typedef struct SilcFSMSemaObject *SilcFSMSema;
839 * The FSM semaphore context allocated with silc_fsm_sema_alloc. The
840 * caller must free it with silc_fsm_sema_free. It is also possible
841 * to use pre-allocated SilcFSMSemaStruct instead of SilcFSMSema context.
844 typedef struct SilcFSMSemaObject *SilcFSMSema;
846 /****s* silcutil/SilcFSMAPI/SilcFSMSemaStruct
850 * typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
854 * The FSM semaphore context that can be used as pre-allocated context.
855 * It is initialized with silc_fsm_sema_init. It need not be
859 typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
861 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_alloc
865 * SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
869 * Allocates FSM semaphore with initial value of `value'. Semaphores are
870 * counters for resources shared between machine and threads. Semaphores
871 * can be waited until the semaphore value is non-zero. The FSM will be
872 * suspended when waiting for semaphore. When the semaphore is incremented
873 * all that are waiting for the semaphore will be signalled and awaken.
875 * Semaphores can be used to wait for example when thread terminates, or
876 * when thread moves into a specific state, or to protect critical
877 * sections. The FSM semaphores can be used also in FSM threads that are
878 * executed in real system threads.
880 * Use the macros SILC_FSM_SEMA_WAIT and SILC_FSM_SEMA_TIMEDWAIT to wait
881 * for semaphore. Use the SILC_FSM_SEMA_POST macro to increment the
882 * counter and wake up all waiters.
885 SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
887 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_init
891 * void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
895 * Initializes a pre-allocates semaphore context. This call is
896 * equivalent to silc_fsm_sema_alloc except this use the pre-allocated
897 * context. This fuction does not allocate any memory.
900 void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
902 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_free
906 * void silc_fsm_sema_free(SilcFSMSema sema);
910 * Free the semaphore allocated by silc_fsm_sema_alloc function.
913 void silc_fsm_sema_free(SilcFSMSema sema);
915 /****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_WAIT
919 * SILC_FSM_SEMA_WAIT(semaphore)
923 * Macro used to wait for the `semaphore' to become non-zero. The
924 * machine will be suspended while it is waiting for the semaphore.
925 * This macro can only be used in FSM state functions. When the
926 * semaphore is signalled the FSM will re-enter the current state (or
927 * state that was set with silc_fsm_next before waiting).
931 * // Signalling example
932 * ctx->async_sema = silc_fsm_sema_alloc(fsm, 0);
935 * SILC_FSM_STATE(silc_foo_state)
939 * // Wait here for async call to complete
940 * SILC_FSM_SEMA_WAIT(ctx->async_sema);
942 * // Async call completed
943 * if (ctx->async_success == FALSE)
948 * // Mutual exclusion example
949 * ctx->lock = silc_fsm_sema_alloc(fsm, 1);
952 * SILC_FSM_STATE(silc_foo_state)
955 * SILC_FSM_SEMA_WAIT(ctx->lock);
956 * very critical stuff...
957 * SILC_FSM_SEMA_POST(ctx->lock);
962 #define SILC_FSM_SEMA_WAIT(sema) \
964 if (silc_fsm_sema_wait(sema, fsm) == 0) \
965 return SILC_FSM_WAIT; \
968 /****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_TIMEDWAIT
972 * SILC_FSM_SEMA_TIMEDWAIT(semaphore, seconds, useconds, timedout)
976 * Macro used to wait for the `semaphore' to become non-zero, or until
977 * the timeout specified by `seconds' and `useconds' has elapsed. If
978 * the timeout occurs before the semaphore becomes non-zero, the machine
979 * will wakeup. The `timedout' is SilcBool pointer and if it is
980 * non-NULL indication of whether timeout occurred or not is saved to
981 * the pointer. This macro can only be used in FSM state functions.
982 * When the semaphore is signalled or timedout the FSM will re-enter
983 * the current state (or state that was set with silc_fsm_next before
988 * SILC_FSM_STATE(silc_foo_state)
993 * // Wait here for async call to complete, or 10 seconds for timeout
994 * SILC_FSM_SEMA_TIMEDWAIT(ctx->async_sema, 10, 0, &timedout);
996 * // See if timeout occurred
997 * if (timedout == TRUE)
1000 * // Async call completed
1001 * if (ctx->async_success == FALSE)
1007 #define SILC_FSM_SEMA_TIMEDWAIT(sema, seconds, useconds, ret_to) \
1009 if (silc_fsm_sema_timedwait(sema, fsm, seconds, useconds, ret_to) == 0) \
1010 return SILC_FSM_WAIT; \
1013 /****f* silcutil/SilcFSMAPI/SILC_FSM_SEMA_POST
1017 * SILC_FSM_SEMA_POST(semaphore)
1021 * Increases the semaphore counter and awakens everybody that are
1022 * waiting for this semaphore. This macro never blocks. It can be
1023 * safely called at any place in state function and in asynchronous
1024 * callbacks or other functions.
1028 * SILC_FSM_STATE(silc_foo_async_completion)
1032 * // Notify all waiters
1033 * ctx->async_success = TRUE;
1034 * SILC_FSM_SEMA_POST(ctx->async_sema);
1039 #define SILC_FSM_SEMA_POST(sema) \
1041 silc_fsm_sema_post(sema); \
1044 #include "silcfsm_i.h"
1046 #endif /* SILCFSM_H */