X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcutil%2Fsilcfsm.h;h=d9c4be0f07054a0420a53fca75bec04975fc0624;hb=e9374395ec9747bddd3ea0bfd3e5a17717e97b31;hp=481385675798d64dabacb0428b5e6d6afa4ce9fd;hpb=d3a63a151997595647efe4b770283d5b2abd43ad;p=silc.git diff --git a/lib/silcutil/silcfsm.h b/lib/silcutil/silcfsm.h index 48138567..d9c4be0f 100644 --- a/lib/silcutil/silcfsm.h +++ b/lib/silcutil/silcfsm.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2005, 2006 Pekka Riikonen + Copyright (C) 2005, 2006, 2007 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,18 +23,21 @@ * * SILC FSM Interface implements a finite state machine. The FSM can be * used to implement all kinds of machines and protocols. The FSM supports - * also threads and can be synchronized by using FSM semaphores. The FSM + * also threads and can be synchronized by using mutex locks. The FSM * also supports real system threads. It is possible to create new FSM * thread and then execute in real system thread, if platform supports * threads. * - * The FSM provides semaphores because of their versatility. The FSM - * semaphores can be used as mutual exclusion locks to protect critical - * sections, and as conditional variables and signallers. The FSM - * semaphores can safely be used to synchronize also FSM threads that are - * executed in real system threads. This makes SILC FSM very effective - * tool to implement complex machines whether they are executed in single - * thread or in multiple threads. + * The FSM provides also asynchronous events that can be used to wait for + * some events or states to occur. The FSM events may be used as condition + * variables and signallers. The FSM events can safely be used in FSM + * threads that are executed in real system threads. + * + * To synchronize machines that use FSM threads that are executed in real + * system threads the SILC Mutex API (silcmutex.h) may be used. Normal + * multi-threaded coding conventions apply when programming with real FSM + * threads. If the FSM threads are not real system threads, synchronization + * is not required. * ***/ @@ -91,7 +94,7 @@ typedef struct SilcFSMObject SilcFSMStruct; ***/ typedef struct SilcFSMObject *SilcFSMThread; -/****s* silcutil/SilcFSMAPI/SilcFSM +/****s* silcutil/SilcFSMAPI/SilcFSMThreadStruct * * NAME * @@ -106,40 +109,82 @@ typedef struct SilcFSMObject *SilcFSMThread; ***/ typedef struct SilcFSMObject SilcFSMThreadStruct; -/****d* silcutil/SilcFSMAPI/SilcFSMStatus +/****d* silcutil/SilcFSMAPI/SILC_FSM_CONTINUE * * NAME * - * typedef enum { ... } SilcFSMStatus; + * #define SILC_FSM_CONTINUE ... * * DESCRIPTION * - * Status values that the FSM state functions return. They dicatate - * how the machine will behave after returning from the state function. + * Moves to next state synchronously. This type is used is returned + * from state functions to immediately move to next state. + * + * EXAMPLE + * + * SILC_FSM_STATE(silc_foo_state) + * { + * ... + * + * // Move to next state now + * silc_fsm_next(fsm, silc_foo_next_state); + * return SILC_FSM_CONTINUE; + * } + * + ***/ +#if defined(SILC_DEBUG) +#define SILC_FSM_CONTINUE \ + fsm->next_state(fsm, fsm->fsm_context, fsm->state_context); +#else +#define SILC_FSM_CONTINUE SILC_FSM_ST_CONTINUE; +#endif /* SILC_DEBUG */ + +/****d* silcutil/SilcFSMAPI/SILC_FSM_YIELD + * + * NAME + * + * #define SILC_FSM_YIELD ... * - * The SILC_FSM_CONTINUE always moves to the next state synchronously. + * DESCRIPTION * - * The SILC_FSM_YIELD always moves to the next state through the - * scheduler. Other threads will get running time with SILC_FSM_YIELD. + * Moves to next state through the machine scheduler. Other threads + * running in the machine will get running time with SILC_FSM_YIELD. * When using real threads, using SILC_FSM_YIELD is usually unnecessary. + * This type is returned in the state function. * - * The SILC_FSM_WAIT will suspend the machine until it is awaken. + ***/ +#define SILC_FSM_YIELD SILC_FSM_ST_YIELD; + +/****d* silcutil/SilcFSMAPI/SILC_FSM_WAIT * - * The SILC_FSM_FINISH finished the machine or thread and calls its - * destructor, if defined. If the machine is finished when it has - * running threads the machine will fatally fail. User must always - * finish the threads before finishing the machine. + * NAME * - * SOURCE - */ -typedef enum { - SILC_FSM_CONTINUE, /* Continue immediately to next state */ - SILC_FSM_YIELD, /* Continue to next state through scheduler */ - SILC_FSM_WAIT, /* Wait for some async call or timeout */ - SILC_FSM_FINISH, /* Finish state machine and call destructor - through scheduler */ -} SilcFSMStatus; -/***/ + * #define SILC_FSM_WAIT ... + * + * DESCRIPTION + * + * Suspends the machine or thread until it is awaken. This is used + * when asynchronous call is made or timer is set, or something else + * that requires waiting. This type is returned in the state function. + * + ***/ +#define SILC_FSM_WAIT SILC_FSM_ST_WAIT + +/****d* silcutil/SilcFSMAPI/SILC_FSM_FINISH + * + * NAME + * + * #define SILC_FSM_FINISH ... + * + * DESCRIPTION + * + * Finishes the machine or thread and calls its destructor, if defined. + * If the machine is finished when it has running threads the machine + * will fatally fail. User must always finish the threads before + * finishing the machine. This type is returned in the state function. + * + ***/ +#define SILC_FSM_FINISH SILC_FSM_ST_FINISH /****f* silcutil/SilcFSMAPI/SilcFSMDestructor * @@ -154,8 +199,8 @@ typedef enum { * silc_fsm_init function. It will be called when a state function * returns SILC_FSM_FINISH. This function will be called through * the scheduler; it will not be called immediately after the state - * function returns SILC_FSM_FINISH, but will be called later. - * The `fsm' may be freed in this function. + * function returns SILC_FSM_FINISH, but will be called later. The + * `fsm' can be freed in this function. * ***/ typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context, @@ -173,10 +218,10 @@ typedef void (*SilcFSMDestructor)(SilcFSM fsm, void *fsm_context, * * The destructor callback that was set in silc_fsm_thread_alloc or in * silc_fsm_thread_init function. It will be called when a state function - * returns SILC_FSM_FINISH. This function will be called through - * the scheduler; it will not be called immediately after the state - * function returns SILC_FSM_FINISH, but will be called later. The - * `thread' may be freed in this function. + * returns SILC_FSM_FINISH. This function will be called through the + * scheduler; it will not be called immediately after the state function + * returns SILC_FSM_FINISH, but will be called later. The `thread' can + * be freed in this function. * * NOTES * @@ -197,7 +242,7 @@ typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread, * * DESCRIPTION * - * This macro is used to declare a FSM state function. The `fsm' is + * This macro is used to declare an FSM state function. The `fsm' is * the SilcFSM or SilcFSMThread context, the `fsm_context' is the context * given as argument to silc_fsm_alloc, silc_fsm_init, silc_fsm_thread_init, * or silc_fsm_thread_alloc function. The `state_context' is the optional @@ -206,14 +251,13 @@ typedef void (*SilcFSMThreadDestructor)(SilcFSMThread thread, * SOURCE */ #define SILC_FSM_STATE(name) \ -SilcFSMStatus name(struct SilcFSMObject *fsm, void *fsm_context, \ - void *state_context) +int name(struct SilcFSMObject *fsm, void *fsm_context, void *state_context) /***/ /* State function callback */ -typedef SilcFSMStatus (*SilcFSMStateCallback)(struct SilcFSMObject *fsm, - void *fsm_context, - void *state_context); +typedef int (*SilcFSMStateCallback)(struct SilcFSMObject *fsm, + void *fsm_context, + void *state_context); /****d* silcutil/SilcFSMAPI/SILC_FSM_CALL * @@ -247,7 +291,7 @@ typedef SilcFSMStatus (*SilcFSMStateCallback)(struct SilcFSMObject *fsm, ***/ #define SILC_FSM_CALL(function) \ do { \ - assert(!silc_fsm_set_call(fsm, TRUE)); \ + SILC_VERIFY(!silc_fsm_set_call(fsm, TRUE)); \ function; \ if (!silc_fsm_set_call(fsm, FALSE)) \ return SILC_FSM_CONTINUE; \ @@ -320,7 +364,8 @@ do { \ * * Macro used to wait for the `thread' to terminate. The machine or * thread will be suspended while it is waiting for the thread to - * terminate. + * terminate. The machine or thread will continue once the waited + * thread has terminated. * * NOTES * @@ -328,7 +373,7 @@ do { \ * * This macro is the only way to safely make sure that the thread has * terminated by the time FSM continues from the waiting state. Using - * semaphores to signal from the thread before SILC_FSM_FINISH is returned + * FSM events to signal from the thread before SILC_FSM_FINISH is returned * works with normal FSM threads, but especially with real system threads * it does not guarantee that the FSM won't continue before the thread has * actually terminated. Usually this is not a problem, but it can be a @@ -440,29 +485,17 @@ SilcBool silc_fsm_init(SilcFSM fsm, * * NOTES * - * Note the limitations on using `real_thread' boolean to indicate running - * the FSM thread in a real system thread: - * * If the system does not support threads, then this function will revert * back to normal FSM threads. * * If the `real_thread' is TRUE then FSM will allocate new SilcSchedule - * for the FSM thread. This is done because the SilcSchedule that the - * `fsm' use cannot be used in the thread. This is limitation in the - * SilcSchedule implementation. If you need scheduler in the real thread - * it is strongly recommended that you use the SilcSchedule that is - * allocated for the thread. You can retrieve the SilcSchedule from the - * thread using silc_fsm_get_schedule function. Note that, the allocated + * for the FSM thread. If you need scheduler in the real thread it is + * strongly recommended that you use the SilcSchedule that is allocated + * for the thread. You can retrieve the SilcSchedule from the thread + * using silc_fsm_get_schedule function. Note that, the allocated * SilcSchedule will become invalid after the thread finishes. * - * You may still however use the original SilcSchedule if you wish. In - * this case note its limitation: you may only add and/or remove tasks, - * tasks cannot be executed in the thread. You will need to deliver the - * original SilcSchedule to the thread in the `thread_context' if you wish - * to use it. - * - * If `real_thread' is FALSE then no limitations on what can be run in - * the thread exist. In this case silc_fsm_get_schedule will return + * If `real_thread' is FALSE the silc_fsm_get_schedule will return * the SilcSchedule that was originally given to silc_fsm_alloc or * silc_fsm_init. * @@ -826,110 +859,107 @@ SilcFSM silc_fsm_get_machine(SilcFSMThread thread); ***/ SilcBool silc_fsm_is_started(void *fsm); -/* FSM Semaphores */ +/* FSM Events */ -/****s* silcutil/SilcFSMAPI/SilcFSMSema +/****s* silcutil/SilcFSMAPI/SilcFSMEvent * * NAME * - * typedef struct SilcFSMSemaObject *SilcFSMSema; + * typedef struct SilcFSMEventObject *SilcFSMEvent; * * DESCRIPTION * - * The FSM semaphore context allocated with silc_fsm_sema_alloc. The - * caller must free it with silc_fsm_sema_free. It is also possible - * to use pre-allocated SilcFSMSemaStruct instead of SilcFSMSema context. + * The FSM event context allocated with silc_fsm_event_alloc. The + * caller must free it with silc_fsm_event_free. It is also possible + * to use pre-allocated SilcFSMEventStruct instead of SilcFSMEvent context. * ***/ -typedef struct SilcFSMSemaObject *SilcFSMSema; +typedef struct SilcFSMEventObject *SilcFSMEvent; -/****s* silcutil/SilcFSMAPI/SilcFSMSemaStruct +/****s* silcutil/SilcFSMAPI/SilcFSMEventStruct * * NAME * - * typedef struct SilcFSMSemaObject SilcFSMSemaStruct; + * typedef struct SilcFSMEventObject SilcFSMEventStruct; * * DESCRIPTION * - * The FSM semaphore context that can be used as pre-allocated context. - * It is initialized with silc_fsm_sema_init. It need not be + * The FSM event context that can be used as pre-allocated context. + * It is initialized with silc_fsm_event_init. It need not be * uninitialized. * ***/ -typedef struct SilcFSMSemaObject SilcFSMSemaStruct; +typedef struct SilcFSMEventObject SilcFSMEventStruct; -/****f* silcutil/SilcFSMAPI/silc_fsm_sema_alloc +/****f* silcutil/SilcFSMAPI/silc_fsm_event_alloc * * SYNOPSIS * - * SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value); + * SilcFSMEvent silc_fsm_event_alloc(SilcFSM fsm); * * DESCRIPTION * - * Allocates FSM semaphore with initial value of `value'. Semaphores are - * counters for resources shared between machine and threads. Semaphores - * can be waited until the semaphore value is non-zero. The FSM will be - * suspended when waiting for semaphore. When the semaphore is incremented - * all that are waiting for the semaphore will be signalled and awaken. + * Allocates asynchronous FSM event. FSM events are asynchronous events + * that can be waited and signalled. They can be used as condition + * variables and signallers. They can be used for example to wait that + * some event happens, some thread moves to a specific state or similar. + * The FSM Events may also be used in FSM threads that are executed in + * real system threads. It is safe to wait and signal the event from + * threads. * - * Semaphores can be used to wait for example when thread terminates, or - * when thread moves into a specific state, or to protect critical - * sections. The FSM semaphores can be used also in FSM threads that are - * executed in real system threads. - * - * Use the macros SILC_FSM_SEMA_WAIT and SILC_FSM_SEMA_TIMEDWAIT to wait - * for semaphore. Use the SILC_FSM_SEMA_POST macro to increment the - * counter and wake up all waiters. + * Use the macros SILC_FSM_EVENT_WAIT and SILC_FSM_EVENT_TIMEDWAIT to wait + * for the event. Use the SILC_FSM_EVENT_SIGNAL macro to signal all the + * waiters. * ***/ -SilcFSMSema silc_fsm_sema_alloc(SilcFSM fsm, SilcUInt32 value); +SilcFSMEvent silc_fsm_event_alloc(SilcFSM fsm); -/****f* silcutil/SilcFSMAPI/silc_fsm_sema_init +/****f* silcutil/SilcFSMAPI/silc_fsm_event_init * * SYNOPSIS * - * void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value); + * void silc_fsm_event_init(SilcFSMEvent event, SilcFSM fsm); * * DESCRIPTION * - * Initializes a pre-allocates semaphore context. This call is - * equivalent to silc_fsm_sema_alloc except this use the pre-allocated + * Initializes a pre-allocates FSM event context. This call is + * equivalent to silc_fsm_event_alloc except this use the pre-allocated * context. This fuction does not allocate any memory. * ***/ -void silc_fsm_sema_init(SilcFSMSema sema, SilcFSM fsm, SilcUInt32 value); +void silc_fsm_event_init(SilcFSMEvent event, SilcFSM fsm); -/****f* silcutil/SilcFSMAPI/silc_fsm_sema_free +/****f* silcutil/SilcFSMAPI/silc_fsm_event_free * * SYNOPSIS * - * void silc_fsm_sema_free(SilcFSMSema sema); + * void silc_fsm_event_free(SilcFSMEvent event); * * DESCRIPTION * - * Free the semaphore allocated by silc_fsm_sema_alloc function. + * Free the event allocated by silc_fsm_event_alloc function. * ***/ -void silc_fsm_sema_free(SilcFSMSema sema); +void silc_fsm_event_free(SilcFSMEvent event); -/****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_WAIT +/****d* silcutil/SilcFSMAPI/SILC_FSM_EVENT_WAIT * * NAME * - * SILC_FSM_SEMA_WAIT(semaphore) + * SILC_FSM_EVENT_WAIT(event) * * DESCRIPTION * - * Macro used to wait for the `semaphore' to become non-zero. The - * machine will be suspended while it is waiting for the semaphore. + * Macro used to wait for the `event' to be signalled. The machine + * or thread will be suspended while it is waiting for the event. * This macro can only be used in FSM state functions. When the - * semaphore is signalled the FSM will re-enter the current state (or + * event is signalled the FSM will re-enter the current state (or * state that was set with silc_fsm_next before waiting). * * EXAMPLE * * // Signalling example - * ctx->async_sema = silc_fsm_sema_alloc(fsm, 0); + * ctx->async_event = silc_fsm_event_alloc(fsm); * ... * * SILC_FSM_STATE(silc_foo_state) @@ -937,7 +967,7 @@ void silc_fsm_sema_free(SilcFSMSema sema); * ... * * // Wait here for async call to complete - * SILC_FSM_SEMA_WAIT(ctx->async_sema); + * SILC_FSM_EVENT_WAIT(ctx->async_event); * * // Async call completed * if (ctx->async_success == FALSE) @@ -945,41 +975,28 @@ void silc_fsm_sema_free(SilcFSMSema sema); * ... * } * - * // Mutual exclusion example - * ctx->lock = silc_fsm_sema_alloc(fsm, 1); - * ... - * - * SILC_FSM_STATE(silc_foo_state) - * { - * ... - * SILC_FSM_SEMA_WAIT(ctx->lock); - * very critical stuff... - * SILC_FSM_SEMA_POST(ctx->lock); - * ... - * } - * ***/ -#define SILC_FSM_SEMA_WAIT(sema) \ +#define SILC_FSM_EVENT_WAIT(event) \ do { \ - if (silc_fsm_sema_wait(sema, fsm) == 0) \ + if (silc_fsm_event_wait(event, fsm) == 0) \ return SILC_FSM_WAIT; \ } while(0) -/****d* silcutil/SilcFSMAPI/SILC_FSM_SEMA_TIMEDWAIT +/****d* silcutil/SilcFSMAPI/SILC_FSM_EVENT_TIMEDWAIT * * NAME * - * SILC_FSM_SEMA_TIMEDWAIT(semaphore, seconds, useconds, timedout) + * SILC_FSM_EVENT_TIMEDWAIT(event, seconds, useconds, timedout) * * DESCRIPTION * - * Macro used to wait for the `semaphore' to become non-zero, or until + * Macro used to wait for the `event' to be signalled, or until * the timeout specified by `seconds' and `useconds' has elapsed. If - * the timeout occurs before the semaphore becomes non-zero, the machine + * the timeout occurs before the event is signalled, the machine * will wakeup. The `timedout' is SilcBool pointer and if it is * non-NULL indication of whether timeout occurred or not is saved to * the pointer. This macro can only be used in FSM state functions. - * When the semaphore is signalled or timedout the FSM will re-enter + * When the event is signalled or timedout the FSM will re-enter * the current state (or state that was set with silc_fsm_next before * waiting). * @@ -991,7 +1008,7 @@ do { \ * ... * * // Wait here for async call to complete, or 10 seconds for timeout - * SILC_FSM_SEMA_TIMEDWAIT(ctx->async_sema, 10, 0, &timedout); + * SILC_FSM_EVENT_TIMEDWAIT(ctx->async_event, 10, 0, &timedout); * * // See if timeout occurred * if (timedout == TRUE) @@ -1004,24 +1021,23 @@ do { \ * } * ***/ -#define SILC_FSM_SEMA_TIMEDWAIT(sema, seconds, useconds, ret_to) \ -do { \ - if (silc_fsm_sema_timedwait(sema, fsm, seconds, useconds, ret_to) == 0) \ - return SILC_FSM_WAIT; \ +#define SILC_FSM_EVENT_TIMEDWAIT(event, seconds, useconds, ret_to) \ +do { \ + if (silc_fsm_event_timedwait(event, fsm, seconds, useconds, ret_to) == 0) \ + return SILC_FSM_WAIT; \ } while(0) -/****f* silcutil/SilcFSMAPI/SILC_FSM_SEMA_POST +/****f* silcutil/SilcFSMAPI/SILC_FSM_EVENT_SIGNAL * * SYNOPSIS * - * SILC_FSM_SEMA_POST(semaphore) + * SILC_FSM_EVENT_SIGNAL(event) * * DESCRIPTION * - * Increases the semaphore counter and awakens everybody that are - * waiting for this semaphore. This macro never blocks. It can be - * safely called at any place in state function and in asynchronous - * callbacks or other functions. + * Signals the `event' and awakens everybody that are waiting for this + * event. This macro never blocks. It can be safely called at any place + * in state function and in asynchronous callbacks or other functions. * * EXAMPLE * @@ -1031,14 +1047,14 @@ do { \ * * // Notify all waiters * ctx->async_success = TRUE; - * SILC_FSM_SEMA_POST(ctx->async_sema); + * SILC_FSM_EVENT_SIGNAL(ctx->async_event); * ... * } * ***/ -#define SILC_FSM_SEMA_POST(sema) \ +#define SILC_FSM_EVENT_SIGNAL(event) \ do { \ - silc_fsm_sema_post(sema); \ + silc_fsm_event_signal(event); \ } while(0) #include "silcfsm_i.h"