Author: Pekka Riikonen <priikone@silcnet.org>
- 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
*
* 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.
*
***/
***/
typedef struct SilcFSMObject *SilcFSMThread;
-/****s* silcutil/SilcFSMAPI/SilcFSM
+/****s* silcutil/SilcFSMAPI/SilcFSMThreadStruct
*
* NAME
*
***/
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 returned from state
+ * functions to immediately move to next state.
*
- * 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;
-/***/
+ * 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 ...
+ *
+ * DESCRIPTION
+ *
+ * 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.
+ *
+ ***/
+#define SILC_FSM_YIELD SILC_FSM_ST_YIELD;
+
+/****d* silcutil/SilcFSMAPI/SILC_FSM_WAIT
+ *
+ * NAME
+ *
+ * #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
*
* 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,
*
* 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
*
*
* 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
* 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
*
***/
#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; \
*
* 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. If the thread to be waited has already terminated (but
- * the context has not been freed yet), this will continue immediately
- * to the following state without waiting.
+ * terminate. The machine or thread will continue once the waited
+ * thread has terminated.
*
* NOTES
*
*
* 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
***/
#define SILC_FSM_THREAD_WAIT(thread) \
do { \
- if (silc_fsm_thread_wait(fsm, thread)) \
- return SILC_FSM_WAIT; \
- return SILC_FSM_CONTINUE; \
+ silc_fsm_thread_wait(fsm, thread); \
+ return SILC_FSM_WAIT; \
} while(0)
/****f* silcutil/SilcFSMAPI/silc_fsm_alloc
* caller must free the returned context with silc_fsm_free. The
* `fsm_context' is delivered to every FSM state function. The `schedule'
* is the caller's scheduler and the FSM will be run in the scheduler.
+ * If `schedule' is NULL this will call silc_schedule_get_global to try
+ * get global scheduler. Returns NULL on error or if system is out of
+ * memory and sets silc_errno.
*
* EXAMPLE
*
* as argument. The silc_fsm_free must not be called if this was called.
* Returns TRUE if the initialization is Ok or FALSE if error occurred.
* This function does not allocate any memory. The `schedule' is the
- * caller's scheduler and the FSM will be run in the scheduler.
+ * caller's scheduler and the FSM will be run in the scheduler. If
+ * `schedule' is NULL this will call silc_schedule_get_global to try to
+ * get global scheduler.
*
* EXAMPLE
*
* thread context with silc_fsm_free. If the 'real_thread' is TRUE
* then the thread will actually be executed in real thread, if platform
* supports them. The `thread_context' is delivered to every state
- * function in the thread.
+ * function in the thread. Returns NULL on error or if the system is out
+ * of memory and sets silc_errno.
*
* 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
- * 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
+ * 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. The new scheduler is a child
+ * scheduler of the original scheduler used with `fsm'. Note that, the
+ * allocated SilcSchedule will become invalid after the thread finishes.
+ * Note also that the scheduler is automatically set as global scheduler
+ * in that thread by calling silc_schedule_set_global.
+ *
+ * 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.
*
***/
void silc_fsm_continue_sync(void *fsm);
+/****f* silcutil/SilcFSMAPI/silc_fsm_finish
+ *
+ * SYNOPSIS
+ *
+ * void silc_fsm_finish(void *fsm);
+ *
+ * DESCRIPTION
+ *
+ * Finishes the `fsm'. This function may be used in case the FSM
+ * needs to be finished outside FSM states. Usually FSM is finished
+ * by returning SILC_FSM_FINISH from the state, but if this is not
+ * possible this function may be called. This function is used with
+ * both SilcFSM and SilcFSMThread contexts.
+ *
+ * If the `fsm' is a machine and it has running threads, the machine
+ * will fatally fail. The caller must first finish the threads and
+ * then the machine.
+ *
+ ***/
+void silc_fsm_finish(void *fsm);
+
/****f* silcutil/SilcFSMAPI/silc_fsm_set_context
*
* SYNOPSIS
***/
SilcFSM silc_fsm_get_machine(SilcFSMThread thread);
+/****f* silcutil/SilcFSMAPI/silc_fsm_is_started
+ *
+ * SYNOPSIS
+ *
+ * SilcBool silc_fsm_is_started(void *fsm);
+ *
+ * DESCRIPTION
+ *
+ * Returns TRUE if the machine or thread `fsm' has been started and has
+ * not been finished yet. This function is used with both SilcFSM and
+ * SilcFSMThread contexts.
+ *
+ ***/
+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. The `fsm' must be the machine, not a thread. Returns NULL
+ * if system is out of memory or `fsm' is not FSM machine.
*
- * 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
- * context. This fuction does not allocate any memory.
+ * 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. The `fsm'
+ * must be the machine, not a thread.
*
***/
-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)
* ...
*
* // 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)
* ...
* }
*
- * // 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).
*
* ...
*
* // 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)
* }
*
***/
-#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
*
*
* // 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"