/*! \brief Terminate FSM instance with given cause * * This safely terminates the given FSM instance by first iterating * over all children and sending them a termination event. Next, it * calls the FSM descriptors cleanup function (if any), followed by * releasing any memory associated with the FSM instance. * * Finally, the parent FSM instance (if any) is notified using the * parent termination event configured at time of FSM instance start. * * \param[in] fi FSM instance to be terminated * \param[in] cause Cause / reason for termination * \param[in] data Opaque event data to be passed with the parent term event * \param[in] file Calling source file (from osmo_fsm_inst_term macro) * \param[in] line Calling source line (from osmo_fsm_inst_term macro) */ void _osmo_fsm_inst_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause, void *data, const char *file, int line) { struct osmo_fsm_inst *parent; uint32_t parent_term_event = fi->proc.parent_term_event; LOGPFSMSRC(fi, file, line, "Terminating (cause = %s)\n", osmo_fsm_term_cause_name(cause)); _osmo_fsm_inst_term_children(fi, OSMO_FSM_TERM_PARENT, NULL, file, line); /* delete ourselves from the parent */ parent = fi->proc.parent; if (parent) LOGPFSMSRC(fi, file, line, "Removing from parent %s\n", osmo_fsm_inst_name(parent)); llist_del(&fi->proc.child); /* call destructor / clean-up function */ if (fi->fsm->cleanup) fi->fsm->cleanup(fi, cause); LOGPFSMSRC(fi, file, line, "Freeing instance\n"); /* Fetch parent again in case it has changed. */ parent = fi->proc.parent; osmo_fsm_inst_free(fi); /* indicate our termination to the parent */ if (parent && cause != OSMO_FSM_TERM_PARENT) _osmo_fsm_inst_dispatch(parent, parent_term_event, data, file, line); }
static int foo(void) { struct osmo_fsm_inst *fi; LOGP(DMAIN, LOGL_INFO, "Checking FSM allocation\n"); fi = osmo_fsm_inst_alloc(&fsm, g_ctx, NULL, LOGL_DEBUG, NULL); OSMO_ASSERT(fi); OSMO_ASSERT(fi->fsm == &fsm); OSMO_ASSERT(!strncmp(osmo_fsm_inst_name(fi), fsm.name, strlen(fsm.name))); OSMO_ASSERT(fi->state == ST_NULL); OSMO_ASSERT(fi->log_level == LOGL_DEBUG); /* Try invalid state transition */ osmo_fsm_inst_dispatch(fi, EV_B, (void *) 42); OSMO_ASSERT(fi->state == ST_NULL); /* Legitimate state transition */ osmo_fsm_inst_dispatch(fi, EV_A, (void *) 23); OSMO_ASSERT(fi->state == ST_ONE); /* Legitimate transition with timer */ fsm.timer_cb = test_fsm_tmr_cb; osmo_fsm_inst_dispatch(fi, EV_B, (void *) 42); OSMO_ASSERT(fi->state == ST_TWO); return 0; }
/*! \brief allocate a new instance of a specified FSM as child of * other FSM instance * * This is like \ref osmo_fsm_inst_alloc but using the parent FSM as * talloc context, and inheriting the log level of the parent. * * \param[in] fsm Descriptor of the to-be-allocated FSM * \param[in] parent Parent FSM instance * \param[in] parent_term_event Event to be sent to parent when terminating * \returns newly-allocated, initialized and registered FSM instance */ struct osmo_fsm_inst *osmo_fsm_inst_alloc_child(struct osmo_fsm *fsm, struct osmo_fsm_inst *parent, uint32_t parent_term_event) { struct osmo_fsm_inst *fi; fi = osmo_fsm_inst_alloc(fsm, parent, NULL, parent->log_level, parent->id); if (!fi) { /* indicate immediate termination to caller */ osmo_fsm_inst_dispatch(parent, parent_term_event, NULL); return NULL; } LOGPFSM(fi, "is child of %s\n", osmo_fsm_inst_name(parent)); fi->proc.parent = parent; fi->proc.parent_term_event = parent_term_event; llist_add(&fi->proc.child, &parent->proc.children); return fi; }