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.
123 SILC_FSM_CONTINUE, /* Continue immediately to next state */
124 SILC_FSM_YIELD, /* Continue to next state through scheduler */
125 SILC_FSM_WAIT, /* Wait for some async call or timeout */
126 SILC_FSM_FINISH, /* Finish state machine and call destructor
131 /****f* silcutil/SilcFSMAPI/SilcFSMDestructor
135 * typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context,
136 * void *destructor_context);
140 * The destructor callback that was set in silc_fsm_alloc or in
141 * silc_fsm_init function. It will be called when a state function
142 * returns SILC_FSM_FINISH. This function will be called through
143 * the scheduler; it will not be called immediately after the state
144 * function returns SILC_FSM_FINISH, but will be called later.
145 * The `fsm' may be freed in this function.
148 typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context,
149 void *destructor_context);
151 /****f* silcutil/SilcFSMAPI/SilcFSMThreadDestructor
155 * typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread,
156 * void *thread_context,
157 * void *destructor_context);
161 * The destructor callback that was set in silc_fsm_thread_alloc or in
162 * silc_fsm_thread_init function. It will be called when a state function
163 * returns SILC_FSM_FINISH. This function will be called through
164 * the scheduler; it will not be called immediately after the state
165 * function returns SILC_FSM_FINISH, but will be called later. The
166 * `thread' may be freed in this function.
170 * Even if the `thread' was executed in real system thread, this callback
171 * is always received in the main machine thread, not in the created
175 typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread,
176 void *thread_context,
177 void *destructor_context);
179 /****d* silcutil/SilcFSMAPI/SILC_FSM_STATE
183 * #define SILC_FSM_STATE(name)
187 * This macro is used to declare a FSM state function. The `fsm' is
188 * the SilcFSM or SilcFSMThread context, the `fsm_context' is the context
189 * given as argument to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_init,
190 * or silc_fsm_thread_alloc function. The `state_context' is the optional
191 * state specific context set with silc_fsm_set_state_context function.
195 #define SILC_FSM_STATE(name) \
196 SilcFSMStatus name(struct SilcFSMObject *fsm, void *fsm_context, \
200 /* State function callback */
201 typedef SilcFSMStatus (*SilcFSMStateCallback)(struct SilcFSMObject *fsm,
203 void *state_context);
205 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL
209 * SILC_FSM_CALL(function)
213 * Macro used to call asynchronous calls from state function. If the
214 * call is not really asynchronous then this will cause the machine to
215 * directly proceed to next state. If the call is truly asynchronous
216 * then this will set the machine to wait state. The silc_fsm_next
217 * must be called before this macro, so that the next state is set.
221 * The state function returns in this macro.
226 * silc_fsm_next(fsm, some_next_state);
227 * SILC_FSM_CALL(silc_some_async_call(server, some_callback, context));
229 * // More complex example
230 * silc_fsm_next(fsm, some_next_state);
231 * SILC_FSM_CALL((some_context->operation =
232 * silc_some_async_call(server, some_callback, context)));
235 #define SILC_FSM_CALL(function) \
237 assert(!silc_fsm_set_call(fsm, TRUE)); \
239 if (!silc_fsm_set_call(fsm, FALSE)) \
240 return SILC_FSM_CONTINUE; \
241 return SILC_FSM_WAIT; \
244 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE
248 * SILC_FSM_CALL_CONTINUE(fsm)
252 * Macro used to proceed after asynchornous call. This is called in the
253 * callback of the asynchronous call to continue in the state machine.
257 * void some_callback(void *context) {
258 * SilcFSM fsm = context;
260 * // Continue to the next state
261 * SILC_FSM_CALL_CONTINUE(fsm);
265 #define SILC_FSM_CALL_CONTINUE(fsm) \
267 if (!silc_fsm_set_call(fsm, FALSE)) \
268 silc_fsm_continue(fsm); \
271 /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL_CONTINUE_SYNC
275 * SILC_FSM_CALL_CONTINUE_SYNC(fsm)
279 * Macro used to proceed after asynchornous call. This is called in the
280 * callback of the asynchronous call to continue in the state machine.
281 * This continues to the next state synchronously, not through the
286 * void some_callback(void *context) {
287 * SilcFSM fsm = context;
289 * // Continue to the next state immediately
290 * SILC_FSM_CALL_CONTINUE_SYNC(fsm);
294 #define SILC_FSM_CALL_CONTINUE_SYNC(fsm) \
296 if (!silc_fsm_set_call(fsm, FALSE)) \
297 silc_fsm_continue_sync(fsm); \
300 /****d* silcutil/SilcFSMAPI/SILC_FSM_THREAD_WAIT
304 * SILC_FSM_THREAD_WAIT(thread)
308 * Macro used to wait for the `thread' to terminate. The machine or
309 * thread will be suspended while it is waiting for the thread to
310 * terminate. If the thread to be waited has already terminated (but
311 * the context has not been freed yet), this will continue immediately
312 * to the following state without waiting.
316 * The state function returns in this macro.
318 * This macro is the only way to safely make sure that the thread has
319 * terminated by the time FSM continues from the waiting state. Using
320 * semaphores to signal from the thread before SILC_FSM_FINISH is returned
321 * works with normal FSM threads, but especially with real system threads
322 * it does not guarantee that the FSM won't continue before the thread has
323 * actually terminated. Usually this is not a problem, but it can be a
324 * problem if the FSM is waiting to be freed. In this case using this
325 * macro is strongly recommended.
328 #define SILC_FSM_THREAD_WAIT(thread) \
330 if (silc_fsm_thread_wait(fsm, thread)) \
331 return SILC_FSM_WAIT; \
332 return SILC_FSM_CONTINUE; \
335 /****f* silcutil/SilcFSMAPI/silc_fsm_alloc
339 * SilcFSM silc_fsm_alloc(void *fsm_context,
340 * SilcFSMDestructor destructor,
341 * void *destructor_context,
342 * SilcSchedule schedule);
346 * Allocates SILC Finite State Machine context. The `destructor' with
347 * `destructor_context' will be called when the machines finishes. The
348 * caller must free the returned context with silc_fsm_free. The
349 * `fsm_context' is delivered to every FSM state function. The `schedule'
350 * is the caller's scheduler and the FSM will be run in the scheduler.
354 * SilcAsyncOperation silc_async_call(Callback callback, void *cb_context)
356 * SilcAsyncOperation op;
360 * // Allocate async operation so that caller can control us, like abort
361 * op = silc_async_alloc(silc_async_call_abort, NULL, ourcontext);
364 * fsm = silc_fsm_alloc(ourcontext, fsm_destructor, ourcontext,
366 * silc_fsm_start(fsm, first_state);
369 * // Return async operation for upper layer
374 SilcFSM silc_fsm_alloc(void *fsm_context,
375 SilcFSMDestructor destructor,
376 void *destructor_context,
377 SilcSchedule schedule);
379 /****f* silcutil/SilcFSMAPI/silc_fsm_init
383 * SilcBool silc_fsm_init(SilcFSM fsm,
385 * SilcFSMDestructor destructor,
386 * void *destructor_context,
387 * SilcSchedule schedule);
391 * Initializes a pre-allocated SilcFSM context. This call is equivalent
392 * to silc_fsm_alloc except that this takes the pre-allocated context
393 * as argument. The silc_fsm_free must not be called if this was called.
394 * Returns TRUE if the initialization is Ok or FALSE if error occurred.
395 * This function does not allocate any memory. The `schedule' is the
396 * caller's scheduler and the FSM will be run in the scheduler.
402 * silc_fsm_init(&fsm, application, fsm_destructor, application, schedule);
403 * silc_fsm_start(&fsm, first_state);
406 SilcBool silc_fsm_init(SilcFSM fsm,
408 SilcFSMDestructor destructor,
409 void *destructor_context,
410 SilcSchedule schedule);
412 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_alloc
416 * SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
417 * void *thread_context,
418 * SilcFSMThreadDestructor destructor,
419 * void *destructor_context,
420 * SilcBool real_thread);
424 * Allocates FSM thread context. The thread will be executed in the
425 * FSM machine indicated by `fsm'. The caller must free the returned
426 * thread context with silc_fsm_free. If the 'real_thread' is TRUE
427 * then the thread will actually be executed in real thread, if platform
428 * supports them. The `thread_context' is delivered to every state
429 * function in the thread.
433 * Note the limitations on using `real_thread' boolean to indicate running
434 * the FSM thread in a real system thread:
436 * If the system does not support threads, then this function will revert
437 * back to normal FSM threads.
439 * If the `real_thread' is TRUE then FSM will allocate new SilcSchedule
440 * for the FSM thread. This is done because the SilcSchedule that the
441 * `fsm' use cannot be used in the thread. This is limitation in the
442 * SilcSchedule implementation. If you need scheduler in the real thread
443 * it is strongly recommended that you use the SilcSchedule that is
444 * allocated for the thread. You can retrieve the SilcSchedule from the
445 * thread using silc_fsm_get_schedule function. Note that, the allocated
446 * SilcSchedule will become invalid after the thread finishes.
448 * You may still however use the original SilcSchedule if you wish. In
449 * this case note its limitation: you may only add and/or remove tasks,
450 * tasks cannot be executed in the thread. You will need to deliver the
451 * original SilcSchedule to the thread in the `thread_context' if you wish
454 * If `real_thread' is FALSE then no limitations on what can be run in
455 * the thread exist. In this case silc_fsm_get_schedule will return
456 * the SilcSchedule that was originally given to silc_fsm_alloc or
461 * SILC_FSM_STATE(silc_foo_state)
463 * SilcFSMThread thread;
466 * // Execute the route lookup in thread
467 * thread = silc_fsm_thread_alloc(fsm, fsm_context, NULL, NULL, FALSE);
468 * silc_fsm_start(thread, silc_route_lookup_start);
470 * // Wait here for the thread to terminate. Set the state where to go
471 * // after the thread has terminated.
472 * silc_fsm_next(fsm, silc_foo_route_lookup_finished);
473 * SILC_FSM_THREAD_WAIT(thread);
477 SilcFSMThread silc_fsm_thread_alloc(SilcFSM fsm,
478 void *thread_context,
479 SilcFSMThreadDestructor destructor,
480 void *destructor_context,
481 SilcBool real_thread);
483 /****f* silcutil/SilcFSMAPI/silc_fsm_thread_init
487 * void silc_fsm_thread_init(SilcFSMThread thread,
489 * void *thread_context,
490 * SilcFSMThreadDestructor destructor,
491 * void *destructor_context,
492 * SilcBool real_thread);
496 * Initializes a pre-allocated SilcFSMThread context. This call is
497 * equivalent to silc_fsm_thread_alloc except that this takes the
498 * pre-allocated context as argument. The silc_fsm_free must not be
499 * called if this was called. If the `real_thread' is TRUE then the
500 * thread will actually be executed in real thread, if platform supports
505 * See the notes from the silc_fsm_thread_alloc.
509 * SilcFSMThreadStruct thread;
511 * silc_fsm_thread_init(&thread, fsm, application, NULL, NULL, FALSE);
512 * silc_fsm_start(&thread, first_state);
515 void silc_fsm_thread_init(SilcFSMThread thread,
517 void *thread_context,
518 SilcFSMThreadDestructor destructor,
519 void *destructor_context,
520 SilcBool real_thread);
522 /****f* silcutil/SilcFSMAPI/silc_fsm_free
526 * void silc_fsm_free(void *fsm);
530 * Free the SILC FSM context that was allocated with silc_fsm_alloc,
531 * or free the SILC FSM thread context that was allocated with
532 * silc_fsm_thread_alloc. This function is used with both SilcFSM
533 * and SilcFSMThread contexts.
537 * When freeing FSM, it must not have any active threads.
540 void silc_fsm_free(void *fsm);
542 /****f* silcutil/SilcFSMAPI/silc_fsm_start
546 * void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
550 * This function must be called after the SILC FSM context was created.
551 * This actually starts the state machine. Note that, the machine is
552 * started later after this function returns. The `start_state' is the
553 * state where the machine or thread is started. This function is used
554 * with both SilcFSM and SilcFSMThread contexts.
560 * fsm = silc_fsm_alloc(context, destructor, context, schedule);
561 * silc_fsm_start(fsm, first_state);
564 void silc_fsm_start(void *fsm, SilcFSMStateCallback start_state);
566 /****f* silcutil/SilcFSMAPI/silc_fsm_start_sync
570 * void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
574 * This function is same as silc_fsm_start, except that the FSM will
575 * be started immediately inside this function. After this function
576 * returns the `start_state' has already been executed. If the machine
577 * is completely synchronous (no waiting used in the machine) then
578 * the machine will have finished once this function returns. Also
579 * note that if the machine is completely synchronous the destructor
580 * will also be called from inside this function. This function is used
581 * with both SilcFSM and SilcFSMThread contexts.
584 void silc_fsm_start_sync(void *fsm, SilcFSMStateCallback start_state);
586 /****f* silcutil/SilcFSMAPI/silc_fsm_next
590 * void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
594 * Set the next state to be executed. If the state function that
595 * call this function returns SILC_FSM_CONTINUE, the `next_state'
596 * will be executed immediately. If it returns SILC_FSM_YIELD it
597 * yields the thread and the `next_state' will be run after other
598 * threads have run first. This function must always be used to set
599 * the next state in the machine or thread. This function is used
600 * with both SilcFSM and SilcFSMThread contexts.
604 * // Move to next state
605 * silc_fsm_next(fsm, next_state);
606 * return SILC_FSM_CONTINUE;
609 void silc_fsm_next(void *fsm, SilcFSMStateCallback next_state);
611 /****f* silcutil/SilcFSMAPI/silc_fsm_next_later
615 * void silc_fsm_next_later(void *fsm, SilcFSMStateCallback next_state,
616 * SilcUInt32 seconds, SilcUInt32 useconds);
620 * Set the next state to be executed later, at the specified time.
621 * The SILC_FSM_WAIT must be returned in the state function if this
622 * function is called. If any other state is returned machine operation
623 * is undefined. The machine or thread will move to `next_state' after
624 * the specified timeout. This function is used with both SilcFSM and
625 * SilcFSMThread contexts.
629 * If both `seconds' and `useconds' are 0, the effect is same as calling
630 * silc_fsm_next function, and SILC_FSM_CONTINUE must be returned.
632 * If silc_fsm_continue or silc_fsm_continue_sync is called while the
633 * machine or thread is in SILC_FSM_WAIT state the timeout is automatically
634 * canceled and the state moves to the next state.
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_continue
650 * void silc_fsm_continue(void *fsm);
654 * Continues in the state machine from a SILC_FSM_WAIT state. This can
655 * be called from outside waiting FSM to continue to the next state.
656 * This function can be used instead of SILC_FSM_CALL_CONTINUE macro
657 * in case the SILC_FSM_CALL was not used. This must not be used if
658 * SILC_FSM_CALL was used. This function is used with both SilcFSM and
659 * SilcFSMThread contexts.
662 void silc_fsm_continue(void *fsm);
664 /****f* silcutil/SilcFSMAPI/silc_fsm_continue_sync
668 * void silc_fsm_continue_sync(void *fsm);
672 * Continues immediately in the state machine from a SILC_FSM_WAIT state.
673 * This can be called from outside waiting FSM to immediately continue to
674 * the next state. This function can be used instead of the
675 * SILC_FSM_CALL_CONTINUE_SYNC macro in case the SILC_FSM_CALL was not used.
676 * This must not be used if SILC_FSM_CALL was used. This function is used
677 * with both SilcFSM and SilcFSMThread contexts.
680 void silc_fsm_continue_sync(void *fsm);
682 /****f* silcutil/SilcFSMAPI/silc_fsm_finish
686 * void silc_fsm_finish(void *fsm);
690 * Finishes the `fsm'. This function may be used in case the FSM
691 * needs to be finished outside FSM states. Usually FSM is finished
692 * by returning SILC_FSM_FINISH from the state, but if this is not
693 * possible this function may be called. This function is used with
694 * both SilcFSM and SilcFSMThread contexts.
697 void silc_fsm_finish(void *fsm);
699 /****f* silcutil/SilcFSMAPI/silc_fsm_set_context
703 * void silc_fsm_set_context(void *fsm, void *fsm_context);
707 * Set new context for the `fsm'. This function can be used to change
708 * the context inside the `fsm', if needed. This function is used with
709 * both SilcFSM and SilcFSMThread contexts. The context is the
710 * `fsm_context' in the state function (SILC_FSM_STATE).
713 void silc_fsm_set_context(void *fsm, void *fsm_context);
715 /****f* silcutil/SilcFSMAPI/silc_fsm_get_context
719 * void *silc_fsm_get_context(void *fsm);
723 * Returns the context associated with the `fsm'. It is the context that
724 * was given to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_alloc or
725 * silc_fsm_thread_init. This function is used with both SilcFSM and
726 * SilcFSMThread contexts.
729 void *silc_fsm_get_context(void *fsm);
731 /****f* silcutil/SilcFSMAPI/silc_fsm_set_state_context
735 * void silc_fsm_set_state_context(void *fsm, void *state_context);
739 * Set's a state specific context for the `fsm'. This function can be
740 * used to change the state context inside the `fsm', if needed. This
741 * function is used with both SilcFSM and SilcFSMThread contexts. The
742 * context is the `state_context' in the state function (SILC_FSM_STATE).
745 void silc_fsm_set_state_context(void *fsm, void *state_context);
747 /****f* silcutil/SilcFSMAPI/silc_fsm_get_state_context
751 * void *silc_fsm_get_state_context(void *fsm);
755 * Returns the state context associated with the `fsm'. It is the context
756 * that was set with silc_fsm_set_state_context function. This function
757 * is used with both SilcFSM and SilcFSMThread contexts.
760 void *silc_fsm_get_state_context(void *fsm);
762 /****f* silcutil/SilcFSMAPI/silc_fsm_get_schedule
766 * SilcSchedule silc_fsm_get_schedule(void *fsm);
770 * Returns the SilcSchedule that has been associated with the `fsm'.
771 * If caller needs scheduler it may retrieve it with this function. This
772 * function is used with both SilcFSM and SilcFSMThread contexts.
774 * If the `fsm' is thread and real system threads are being used, and this
775 * is called from the thread, it will return the SilcSchedule that was
776 * allocated by the FSM for the thread. It is strongly recommended to
777 * use this SilcSchedule if you are using real threads, and you need
778 * scheduler in the thread. Note that, once the thread finishes the
779 * returned SilcSchedule becomes invalid.
781 * In other times this returns the SilcSchedule pointer that was given
782 * to silc_fsm_alloc or silc_fsm_init.
785 SilcSchedule silc_fsm_get_schedule(void *fsm);
787 /****f* silcutil/SilcFSMAPI/silc_fsm_get_machine
791 * SilcFSM silc_fsm_get_machine(SilcFSMThread thread);
795 * Returns the machine from the FSM thread indicated by `thread'.
798 SilcFSM silc_fsm_get_machine(SilcFSMThread thread);
800 /****f* silcutil/SilcFSMAPI/silc_fsm_is_started
804 * SilcBool silc_fsm_is_started(void *fsm);
808 * Returns TRUE if the machine or thread `fsm' has been started and has
809 * not been finished yet. This function is used with both SilcFSM and
810 * SilcFSMThread contexts.
813 SilcBool silc_fsm_is_started(void *fsm);
817 /****s* silcutil/SilcFSMAPI/SilcFSMSema
821 * typedef struct SilcFSMSemaObject *SilcFSMSema;
825 * The FSM semaphore context allocated with silc_fsm_sema_alloc. The
826 * caller must free it with silc_fsm_sema_free. It is also possible
827 * to use pre-allocated SilcFSMSemaStruct instead of SilcFSMSema context.
830 typedef struct SilcFSMSemaObject *SilcFSMSema;
832 /****s* silcutil/SilcFSMAPI/SilcFSMSemaStruct
836 * typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
840 * The FSM semaphore context that can be used as pre-allocated context.
841 * It is initialized with silc_fsm_sema_init. It need not be
845 typedef struct SilcFSMSemaObject SilcFSMSemaStruct;
847 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_alloc
851 * SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
855 * Allocates FSM semaphore with initial value of `value'. Semaphores are
856 * counters for resources shared between machine and threads. Semaphores
857 * can be waited until the semaphore value is non-zero. The FSM will be
858 * suspended when waiting for semaphore. When the semaphore is incremented
859 * all that are waiting for the semaphore will be signalled and awaken.
861 * Semaphores can be used to wait for example when thread terminates, or
862 * when thread moves into a specific state, or to protect critical
863 * sections. The FSM semaphores can be used also in FSM threads that are
864 * executed in real system threads.
866 * Use the macros SILC_FSM_SEMA_WAIT and SILC_FSM_SEMA_TIMEDWAIT to wait
867 * for semaphore. Use the SILC_FSM_SEMA_POST macro to increment the
868 * counter and wake up all waiters.
871 SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value);
873 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_init
877 * void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
881 * Initializes a pre-allocates semaphore context. This call is
882 * equivalent to silc_fsm_sema_alloc except this use the pre-allocated
883 * context. This fuction does not allocate any memory.
886 void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value);
888 /****f* silcutil/SilcFSMAPI/silc_fsm_sema_free
892 * void silc_fsm_sema_free(SilcFSMSema sema);
896 * Free the semaphore allocated by silc_fsm_sema_alloc function.
899 void silc_fsm_sema_free(SilcFSMSema sema);
901 /****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_WAIT
905 * SILC_FSM_SEMA_WAIT(semaphore)
909 * Macro used to wait for the `semaphore' to become non-zero. The
910 * machine will be suspended while it is waiting for the semaphore.
911 * This macro can only be used in FSM state functions. When the
912 * semaphore is signalled the FSM will re-enter the current state (or
913 * state that was set with silc_fsm_next before waiting).
917 * // Signalling example
918 * ctx->async_sema = silc_fsm_sema_alloc(fsm, 0);
921 * SILC_FSM_STATE(silc_foo_state)
925 * // Wait here for async call to complete
926 * SILC_FSM_SEMA_WAIT(ctx->async_sema);
928 * // Async call completed
929 * if (ctx->async_success == FALSE)
934 * // Mutual exclusion example
935 * ctx->lock = silc_fsm_sema_alloc(fsm, 1);
938 * SILC_FSM_STATE(silc_foo_state)
941 * SILC_FSM_SEMA_WAIT(ctx->lock);
942 * very critical stuff...
943 * SILC_FSM_SEMA_POST(ctx->lock);
948 #define SILC_FSM_SEMA_WAIT(sema) \
950 if (silc_fsm_sema_wait(sema, fsm) == 0) \
951 return SILC_FSM_WAIT; \
954 /****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_TIMEDWAIT
958 * SILC_FSM_SEMA_TIMEDWAIT(semaphore, seconds, useconds, timedout)
962 * Macro used to wait for the `semaphore' to become non-zero, or until
963 * the timeout specified by `seconds' and `useconds' has elapsed. If
964 * the timeout occurs before the semaphore becomes non-zero, the machine
965 * will wakeup. The `timedout' is SilcBool pointer and if it is
966 * non-NULL indication of whether timeout occurred or not is saved to
967 * the pointer. This macro can only be used in FSM state functions.
968 * When the semaphore is signalled or timedout the FSM will re-enter
969 * the current state (or state that was set with silc_fsm_next before
974 * SILC_FSM_STATE(silc_foo_state)
979 * // Wait here for async call to complete, or 10 seconds for timeout
980 * SILC_FSM_SEMA_TIMEDWAIT(ctx->async_sema, 10, 0, &timedout);
982 * // See if timeout occurred
983 * if (timedout == TRUE)
986 * // Async call completed
987 * if (ctx->async_success == FALSE)
993 #define SILC_FSM_SEMA_TIMEDWAIT(sema, seconds, useconds, ret_to) \
995 if (silc_fsm_sema_timedwait(sema, fsm, seconds, useconds, ret_to) == 0) \
996 return SILC_FSM_WAIT; \
999 /****f* silcutil/SilcFSMAPI/SILC_FSM_SEMA_POST
1003 * SILC_FSM_SEMA_POST(semaphore)
1007 * Increases the semaphore counter and awakens everybody that are
1008 * waiting for this semaphore. This macro never blocks. It can be
1009 * safely called at any place in state function and in asynchronous
1010 * callbacks or other functions.
1014 * SILC_FSM_STATE(silc_foo_async_completion)
1018 * // Notify all waiters
1019 * ctx->async_success = TRUE;
1020 * SILC_FSM_SEMA_POST(ctx->async_sema);
1025 #define SILC_FSM_SEMA_POST(sema) \
1027 silc_fsm_sema_post(sema); \
1030 #include "silcfsm_i.h"
1032 #endif /* SILCFSM_H */