Neels Hofmeyr | 17518fe | 2017-06-20 04:35:06 +0200 | [diff] [blame] | 1 | /*! \file fsm.h |
| 2 | * Finite State Machine |
| 3 | */ |
| 4 | |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 5 | #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 Hofmeyr | 17518fe | 2017-06-20 04:35:06 +0200 | [diff] [blame] | 16 | * \file fsm.h */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 17 | |
| 18 | struct osmo_fsm_inst; |
| 19 | |
| 20 | enum osmo_fsm_term_cause { |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 21 | /*! terminate because parent terminated */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 22 | OSMO_FSM_TERM_PARENT, |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 23 | /*! terminate on explicit user request */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 24 | OSMO_FSM_TERM_REQUEST, |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 25 | /*! regular termination of process */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 26 | OSMO_FSM_TERM_REGULAR, |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 27 | /*! erroneous termination of process */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 28 | OSMO_FSM_TERM_ERROR, |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 29 | /*! termination due to time-out */ |
Harald Welte | f627c0f | 2016-06-18 10:36:25 +0200 | [diff] [blame] | 30 | OSMO_FSM_TERM_TIMEOUT, |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 31 | }; |
| 32 | |
Neels Hofmeyr | 5c5c78a | 2016-12-14 18:35:47 +0100 | [diff] [blame] | 33 | extern const struct value_string osmo_fsm_term_cause_names[]; |
| 34 | static 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 Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 40 | /*! description of a rule in the FSM */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 41 | struct osmo_fsm_state { |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 42 | /*! bit-mask of permitted input events for this state */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 43 | uint32_t in_event_mask; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 44 | /*! bit-mask to which other states this state may transiton */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 45 | uint32_t out_state_mask; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 46 | /*! human-readable name of this state */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 47 | const char *name; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 48 | /*! function to be called for events arriving in this state */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 49 | void (*action)(struct osmo_fsm_inst *fi, uint32_t event, void *data); |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 50 | /*! function to be called just after entering the state */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 51 | void (*onenter)(struct osmo_fsm_inst *fi, uint32_t prev_state); |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 52 | /*! function to be called just before leaving the state */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 53 | void (*onleave)(struct osmo_fsm_inst *fi, uint32_t next_state); |
| 54 | }; |
| 55 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 56 | /*! a description of an osmocom finite state machine */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 57 | struct osmo_fsm { |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 58 | /*! global list */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 59 | struct llist_head list; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 60 | /*! list of instances of this FSM */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 61 | struct llist_head instances; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 62 | /*! human readable name */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 63 | const char *name; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 64 | /*! table of state transition rules */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 65 | const struct osmo_fsm_state *states; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 66 | /*! number of entries in \ref states */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 67 | unsigned int num_states; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 68 | /*! bit-mask of events permitted in all states */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 69 | uint32_t allstate_event_mask; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 70 | /*! function pointer to be called for allstate events */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 71 | void (*allstate_action)(struct osmo_fsm_inst *fi, uint32_t event, void *data); |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 72 | /*! clean-up function, called during termination */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 73 | void (*cleanup)(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause); |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 74 | /*! timer call-back for states with time-out. |
Neels Hofmeyr | dda5e79 | 2016-12-09 16:10:34 +0100 | [diff] [blame] | 75 | * \returns 1 to request termination, 0 to keep running. */ |
Harald Welte | f627c0f | 2016-06-18 10:36:25 +0200 | [diff] [blame] | 76 | int (*timer_cb)(struct osmo_fsm_inst *fi); |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 77 | /*! logging sub-system for this FSM */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 78 | int log_subsys; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 79 | /*! human-readable names of events */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 80 | const struct value_string *event_names; |
Philipp Maier | d1f5793 | 2018-02-14 18:20:07 +0100 | [diff] [blame] | 81 | /*! 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 Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 83 | }; |
| 84 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 85 | /*! a single instanceof an osmocom finite state machine */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 86 | struct osmo_fsm_inst { |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 87 | /*! member in the fsm->instances list */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 88 | struct llist_head list; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 89 | /*! back-pointer to the FSM of which we are an instance */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 90 | struct osmo_fsm *fsm; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 91 | /*! human readable identifier */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 92 | const char *id; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 93 | /*! human readable fully-qualified name */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 94 | const char *name; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 95 | /*! some private data of this instance */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 96 | void *priv; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 97 | /*! logging level for this FSM */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 98 | int log_level; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 99 | /*! current state of the FSM */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 100 | uint32_t state; |
| 101 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 102 | /*! timer number for states with time-out */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 103 | int T; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 104 | /*! timer back-end for states with time-out */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 105 | struct osmo_timer_list timer; |
| 106 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 107 | /*! support for fsm-based procedures */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 108 | struct { |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 109 | /*! the parent FSM that has created us */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 110 | struct osmo_fsm_inst *parent; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 111 | /*! the event we should send upon termination */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 112 | uint32_t parent_term_event; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 113 | /*! a list of children processes */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 114 | struct llist_head children; |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 115 | /*! \ref llist_head linked to parent->proc.children */ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 116 | struct llist_head child; |
| 117 | } proc; |
| 118 | }; |
| 119 | |
| 120 | void osmo_fsm_log_addr(bool log_addr); |
| 121 | |
Neels Hofmeyr | 6a13e7f | 2016-12-14 17:37:34 +0100 | [diff] [blame] | 122 | #define LOGPFSML(fi, level, fmt, args...) \ |
| 123 | LOGP((fi)->fsm->log_subsys, level, "%s{%s}: " fmt, \ |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 124 | osmo_fsm_inst_name(fi), \ |
| 125 | osmo_fsm_state_name((fi)->fsm, (fi)->state), ## args) |
| 126 | |
Neels Hofmeyr | 6a13e7f | 2016-12-14 17:37:34 +0100 | [diff] [blame] | 127 | #define LOGPFSM(fi, fmt, args...) \ |
| 128 | LOGPFSML(fi, (fi)->log_level, fmt, ## args) |
| 129 | |
Neels Hofmeyr | eeacf90 | 2016-12-23 01:00:13 +0100 | [diff] [blame] | 130 | #define LOGPFSMLSRC(fi, level, caller_file, caller_line, fmt, args...) \ |
| 131 | LOGPSRC((fi)->fsm->log_subsys, level, \ |
| 132 | caller_file, caller_line, \ |
| 133 | "%s{%s}: " fmt, \ |
| 134 | osmo_fsm_inst_name(fi), \ |
| 135 | osmo_fsm_state_name((fi)->fsm, (fi)->state), \ |
| 136 | ## args) |
| 137 | |
| 138 | #define LOGPFSMSRC(fi, caller_file, caller_line, fmt, args...) \ |
| 139 | LOGPFSMLSRC(fi, (fi)->log_level, \ |
| 140 | caller_file, caller_line, \ |
| 141 | fmt, ## args) |
| 142 | |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 143 | int osmo_fsm_register(struct osmo_fsm *fsm); |
Max | 8b25a3f | 2016-11-01 11:02:17 +0100 | [diff] [blame] | 144 | void osmo_fsm_unregister(struct osmo_fsm *fsm); |
Harald Welte | 8808bb4 | 2017-01-07 11:11:03 +0100 | [diff] [blame] | 145 | struct osmo_fsm *osmo_fsm_find_by_name(const char *name); |
Harald Welte | 4585e67 | 2017-04-16 17:23:56 +0200 | [diff] [blame] | 146 | struct osmo_fsm_inst *osmo_fsm_inst_find_by_name(const struct osmo_fsm *fsm, |
| 147 | const char *name); |
| 148 | struct osmo_fsm_inst *osmo_fsm_inst_find_by_id(const struct osmo_fsm *fsm, |
| 149 | const char *id); |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 150 | struct osmo_fsm_inst *osmo_fsm_inst_alloc(struct osmo_fsm *fsm, void *ctx, void *priv, |
| 151 | int log_level, const char *id); |
| 152 | struct osmo_fsm_inst *osmo_fsm_inst_alloc_child(struct osmo_fsm *fsm, |
| 153 | struct osmo_fsm_inst *parent, |
| 154 | uint32_t parent_term_event); |
Philipp Maier | 2a06a49 | 2018-01-16 18:45:56 +0100 | [diff] [blame] | 155 | void osmo_fsm_inst_unlink_parent(struct osmo_fsm_inst *fi, void *ctx); |
| 156 | void osmo_fsm_inst_change_parent(struct osmo_fsm_inst *fi, |
| 157 | struct osmo_fsm_inst *new_parent, |
| 158 | uint32_t new_parent_term_event); |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 159 | void osmo_fsm_inst_free(struct osmo_fsm_inst *fi); |
| 160 | |
Daniel Willmann | b0c43a6 | 2018-02-08 18:00:37 +0100 | [diff] [blame] | 161 | int osmo_fsm_inst_update_id(struct osmo_fsm_inst *fi, const char *id); |
| 162 | |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 163 | const char *osmo_fsm_event_name(struct osmo_fsm *fsm, uint32_t event); |
| 164 | const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi); |
| 165 | const char *osmo_fsm_state_name(struct osmo_fsm *fsm, uint32_t state); |
| 166 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 167 | /*! return the name of the state the FSM instance is currently in. */ |
Neels Hofmeyr | fca04bb | 2017-02-28 03:38:38 +0100 | [diff] [blame] | 168 | static inline const char *osmo_fsm_inst_state_name(struct osmo_fsm_inst *fi) |
| 169 | { return osmo_fsm_state_name(fi->fsm, fi->state); } |
| 170 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 171 | /*! perform a state change of the given FSM instance |
Neels Hofmeyr | 725698a | 2016-12-14 17:24:54 +0100 | [diff] [blame] | 172 | * |
| 173 | * This is a macro that calls _osmo_fsm_inst_state_chg() with the given |
| 174 | * parameters as well as the caller's source file and line number for logging |
| 175 | * purposes. See there for documentation. |
| 176 | */ |
| 177 | #define osmo_fsm_inst_state_chg(fi, new_state, timeout_secs, T) \ |
| 178 | _osmo_fsm_inst_state_chg(fi, new_state, timeout_secs, T, \ |
| 179 | __BASE_FILE__, __LINE__) |
| 180 | int _osmo_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t new_state, |
| 181 | unsigned long timeout_secs, int T, |
| 182 | const char *file, int line); |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 183 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 184 | /*! dispatch an event to an osmocom finite state machine instance |
Neels Hofmeyr | 725698a | 2016-12-14 17:24:54 +0100 | [diff] [blame] | 185 | * |
| 186 | * This is a macro that calls _osmo_fsm_inst_dispatch() with the given |
| 187 | * parameters as well as the caller's source file and line number for logging |
| 188 | * purposes. See there for documentation. |
| 189 | */ |
| 190 | #define osmo_fsm_inst_dispatch(fi, event, data) \ |
| 191 | _osmo_fsm_inst_dispatch(fi, event, data, __BASE_FILE__, __LINE__) |
| 192 | int _osmo_fsm_inst_dispatch(struct osmo_fsm_inst *fi, uint32_t event, void *data, |
| 193 | const char *file, int line); |
| 194 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 195 | /*! Terminate FSM instance with given cause |
Neels Hofmeyr | 725698a | 2016-12-14 17:24:54 +0100 | [diff] [blame] | 196 | * |
| 197 | * This is a macro that calls _osmo_fsm_inst_term() with the given parameters |
| 198 | * as well as the caller's source file and line number for logging purposes. |
| 199 | * See there for documentation. |
| 200 | */ |
| 201 | #define osmo_fsm_inst_term(fi, cause, data) \ |
| 202 | _osmo_fsm_inst_term(fi, cause, data, __BASE_FILE__, __LINE__) |
| 203 | void _osmo_fsm_inst_term(struct osmo_fsm_inst *fi, |
| 204 | enum osmo_fsm_term_cause cause, void *data, |
| 205 | const char *file, int line); |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 206 | |
Neels Hofmeyr | 87e4550 | 2017-06-20 00:17:59 +0200 | [diff] [blame] | 207 | /*! Terminate all child FSM instances of an FSM instance. |
Neels Hofmeyr | c014f60 | 2016-12-23 04:26:39 +0100 | [diff] [blame] | 208 | * |
| 209 | * This is a macro that calls _osmo_fsm_inst_term_children() with the given |
| 210 | * parameters as well as the caller's source file and line number for logging |
| 211 | * purposes. See there for documentation. |
| 212 | */ |
| 213 | #define osmo_fsm_inst_term_children(fi, cause, data) \ |
| 214 | _osmo_fsm_inst_term_children(fi, cause, data, __BASE_FILE__, __LINE__) |
| 215 | void _osmo_fsm_inst_term_children(struct osmo_fsm_inst *fi, |
| 216 | enum osmo_fsm_term_cause cause, |
| 217 | void *data, |
| 218 | const char *file, int line); |
| 219 | |
Harald Welte | 136e737 | 2016-05-29 10:53:17 +0900 | [diff] [blame] | 220 | /*! @} */ |