blob: c40d7f3c263202f8338e2d6523cd05172ec25bdd [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file fsm.h
2 * Finite State Machine
3 */
4
Harald Welte136e7372016-05-29 10:53:17 +09005#pragma once
6
7#include <stdint.h>
8#include <stdbool.h>
9
10#include <osmocom/core/linuxlist.h>
11#include <osmocom/core/timer.h>
12#include <osmocom/core/utils.h>
13
14/*! \defgroup fsm Finite State Machine abstraction
15 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020016 * \file fsm.h */
Harald Welte136e7372016-05-29 10:53:17 +090017
18struct osmo_fsm_inst;
19
20enum osmo_fsm_term_cause {
Neels Hofmeyr87e45502017-06-20 00:17:59 +020021 /*! terminate because parent terminated */
Harald Welte136e7372016-05-29 10:53:17 +090022 OSMO_FSM_TERM_PARENT,
Neels Hofmeyr87e45502017-06-20 00:17:59 +020023 /*! terminate on explicit user request */
Harald Welte136e7372016-05-29 10:53:17 +090024 OSMO_FSM_TERM_REQUEST,
Neels Hofmeyr87e45502017-06-20 00:17:59 +020025 /*! regular termination of process */
Harald Welte136e7372016-05-29 10:53:17 +090026 OSMO_FSM_TERM_REGULAR,
Neels Hofmeyr87e45502017-06-20 00:17:59 +020027 /*! erroneous termination of process */
Harald Welte136e7372016-05-29 10:53:17 +090028 OSMO_FSM_TERM_ERROR,
Neels Hofmeyr87e45502017-06-20 00:17:59 +020029 /*! termination due to time-out */
Harald Weltef627c0f2016-06-18 10:36:25 +020030 OSMO_FSM_TERM_TIMEOUT,
Harald Welte136e7372016-05-29 10:53:17 +090031};
32
Neels Hofmeyr5c5c78a2016-12-14 18:35:47 +010033extern const struct value_string osmo_fsm_term_cause_names[];
34static inline const char *osmo_fsm_term_cause_name(enum osmo_fsm_term_cause cause)
35{
36 return get_value_string(osmo_fsm_term_cause_names, cause);
37}
38
39
Neels Hofmeyr87e45502017-06-20 00:17:59 +020040/*! description of a rule in the FSM */
Harald Welte136e7372016-05-29 10:53:17 +090041struct osmo_fsm_state {
Neels Hofmeyr87e45502017-06-20 00:17:59 +020042 /*! bit-mask of permitted input events for this state */
Harald Welte136e7372016-05-29 10:53:17 +090043 uint32_t in_event_mask;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020044 /*! bit-mask to which other states this state may transiton */
Harald Welte136e7372016-05-29 10:53:17 +090045 uint32_t out_state_mask;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020046 /*! human-readable name of this state */
Harald Welte136e7372016-05-29 10:53:17 +090047 const char *name;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020048 /*! function to be called for events arriving in this state */
Harald Welte136e7372016-05-29 10:53:17 +090049 void (*action)(struct osmo_fsm_inst *fi, uint32_t event, void *data);
Neels Hofmeyr87e45502017-06-20 00:17:59 +020050 /*! function to be called just after entering the state */
Harald Welte136e7372016-05-29 10:53:17 +090051 void (*onenter)(struct osmo_fsm_inst *fi, uint32_t prev_state);
Neels Hofmeyr87e45502017-06-20 00:17:59 +020052 /*! function to be called just before leaving the state */
Harald Welte136e7372016-05-29 10:53:17 +090053 void (*onleave)(struct osmo_fsm_inst *fi, uint32_t next_state);
54};
55
Neels Hofmeyr87e45502017-06-20 00:17:59 +020056/*! a description of an osmocom finite state machine */
Harald Welte136e7372016-05-29 10:53:17 +090057struct osmo_fsm {
Neels Hofmeyr87e45502017-06-20 00:17:59 +020058 /*! global list */
Harald Welte136e7372016-05-29 10:53:17 +090059 struct llist_head list;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020060 /*! list of instances of this FSM */
Harald Welte136e7372016-05-29 10:53:17 +090061 struct llist_head instances;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020062 /*! human readable name */
Harald Welte136e7372016-05-29 10:53:17 +090063 const char *name;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020064 /*! table of state transition rules */
Harald Welte136e7372016-05-29 10:53:17 +090065 const struct osmo_fsm_state *states;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020066 /*! number of entries in \ref states */
Harald Welte136e7372016-05-29 10:53:17 +090067 unsigned int num_states;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020068 /*! bit-mask of events permitted in all states */
Harald Welte136e7372016-05-29 10:53:17 +090069 uint32_t allstate_event_mask;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020070 /*! function pointer to be called for allstate events */
Harald Welte136e7372016-05-29 10:53:17 +090071 void (*allstate_action)(struct osmo_fsm_inst *fi, uint32_t event, void *data);
Neels Hofmeyr87e45502017-06-20 00:17:59 +020072 /*! clean-up function, called during termination */
Harald Welte136e7372016-05-29 10:53:17 +090073 void (*cleanup)(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause);
Neels Hofmeyr87e45502017-06-20 00:17:59 +020074 /*! timer call-back for states with time-out.
Neels Hofmeyrdda5e792016-12-09 16:10:34 +010075 * \returns 1 to request termination, 0 to keep running. */
Harald Weltef627c0f2016-06-18 10:36:25 +020076 int (*timer_cb)(struct osmo_fsm_inst *fi);
Neels Hofmeyr87e45502017-06-20 00:17:59 +020077 /*! logging sub-system for this FSM */
Harald Welte136e7372016-05-29 10:53:17 +090078 int log_subsys;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020079 /*! human-readable names of events */
Harald Welte136e7372016-05-29 10:53:17 +090080 const struct value_string *event_names;
Philipp Maierd1f57932018-02-14 18:20:07 +010081 /*! graceful exit function, called at the beginning of termination */
82 void (*pre_term)(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause);
Harald Welte136e7372016-05-29 10:53:17 +090083};
84
Neels Hofmeyr87e45502017-06-20 00:17:59 +020085/*! a single instanceof an osmocom finite state machine */
Harald Welte136e7372016-05-29 10:53:17 +090086struct osmo_fsm_inst {
Neels Hofmeyr87e45502017-06-20 00:17:59 +020087 /*! member in the fsm->instances list */
Harald Welte136e7372016-05-29 10:53:17 +090088 struct llist_head list;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020089 /*! back-pointer to the FSM of which we are an instance */
Harald Welte136e7372016-05-29 10:53:17 +090090 struct osmo_fsm *fsm;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020091 /*! human readable identifier */
Harald Welte136e7372016-05-29 10:53:17 +090092 const char *id;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020093 /*! human readable fully-qualified name */
Harald Welte136e7372016-05-29 10:53:17 +090094 const char *name;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020095 /*! some private data of this instance */
Harald Welte136e7372016-05-29 10:53:17 +090096 void *priv;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020097 /*! logging level for this FSM */
Harald Welte136e7372016-05-29 10:53:17 +090098 int log_level;
Neels Hofmeyr87e45502017-06-20 00:17:59 +020099 /*! current state of the FSM */
Harald Welte136e7372016-05-29 10:53:17 +0900100 uint32_t state;
101
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200102 /*! timer number for states with time-out */
Harald Welte136e7372016-05-29 10:53:17 +0900103 int T;
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200104 /*! timer back-end for states with time-out */
Harald Welte136e7372016-05-29 10:53:17 +0900105 struct osmo_timer_list timer;
106
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200107 /*! support for fsm-based procedures */
Harald Welte136e7372016-05-29 10:53:17 +0900108 struct {
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200109 /*! the parent FSM that has created us */
Harald Welte136e7372016-05-29 10:53:17 +0900110 struct osmo_fsm_inst *parent;
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200111 /*! the event we should send upon termination */
Harald Welte136e7372016-05-29 10:53:17 +0900112 uint32_t parent_term_event;
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200113 /*! a list of children processes */
Harald Welte136e7372016-05-29 10:53:17 +0900114 struct llist_head children;
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200115 /*! \ref llist_head linked to parent->proc.children */
Harald Welte136e7372016-05-29 10:53:17 +0900116 struct llist_head child;
117 } proc;
118};
119
120void osmo_fsm_log_addr(bool log_addr);
Neels Hofmeyr050f2d32018-05-31 15:30:15 +0200121void osmo_fsm_log_timeouts(bool log_timeouts);
Harald Welte136e7372016-05-29 10:53:17 +0900122
Neels Hofmeyr691ba522018-12-19 23:34:25 +0100123/*! Log using FSM instance's context, on explicit logging subsystem and level.
124 * \param fi An osmo_fsm_inst.
125 * \param subsys A logging subsystem, e.g. DLGLOBAL.
126 * \param level A logging level, e.g. LOGL_INFO.
127 * \param fmt printf-like format string.
128 * \param args Format string arguments.
129 */
130#define LOGPFSMSL(fi, subsys, level, fmt, args...) \
131 LOGPFSMSLSRC(fi, subsys, level, __FILE__, __LINE__, fmt, ## args)
132
133/*! Log using FSM instance's context, on explicit logging subsystem and level,
134 * and passing explicit source file and line information.
135 * \param fi An osmo_fsm_inst.
136 * \param subsys A logging subsystem, e.g. DLGLOBAL.
137 * \param level A logging level, e.g. LOGL_INFO.
138 * \param caller_file A string constant containing a source file path, like __FILE__.
139 * \param caller_line A number constant containing a source file line, like __LINE__.
140 * \param fmt printf-like format string.
141 * \param args Format string arguments.
142 */
143#define LOGPFSMSLSRC(fi, subsys, level, caller_file, caller_line, fmt, args...) \
144 LOGPSRC(subsys, level, \
145 caller_file, caller_line, \
146 "%s{%s}: " fmt, \
147 osmo_fsm_inst_name(fi), \
148 (fi) ? osmo_fsm_state_name((fi)->fsm, (fi)->state) : "fi=NULL", ## args)
149
150
Neels Hofmeyrb11ff7c2018-12-20 00:19:29 +0100151/*! Log using FSM instance's context, on explicit logging level.
152 * \param fi An osmo_fsm_inst.
153 * \param level A logging level, e.g. LOGL_INFO.
154 * \param fmt printf-like format string.
155 * \param args Format string arguments.
156 */
Neels Hofmeyr6a13e7f2016-12-14 17:37:34 +0100157#define LOGPFSML(fi, level, fmt, args...) \
Neels Hofmeyre5bde902018-12-20 00:00:11 +0100158 LOGPFSMLSRC(fi, level, __FILE__, __LINE__, fmt, ## args)
Neels Hofmeyr6a13e7f2016-12-14 17:37:34 +0100159
Neels Hofmeyrb11ff7c2018-12-20 00:19:29 +0100160/*! Log using FSM instance's context, on explicit logging level, and with explicit source file and line info.
161 * The log subsystem to log on is obtained from the underlying FSM definition.
162 * \param fi An osmo_fsm_inst.
163 * \param level A logging level, e.g. LOGL_INFO.
164 * \param caller_file A string constant containing a source file path, like __FILE__.
165 * \param caller_line A number constant containing a source file line, like __LINE__.
166 * \param fmt printf-like format string.
167 * \param args Format string arguments.
168 */
Neels Hofmeyreeacf902016-12-23 01:00:13 +0100169#define LOGPFSMLSRC(fi, level, caller_file, caller_line, fmt, args...) \
Neels Hofmeyr691ba522018-12-19 23:34:25 +0100170 LOGPFSMSLSRC(fi, (fi) ? (fi)->fsm->log_subsys : DLGLOBAL, level, \
171 caller_file, caller_line, fmt, ## args)
Neels Hofmeyreeacf902016-12-23 01:00:13 +0100172
Neels Hofmeyrb11ff7c2018-12-20 00:19:29 +0100173/*! Log using FSM instance's context.
174 * The log level to log on is obtained from the FSM instance.
175 * The log subsystem to log on is obtained from the underlying FSM definition.
176 * \param fi An osmo_fsm_inst.
177 * \param fmt printf-like format string.
178 * \param args Format string arguments.
179 */
Neels Hofmeyre5bde902018-12-20 00:00:11 +0100180#define LOGPFSM(fi, fmt, args...) \
Neels Hofmeyrb0b39af2018-12-20 00:03:59 +0100181 LOGPFSML(fi, (fi) ? (fi)->log_level : LOGL_ERROR, fmt, ## args)
Neels Hofmeyre5bde902018-12-20 00:00:11 +0100182
Neels Hofmeyrb11ff7c2018-12-20 00:19:29 +0100183/*! Log using FSM instance's context, with explicit source file and line info.
184 * The log level to log on is obtained from the FSM instance.
185 * The log subsystem to log on is obtained from the underlying FSM definition.
186 * \param fi An osmo_fsm_inst.
187 * \param caller_file A string constant containing a source file path, like __FILE__.
188 * \param caller_line A number constant containing a source file line, like __LINE__.
189 * \param fmt printf-like format string.
190 * \param args Format string arguments.
191 */
Neels Hofmeyreeacf902016-12-23 01:00:13 +0100192#define LOGPFSMSRC(fi, caller_file, caller_line, fmt, args...) \
Neels Hofmeyrb0b39af2018-12-20 00:03:59 +0100193 LOGPFSMLSRC(fi, (fi) ? (fi)->log_level : LOGL_ERROR, \
Neels Hofmeyreeacf902016-12-23 01:00:13 +0100194 caller_file, caller_line, \
195 fmt, ## args)
196
Neels Hofmeyr5734bff2019-02-21 02:27:48 +0100197#define OSMO_T_FMT "%c%u"
198#define OSMO_T_FMT_ARGS(T) ((T) >= 0 ? 'T' : 'X'), ((T) >= 0 ? T : -T)
199
Harald Welte136e7372016-05-29 10:53:17 +0900200int osmo_fsm_register(struct osmo_fsm *fsm);
Max8b25a3f2016-11-01 11:02:17 +0100201void osmo_fsm_unregister(struct osmo_fsm *fsm);
Harald Welte8808bb42017-01-07 11:11:03 +0100202struct osmo_fsm *osmo_fsm_find_by_name(const char *name);
Harald Welte4585e672017-04-16 17:23:56 +0200203struct osmo_fsm_inst *osmo_fsm_inst_find_by_name(const struct osmo_fsm *fsm,
204 const char *name);
205struct osmo_fsm_inst *osmo_fsm_inst_find_by_id(const struct osmo_fsm *fsm,
206 const char *id);
Harald Welte136e7372016-05-29 10:53:17 +0900207struct osmo_fsm_inst *osmo_fsm_inst_alloc(struct osmo_fsm *fsm, void *ctx, void *priv,
208 int log_level, const char *id);
209struct osmo_fsm_inst *osmo_fsm_inst_alloc_child(struct osmo_fsm *fsm,
210 struct osmo_fsm_inst *parent,
211 uint32_t parent_term_event);
Philipp Maier2a06a492018-01-16 18:45:56 +0100212void osmo_fsm_inst_unlink_parent(struct osmo_fsm_inst *fi, void *ctx);
213void osmo_fsm_inst_change_parent(struct osmo_fsm_inst *fi,
214 struct osmo_fsm_inst *new_parent,
215 uint32_t new_parent_term_event);
Harald Welte136e7372016-05-29 10:53:17 +0900216void osmo_fsm_inst_free(struct osmo_fsm_inst *fi);
217
Daniel Willmannb0c43a62018-02-08 18:00:37 +0100218int osmo_fsm_inst_update_id(struct osmo_fsm_inst *fi, const char *id);
Neels Hofmeyra64c45a2018-03-31 16:34:49 +0200219int osmo_fsm_inst_update_id_f(struct osmo_fsm_inst *fi, const char *fmt, ...);
Daniel Willmannb0c43a62018-02-08 18:00:37 +0100220
Harald Welte136e7372016-05-29 10:53:17 +0900221const char *osmo_fsm_event_name(struct osmo_fsm *fsm, uint32_t event);
222const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi);
223const char *osmo_fsm_state_name(struct osmo_fsm *fsm, uint32_t state);
224
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200225/*! return the name of the state the FSM instance is currently in. */
Neels Hofmeyrfca04bb2017-02-28 03:38:38 +0100226static inline const char *osmo_fsm_inst_state_name(struct osmo_fsm_inst *fi)
Neels Hofmeyr56632b62019-01-22 00:22:17 +0100227{ return fi ? osmo_fsm_state_name(fi->fsm, fi->state) : "NULL"; }
Neels Hofmeyrfca04bb2017-02-28 03:38:38 +0100228
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200229/*! perform a state change of the given FSM instance
Neels Hofmeyr725698a2016-12-14 17:24:54 +0100230 *
231 * This is a macro that calls _osmo_fsm_inst_state_chg() with the given
232 * parameters as well as the caller's source file and line number for logging
233 * purposes. See there for documentation.
234 */
235#define osmo_fsm_inst_state_chg(fi, new_state, timeout_secs, T) \
236 _osmo_fsm_inst_state_chg(fi, new_state, timeout_secs, T, \
Neels Hofmeyr983dcb92018-08-20 12:33:22 +0200237 __FILE__, __LINE__)
Neels Hofmeyr725698a2016-12-14 17:24:54 +0100238int _osmo_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t new_state,
239 unsigned long timeout_secs, int T,
240 const char *file, int line);
Harald Welte136e7372016-05-29 10:53:17 +0900241
Neels Hofmeyr407df022018-05-25 18:20:06 +0200242/*! perform a state change while keeping the current timer running.
243 *
244 * This is useful to keep a timeout across several states (without having to round the
245 * remaining time to seconds).
246 *
247 * This is a macro that calls _osmo_fsm_inst_state_chg_keep_timer() with the given
248 * parameters as well as the caller's source file and line number for logging
249 * purposes. See there for documentation.
250 */
251#define osmo_fsm_inst_state_chg_keep_timer(fi, new_state) \
252 _osmo_fsm_inst_state_chg_keep_timer(fi, new_state, \
Neels Hofmeyr983dcb92018-08-20 12:33:22 +0200253 __FILE__, __LINE__)
Neels Hofmeyr407df022018-05-25 18:20:06 +0200254int _osmo_fsm_inst_state_chg_keep_timer(struct osmo_fsm_inst *fi, uint32_t new_state,
255 const char *file, int line);
256
Neels Hofmeyrd4b79c82019-03-06 05:43:23 +0100257/*! perform a state change while keeping the current timer if running, or starting a timer otherwise.
258 *
259 * This is useful to keep a timeout across several states, but to make sure that some timeout is actually running.
260 *
261 * This is a macro that calls _osmo_fsm_inst_state_chg_keep_or_start_timer() with the given
262 * parameters as well as the caller's source file and line number for logging
263 * purposes. See there for documentation.
264 */
265#define osmo_fsm_inst_state_chg_keep_or_start_timer(fi, new_state, timeout_secs, T) \
266 _osmo_fsm_inst_state_chg_keep_or_start_timer(fi, new_state, timeout_secs, T, \
267 __FILE__, __LINE__)
268int _osmo_fsm_inst_state_chg_keep_or_start_timer(struct osmo_fsm_inst *fi, uint32_t new_state,
269 unsigned long timeout_secs, int T,
270 const char *file, int line);
271
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200272/*! dispatch an event to an osmocom finite state machine instance
Neels Hofmeyr725698a2016-12-14 17:24:54 +0100273 *
274 * This is a macro that calls _osmo_fsm_inst_dispatch() with the given
275 * parameters as well as the caller's source file and line number for logging
276 * purposes. See there for documentation.
277 */
278#define osmo_fsm_inst_dispatch(fi, event, data) \
Neels Hofmeyr983dcb92018-08-20 12:33:22 +0200279 _osmo_fsm_inst_dispatch(fi, event, data, __FILE__, __LINE__)
Neels Hofmeyr725698a2016-12-14 17:24:54 +0100280int _osmo_fsm_inst_dispatch(struct osmo_fsm_inst *fi, uint32_t event, void *data,
281 const char *file, int line);
282
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200283/*! Terminate FSM instance with given cause
Neels Hofmeyr725698a2016-12-14 17:24:54 +0100284 *
285 * This is a macro that calls _osmo_fsm_inst_term() with the given parameters
286 * as well as the caller's source file and line number for logging purposes.
287 * See there for documentation.
288 */
289#define osmo_fsm_inst_term(fi, cause, data) \
Neels Hofmeyr983dcb92018-08-20 12:33:22 +0200290 _osmo_fsm_inst_term(fi, cause, data, __FILE__, __LINE__)
Neels Hofmeyr725698a2016-12-14 17:24:54 +0100291void _osmo_fsm_inst_term(struct osmo_fsm_inst *fi,
292 enum osmo_fsm_term_cause cause, void *data,
293 const char *file, int line);
Harald Welte136e7372016-05-29 10:53:17 +0900294
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200295/*! Terminate all child FSM instances of an FSM instance.
Neels Hofmeyrc014f602016-12-23 04:26:39 +0100296 *
297 * This is a macro that calls _osmo_fsm_inst_term_children() with the given
298 * parameters as well as the caller's source file and line number for logging
299 * purposes. See there for documentation.
300 */
301#define osmo_fsm_inst_term_children(fi, cause, data) \
Neels Hofmeyr983dcb92018-08-20 12:33:22 +0200302 _osmo_fsm_inst_term_children(fi, cause, data, __FILE__, __LINE__)
Neels Hofmeyrc014f602016-12-23 04:26:39 +0100303void _osmo_fsm_inst_term_children(struct osmo_fsm_inst *fi,
304 enum osmo_fsm_term_cause cause,
305 void *data,
306 const char *file, int line);
307
Harald Welte136e7372016-05-29 10:53:17 +0900308/*! @} */