Mstdlib-1.24.0
m_state_machine.h
1/* The MIT License (MIT)
2 *
3 * Copyright (c) 2015 Monetra Technologies, LLC.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24#ifndef __M_STATE_MACHINE_H__
25#define __M_STATE_MACHINE_H__
26
27/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
28
29#include <mstdlib/base/m_defs.h>
30#include <mstdlib/base/m_types.h>
31
32/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
33
34__BEGIN_DECLS
35
36
37/*! \addtogroup m_state_machine State Machine
38 * \ingroup mstdlib_base
39 *
40 * Non-linear state machine for running a sequence of states.
41 *
42 * Normally the state machine will run using a linear or linear hybrid method. When adding states to the state machine
43 * the order is preserved. By default the next state is the next state added. A state can change/set the next state
44 * id. Using the next state is simply a convenience for using the state machine in a linear manner. So a state can
45 * set an id to transition to or it can rely on the next state in the ordered state list to be called next. This
46 * behavior can be disabled and the state machine will run as a pure non-linear state machine where a transition id
47 * is required to be set by a state.
48 *
49 * States are given an integral id. The id must be unique and is used so a state can specify which state to transition
50 * to. No id can use the number zero (0); it is reserved for internal use.
51 *
52 * Each state can have an optional cleanup state machine. The cleanup will be called when the error or done status
53 * are returned by run. All status that ran (including the one that generated the error) will have their cleanup
54 * state machine run. The state must have fully completed before it is eligible for its cleanup to run. A machine
55 * that has only returned M_STATE_MACHINE_STATUS_WAIT is not eligible for its cleanup to run. A state that is a
56 * sub state machine is eligible for cleanup if it is entered (pre does not return M_FALSE).
57 *
58 * Cleanup is run in reverse order that the states were run in. For example states A - E were run and
59 * E returns done. Cleanup for states will run E - A. Due to the state machine supporting non-linear sequences
60 * it is possible that a cleanup machines will be called multiple times.
61 *
62 * Cleanup can be used in multiple ways. They can be resource clean up (particularly useful when the
63 * done_cleanup flag is used) that is run when the machine finishes. Or they can be used as error recovery such as
64 * performing an action if an error occurs (default use).
65 *
66 * Cleanup should be specific to the state and should in some way be based on the state they're associated
67 * with. A final cleanup on success could be handled as a final state but should be handled outside of
68 * the state machine entirely. Such as being handled as part of cleaning up the void pointer of state data.
69 *
70 * ## Example
71 *
72 * \code{.c}
73 * typedef enum {
74 * STATE_A = 1,
75 * STATE_B,
76 * STATE_C
77 * } states_t;
78 *
79 * static M_state_machine_status_t state_a(void *data, M_uint64 *next)
80 * {
81 * (void)data;
82 * *next = STATE_C;
83 * return M_STATE_MACHINE_STATUS_NEXT;
84 * }
85 *
86 * static M_state_machine_status_t state_b(void *data, M_uint64 *next)
87 * {
88 * (void)data;
89 * (void)next;
90 * return M_STATE_MACHINE_STATUS_DONE;
91 * }
92 *
93 * static M_state_machine_status_t state_c(void *data, M_uint64 *next)
94 * {
95 * (void)data;
96 * *next = STATE_B;
97 * return M_STATE_MACHINE_STATUS_NEXT;
98 * }
99 *
100 * int main(int argc, char **argv)
101 * {
102 * M_state_machine_t *sm;
103 * M_state_machine_status_t status;
104 *
105 * sm = M_state_machine_create(0, NULL, M_STATE_MACHINE_NONE);
106 *
107 * M_state_machine_insert_state(sm, STATE_A, 0, NULL, state_a, NULL, NULL);
108 * M_state_machine_insert_state(sm, STATE_B, 0, NULL, state_b, NULL, NULL);
109 * M_state_machine_insert_state(sm, STATE_C, 0, NULL, state_c, NULL, NULL);
110 *
111 * do {
112 * status = M_state_machine_run(sm, NULL);
113 * } while (status == M_STATE_MACHINE_STATUS_WAIT);
114 *
115 * if (status != M_STATE_MACHINE_STATUS_DONE) {
116 * M_printf("state machine failure\n");
117 * } else {
118 * M_printf("state machine success\n");
119 * }
120 *
121 * M_state_machine_destroy(sm);
122 * return 0;
123 * }
124 * \endcode
125 *
126 *
127 *
128 * ## Interleaved Example
129 *
130 * \code{.c}
131 * #include <mstdlib/mstdlib.h>
132 *
133 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
134 *
135 * typedef struct {
136 * size_t cnt;
137 * size_t a1_wait_cnt;
138 * size_t a2_wait_cnt;
139 * size_t a3_wait_cnt;
140 * size_t b1_wait_cnt;
141 * size_t b2_wait_cnt;
142 * size_t b3_wait_cnt;
143 * size_t b5_wait_cnt;
144 * size_t c1_wait_cnt;
145 * size_t c2_wait_cnt;
146 * size_t c3_wait_cnt;
147 * } data_obj_t;
148 *
149 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
150 *
151 * void trace(M_state_machine_trace_t trace, M_uint64 mndescr, const char *mdescr, M_uint64 sndescr, const char *sdescr, const char *fdescr, M_uint64 id, M_state_machine_status_t status, M_bool run_sub, M_uint64 next_id, void *thunk)
152 * {
153 * if (trace == M_STATE_MACHINE_TRACE_STATE_START)
154 * M_printf("STATE: %s\n", fdescr);
155 *
156 * if (trace == M_STATE_MACHINE_TRACE_STATE_FINISH && status == M_STATE_MACHINE_STATUS_WAIT)
157 * M_printf("STATE: %s - WAIT\n", fdescr);
158 * }
159 *
160 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
161 * // CSM
162 *
163 * typedef enum {
164 * STATE_CL = 1
165 * } state_cl_b_t;
166 *
167 * static M_state_machine_status_t state_csm(void *data, M_state_machine_cleanup_reason_t reason, M_uint64 *next)
168 * {
169 * (void)data;
170 * (void)reason;
171 * (void)next;
172 *
173 * M_printf("Calling func: %s\n", __func__);
174 * return M_STATE_MACHINE_STATUS_NEXT;
175 * }
176 *
177 * static M_state_machine_cleanup_t *create_csm(void)
178 * {
179 * M_state_machine_cleanup_t *csm;
180 *
181 * csm = M_state_machine_cleanup_create(0, "CSM", M_STATE_MACHINE_LINEAR_END);
182 *
183 * M_state_machine_cleanup_insert_state(csm, STATE_CL, 0, "CL1", state_csm, NULL, NULL);
184 *
185 * return csm;
186 * }
187 *
188 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
189 * // SM A
190 *
191 * typedef enum {
192 * STATE_A1 = 1,
193 * STATE_A2,
194 * STATE_A3
195 * } states_a_t;
196 *
197 * static M_state_machine_status_t state_a1(void *data, M_uint64 *next)
198 * {
199 * data_obj_t *obj = data;
200 *
201 * (void)next;
202 *
203 * M_printf("Calling func: %s\n", __func__);
204 *
205 * if (obj->a1_wait_cnt < 4) {
206 * obj->a1_wait_cnt++;
207 * return M_STATE_MACHINE_STATUS_WAIT;
208 * }
209 * obj->a1_wait_cnt = 0;
210 * return M_STATE_MACHINE_STATUS_NEXT;
211 * }
212 *
213 * static M_state_machine_status_t state_a2(void *data, M_uint64 *next)
214 * {
215 * data_obj_t *obj = data;
216 *
217 * (void)next;
218 *
219 * M_printf("Calling func: %s\n", __func__);
220 *
221 * if (obj->a2_wait_cnt < 3) {
222 * obj->a2_wait_cnt++;
223 * return M_STATE_MACHINE_STATUS_WAIT;
224 * }
225 * obj->a2_wait_cnt = 0;
226 * return M_STATE_MACHINE_STATUS_NEXT;
227 * }
228 *
229 * static M_state_machine_status_t state_a3(void *data, M_uint64 *next)
230 * {
231 * data_obj_t *obj = data;
232 *
233 * (void)next;
234 *
235 * M_printf("Calling func: %s\n", __func__);
236 *
237 * if (obj->a3_wait_cnt < 2) {
238 * obj->a3_wait_cnt++;
239 * return M_STATE_MACHINE_STATUS_WAIT;
240 * }
241 * obj->a3_wait_cnt = 0;
242 * return M_STATE_MACHINE_STATUS_NEXT;
243 * }
244 *
245 * static M_state_machine_t *create_sm_a(void)
246 * {
247 * M_state_machine_t *sm;
248 *
249 * sm = M_state_machine_create(0, "SM A", M_STATE_MACHINE_LINEAR_END);
250 *
251 * M_state_machine_insert_state(sm, STATE_A1, 0, "A1", state_a1, NULL, NULL);
252 * M_state_machine_insert_state(sm, STATE_A2, 0, "A2", state_a2, NULL, NULL);
253 * M_state_machine_insert_state(sm, STATE_A3, 0, "A3", state_a3, NULL, NULL);
254 *
255 * return sm;
256 * }
257 *
258 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
259 * // SM B
260 *
261 * typedef enum {
262 * STATE_B1 = 1,
263 * STATE_B2,
264 * STATE_B3,
265 * STATE_B4,
266 * STATE_B5,
267 * STATE_B6
268 * } states_b_t;
269 *
270 * static M_state_machine_status_t state_b1(void *data, M_uint64 *next)
271 * {
272 * data_obj_t *obj = data;
273 *
274 * (void)next;
275 *
276 * M_printf("Calling func: %s\n", __func__);
277 *
278 * if (obj->b1_wait_cnt < 2) {
279 * obj->b1_wait_cnt++;
280 * return M_STATE_MACHINE_STATUS_WAIT;
281 * }
282 * obj->b1_wait_cnt = 0;
283 * return M_STATE_MACHINE_STATUS_NEXT;
284 * }
285 *
286 * static M_state_machine_status_t state_b2(void *data, M_uint64 *next)
287 * {
288 * data_obj_t *obj = data;
289 *
290 * (void)next;
291 *
292 * M_printf("Calling func: %s\n", __func__);
293 *
294 * if (obj->b2_wait_cnt < 3) {
295 * obj->b2_wait_cnt++;
296 * return M_STATE_MACHINE_STATUS_WAIT;
297 * }
298 * obj->b2_wait_cnt = 0;
299 * return M_STATE_MACHINE_STATUS_NEXT;
300 * }
301 *
302 * static M_state_machine_status_t state_b3(void *data, M_uint64 *next)
303 * {
304 * data_obj_t *obj = data;
305 *
306 * (void)next;
307 *
308 * M_printf("Calling func: %s\n", __func__);
309 *
310 * if (obj->b3_wait_cnt < 4) {
311 * obj->b3_wait_cnt++;
312 * return M_STATE_MACHINE_STATUS_WAIT;
313 * }
314 * obj->b3_wait_cnt = 0;
315 * return M_STATE_MACHINE_STATUS_NEXT;
316 * }
317 *
318 * static M_state_machine_status_t state_b4(void *data, M_uint64 *next)
319 * {
320 * (void)data;
321 * (void)next;
322 *
323 * M_printf("Calling func: %s\n", __func__);
324 *
325 * return M_STATE_MACHINE_STATUS_NEXT;
326 * }
327 *
328 * static M_state_machine_status_t state_b5(void *data, M_uint64 *next)
329 * {
330 * data_obj_t *obj = data;
331 *
332 * (void)next;
333 *
334 * M_printf("Calling func: %s\n", __func__);
335 *
336 * if (obj->b5_wait_cnt < 5) {
337 * obj->b5_wait_cnt++;
338 * return M_STATE_MACHINE_STATUS_WAIT;
339 * }
340 * obj->b5_wait_cnt = 0;
341 * return M_STATE_MACHINE_STATUS_NEXT;
342 * }
343 *
344 * static M_state_machine_status_t state_b6(void *data, M_uint64 *next)
345 * {
346 * (void)data;
347 * (void)next;
348 *
349 * M_printf("Calling func: %s\n", __func__);
350 *
351 * return M_STATE_MACHINE_STATUS_NEXT;
352 * }
353 *
354 * static M_state_machine_t *create_sm_b(void)
355 * {
356 * M_state_machine_t *sm;
357 * M_state_machine_cleanup_t *csm;
358 *
359 * sm = M_state_machine_create(0, "SM B", M_STATE_MACHINE_LINEAR_END|M_STATE_MACHINE_DONE_CLEANUP);
360 *
361 * csm = create_csm();
362 * M_state_machine_insert_state(sm, STATE_B1, 0, "B1", state_b1, csm, NULL);
363 * M_state_machine_insert_state(sm, STATE_B1, 0, "B1", state_b1, NULL, NULL);
364 * M_state_machine_cleanup_destroy(csm);
365 * M_state_machine_insert_state(sm, STATE_B2, 0, "B2", state_b2, NULL, NULL);
366 * M_state_machine_insert_state(sm, STATE_B3, 0, "B3", state_b3, NULL, NULL);
367 * M_state_machine_insert_state(sm, STATE_B4, 0, "B4", state_b4, NULL, NULL);
368 * M_state_machine_insert_state(sm, STATE_B5, 0, "B5", state_b5, NULL, NULL);
369 * M_state_machine_insert_state(sm, STATE_B6, 0, "B6", state_b6, NULL, NULL);
370 *
371 * return sm;
372 * }
373 *
374 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
375 * // SM C
376 *
377 * typedef enum {
378 * STATE_C1 = 1,
379 * STATE_C2,
380 * STATE_C3
381 * } states_c_t;
382 *
383 * static M_state_machine_status_t state_c1(void *data, M_uint64 *next)
384 * {
385 * data_obj_t *obj = data;
386 *
387 * (void)next;
388 *
389 * M_printf("Calling func: %s\n", __func__);
390 *
391 * if (obj->c1_wait_cnt < 4) {
392 * obj->c1_wait_cnt++;
393 * return M_STATE_MACHINE_STATUS_WAIT;
394 * }
395 * obj->c1_wait_cnt = 0;
396 * return M_STATE_MACHINE_STATUS_NEXT;
397 * }
398 *
399 * static M_state_machine_status_t state_c2(void *data, M_uint64 *next)
400 * {
401 * data_obj_t *obj = data;
402 *
403 * (void)next;
404 *
405 * M_printf("Calling func: %s\n", __func__);
406 *
407 * if (obj->c2_wait_cnt < 0) {
408 * obj->c2_wait_cnt++;
409 * return M_STATE_MACHINE_STATUS_WAIT;
410 * }
411 * obj->c2_wait_cnt = 0;
412 * return M_STATE_MACHINE_STATUS_NEXT;
413 * }
414 *
415 * static M_state_machine_status_t state_c3(void *data, M_uint64 *next)
416 * {
417 * data_obj_t *obj = data;
418 *
419 * (void)next;
420 *
421 * M_printf("Calling func: %s\n", __func__);
422 *
423 * if (obj->c3_wait_cnt < 1) {
424 * obj->c3_wait_cnt++;
425 * return M_STATE_MACHINE_STATUS_WAIT;
426 * }
427 * obj->c3_wait_cnt = 0;
428 * return M_STATE_MACHINE_STATUS_NEXT;
429 * }
430 *
431 * static M_state_machine_t *create_sm_c(void)
432 * {
433 * M_state_machine_t *sm;
434 * M_state_machine_cleanup_t *csm;
435 *
436 * sm = M_state_machine_create(0, "SM C", M_STATE_MACHINE_LINEAR_END|M_STATE_MACHINE_DONE_CLEANUP);
437 *
438 * csm = create_csm();
439 * M_state_machine_insert_state(sm, STATE_C1, 0, "C1", state_c1, csm, NULL);
440 * M_state_machine_cleanup_destroy(csm);
441 * M_state_machine_insert_state(sm, STATE_C2, 0, "C2", state_c2, NULL, NULL);
442 * M_state_machine_insert_state(sm, STATE_C3, 0, "C3", state_c3, NULL, NULL);
443 *
444 * return sm;
445 * }
446 *
447 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
448 * // SM MAIN
449 *
450 * typedef enum {
451 * STATE_MA = 1,
452 * STATE_MB, // Interleaved state.
453 * STATE_MC
454 * } states_m_t;
455 *
456 * static M_state_machine_status_t state_ma(void *data, M_uint64 *next)
457 * {
458 * M_printf("Calling func: %s\n", __func__);
459 * return M_STATE_MACHINE_STATUS_NEXT;
460 * }
461 *
462 * static M_bool inter_pre(void *data, M_state_machine_status_t *status, M_uint64 *next)
463 * {
464 * (void)data;
465 * (void)status;
466 * (void)next;
467 * M_printf("Calling func: %s\n", __func__);
468 * return M_TRUE;
469 * }
470 *
471 * static M_state_machine_status_t inter_post(void *data, M_state_machine_status_t sub_status, M_uint64 *next)
472 * {
473 * (void)data;
474 * (void)next;
475 *
476 * M_printf("Calling func: %s with sub status %u\n", __func__, sub_status);
477 *
478 * if (sub_status != M_STATE_MACHINE_STATUS_DONE)
479 * return sub_status;
480 *
481 * return M_STATE_MACHINE_STATUS_NEXT;
482 * }
483 *
484 * static M_state_machine_status_t state_mc(void *data, M_uint64 *next)
485 * {
486 * data_obj_t *obj = data;
487 *
488 * M_printf("Calling func: %s\n", __func__);
489 *
490 * if (obj->cnt < 3) {
491 * *next = STATE_MA;
492 *
493 * obj->cnt++;
494 * M_printf("Going back to main cnt: %zu\n", obj->cnt);
495 * }
496 * return M_STATE_MACHINE_STATUS_NEXT;
497 * }
498 *
499 * static M_state_machine_t *create_sm_m(void)
500 * {
501 * M_state_machine_t *sm;
502 * M_state_machine_t *subm;
503 *
504 * sm = M_state_machine_create(0, "SM M", M_STATE_MACHINE_LINEAR_END|M_STATE_MACHINE_INTERNOABORT);
505 *
506 * M_state_machine_insert_state(sm, STATE_MA, 0, "SA", state_ma, NULL, NULL);
507 *
508 * M_state_machine_insert_state_interleaved(sm, STATE_MB, 0, "SIB", inter_pre, inter_post, NULL, NULL);
509 * subm = create_sm_a();
510 * M_state_machine_insert_sub_state_machine_interleaved(sm, STATE_MB, subm);
511 * M_state_machine_destroy(subm);
512 * subm = create_sm_b();
513 * M_state_machine_insert_sub_state_machine_interleaved(sm, STATE_MB, subm);
514 * M_state_machine_destroy(subm);
515 * subm = create_sm_c();
516 * M_state_machine_insert_sub_state_machine_interleaved(sm, STATE_MB, subm);
517 * M_state_machine_destroy(subm);
518 *
519 * M_state_machine_insert_state(sm, STATE_MC, 0, "SC", state_mc, NULL, NULL);
520 *
521 * return sm;
522 * }
523 *
524 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
525 *
526 * int main(int argc, char **argv)
527 * {
528 * M_state_machine_t *sm;
529 * data_obj_t *obj;
530 * M_state_machine_status_t status;
531 *
532 * sm = create_sm_m();
533 * obj = M_malloc_zero(sizeof(*obj));
534 * M_state_machine_enable_trace(sm, trace, NULL);
535 *
536 * do {
537 * status = M_state_machine_run(sm, obj);
538 * } while (status == M_STATE_MACHINE_STATUS_WAIT);
539 *
540 * if (status != M_STATE_MACHINE_STATUS_DONE) {
541 * M_printf("state machine failure\n");
542 * } else {
543 * M_printf("state machine success\n");
544 * }
545 *
546 * M_state_machine_destroy(sm);
547 * M_free(obj);
548 * return 0;
549 * }
550 * \endcode
551 *
552 * @{
553 */
554
555struct M_state_machine;
556typedef struct M_state_machine M_state_machine_t;
557
558struct M_state_machine_cleanup;
559typedef struct M_state_machine_cleanup M_state_machine_cleanup_t;
560
561
562/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
563
564/*! Status of the state machine while running though states. */
565typedef enum {
566 /* State specific status. Only states can return these. */
567 M_STATE_MACHINE_STATUS_NONE = 0, /*!< Invalid status. */
568 M_STATE_MACHINE_STATUS_NEXT, /*!< Success continue to the next state.
569 The state was run and should be recorded as well as cleanup added
570 to the cleanup list. */
571 M_STATE_MACHINE_STATUS_PREV, /*!< A recoverable error occurred. Go to the last successful (non-continue)
572 state.
573
574 This should be treated as a special case and is primarily a
575 convenience when using the state machine in a linear manner. It should
576 not be used in stead of specifying an id and calling next if it is
577 possible to do so.
578
579 This does not back out states. State cleanups will not be called when
580 skipping back over states. Also, the list of cleanups will not be
581 modified to remove cleanups for states that have been called. Further,
582 This can result in a state having it's cleanup registered multiple
583 times as a result of multiple successful calls. */
584 M_STATE_MACHINE_STATUS_CONTINUE, /*!< Success continue to the next state.
585 The state was skipped and should be treated as such. The cleanup
586 for this state will not be added to the cleanup list.
587
588 This should not be treated as next without cleanup. It is for
589 signifying that the state was skipped. If you need next without
590 cleanup the state should be registered without a cleanup state
591 machine. Even if that means having two ids for the same state function
592 one with and one without a cleanup registered. */
593
594 /* Shared status. States and the state machine can return these. */
595 M_STATE_MACHINE_STATUS_ERROR_STATE, /*!< An unrecoverable error occurred within a state. Exit and clean up.
596 The state is responsible for error reporting though the void data
597 pointer passed to the state function. */
598 M_STATE_MACHINE_STATUS_WAIT, /*!< The state is processing in a non-blocking fashion. More calls to run
599 are required to continue the operation. The state that returned
600 WAIT will be called when the state machine is run. */
601 M_STATE_MACHINE_STATUS_PAUSE, /*!< The state is processing in a non-blocking fashion. More calls to run
602 are required to continue the operation. The next state (set
603 explicit or implicitly) will be run when the state machine is run.
604 An error will be returned by run instead of pause if continuing
605 would cause an error. In the same way as with the state returning
606 next. If there is no state to continue to run will return done if
607 doing so is not an error. Such as if linear end flag is set. */
608 M_STATE_MACHINE_STATUS_DONE, /*!< The sequence completed successfully. */
609
610 /* State machine specific status. Only the state machine can return these. */
611 M_STATE_MACHINE_STATUS_STOP_CLEANUP, /*< Used by cleanup state machines to stop processing further cleanup
612 state machines within a state machine. */
613 /* All of these are unrecoverable errors. */
614 M_STATE_MACHINE_STATUS_ERROR_INVALID, /*!< The state machine was called with an invalid parameter. */
615 M_STATE_MACHINE_STATUS_ERROR_BAD_ID, /*!< Invalid transition specified. Id not found. Most likely the state
616 specified an id to transition to that doesn't exist. */
617 M_STATE_MACHINE_STATUS_ERROR_NO_NEXT, /*!< Invalid transition specified. An next id was not specified. This can
618 happen when running in a linear manner and the last state in the
619 sequence does not return done. There are no states after the last
620 state so we cannot continue with the sequence. */
621 M_STATE_MACHINE_STATUS_ERROR_BAD_NEXT, /*!< Invalid transition specified. The specified next id is not valid (not
622 listed in the states list of next ids) for the state. */
623 M_STATE_MACHINE_STATUS_ERROR_SELF_NEXT, /*!< Invalid transition specified. The specified next id is the current id.
624 Use the continue_loop flag to disable this check. */
625 M_STATE_MACHINE_STATUS_ERROR_NO_PREV, /*!< Invalid transition specified. There are no previous states to
626 transition to. */
627 M_STATE_MACHINE_STATUS_ERROR_INF_CONT /*!< A possible infinite continuation loop has been encountered. */
629
630
631/*! Options to control the behavior of the state machine. */
632typedef enum {
633 M_STATE_MACHINE_NONE = 0, /*!< Normal operation. */
634 M_STATE_MACHINE_SINGLE_PREV = 1 << 1, /*!< Do not allow multiple states to return STATUS_PREV in a row.
635 Only one PREV return is allowed between NEXT calls. */
636 M_STATE_MACHINE_CONTINUE_LOOP = 1 << 2, /*!< Normally continuations are tracked for the continuation cycle
637 and any continuation that is repeated is treated as an internal
638 error in order to detect and prevent accidental infinite loops.
639
640 This option disables this check and allows continuations to call
641 continuations that have been called previously. */
642 M_STATE_MACHINE_SELF_CALL = 1 << 3, /*!< Normally states cannot call themselves. This flag also
643 allows states to call themselves. */
644 M_STATE_MACHINE_DONE_CLEANUP = 1 << 4, /*!< State cleanups should be called on done. */
645 M_STATE_MACHINE_ONE_CLEANUP = 1 << 5, /*!< State cleanup should be called once no matter how many times the
646 state was called. */
647 M_STATE_MACHINE_EXPLICIT_NEXT = 1 << 6, /*!< Normally the state machine defaults to using the next state in
648 the order states were added if a state isn't explicits specified
649 by the current state. This requires that a state specify the next
650 (transition) state.
651
652 This will force the state machine to function purely as a non-linear
653 state machine. The linear / linear hybrid functionality will be
654 disabled. This option cannot be used in conjunction with linear_end.
655 The linear_end flag will be ignored if this flag is set. */
656 M_STATE_MACHINE_LINEAR_END = 1 << 7, /*!< Normally a state machine is done when the done status is returned
657 by a state. This allows the state machine to be considered done if
658 a state does not specify a transition, it returns next or continue
659 and the current state is the last state in the ordered state list. */
660 M_STATE_MACHINE_INTERNOABORT = 1 << 8 /*!< Interleaved sub state machines should continue processing until done
661 even when another sub state machine errors. Prevents aborting other
662 interleaved sub state machines from aborting. An error will still
663 be returned as the result of the interleaved state (sent to the post
664 callback) but only after all sub state machines have finished. If
665 multiple sub state machines failed, the error status for the first
666 added will be used. */
668
669
670/*! Status of the state machine which caused the cleanup routines to trigger. */
671typedef enum {
672 M_STATE_MACHINE_CLEANUP_REASON_NONE = 0, /*!< Cleanup should not be run. When calling reset this will not run
673 cleanup. */
674 M_STATE_MACHINE_CLEANUP_REASON_DONE, /*!< State machine finished successfully. */
675 M_STATE_MACHINE_CLEANUP_REASON_ERROR, /*!< State machine stopped due to error. */
676 M_STATE_MACHINE_CLEANUP_REASON_RESET, /*!< State machine should be reset so it can run again. This is a reason
677 why cleanup is being run. */
678 M_STATE_MACHINE_CLEANUP_REASON_CANCEL /*!< State machine was canceled. This will reset the machine so it can
679 run again but should be considered that it will not be run again.
680 Use reset for restarting instead. */
682
683
684/*! Tracing information. */
685typedef enum {
686 M_STATE_MACHINE_TRACE_NONE = 0, /*!< Invalid. */
687 M_STATE_MACHINE_TRACE_MACHINEENTER, /*!< About to enter a given state machine (could be sub)
688 Will provide the following information:
689 * mndescr
690 * mdescr
691 * fdescr */
692 M_STATE_MACHINE_TRACE_MACHINEEXIT, /*!< Machine exited.
693 Will provide the following information:
694 * mndescr
695 * mdescr
696 * fdescr
697 * status */
698 M_STATE_MACHINE_TRACE_STATE_START, /*!< State is about to run.
699 Will provide the following information:
700 * mndescr
701 * mdescr
702 * sndescr
703 * sdescr
704 * fdescr
705 * id
706 */
707 M_STATE_MACHINE_TRACE_STATE_FINISH, /*!< State finished running.
708 Will provide the following information:
709 * mndescr
710 * mdescr
711 * sndescr
712 * sdescr
713 * fdescr
714 * id
715 * next_id
716 * status */
717 M_STATE_MACHINE_TRACE_PRE_START, /*!< Pre function will run before entering a sub machine.
718 Will provide the following information:
719 * mndescr
720 * mdescr
721 * sndescr
722 * sdescr
723 * fdescr
724 * id
725 */
726 M_STATE_MACHINE_TRACE_PRE_FINISH, /*!< Pre functoin finished running.
727 Will provide the following information:
728 * mndescr
729 * mdescr
730 * sndescr
731 * sdescr
732 * fdescr
733 * id
734 * run_sub
735 * status */
736 M_STATE_MACHINE_TRACE_POST_START, /*!< Sub machine finished but before post function runs.
737 Will provide the following information:
738 * mndescr
739 * mdescr
740 * sndescr
741 * sdescr
742 * fdescr
743 * id */
744 M_STATE_MACHINE_TRACE_POST_FINISH, /*!< Sub machine finished running but after post function ran.
745 Will provide the following information:
746 * mndescr
747 * mdescr
748 * sndescr
749 * sdescr
750 * fdescr
751 * id
752 * status */
753 M_STATE_MACHINE_TRACE_CLEANUP /*!< Cleanup function ran.
754 Will provide the following information:
755 * mndescr
756 * mdescr
757 * sndescr
758 * sdescr */
760
761
762/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
763
764/*! Trace callback.
765 *
766 * \param[in] trace Type of action traced.
767 * \param[in] mndescr Numeric state machine description code.
768 * \param[in] mdescr State machine description.
769 * \param[in] sndescr Numeric state description code.
770 * \param[in] sdescr State description.
771 * \param[in] fdescr Full description of the entire machine flow.
772 * \param[in] id Id of state.
773 * \param[in] status Return status.
774 * \param[in] run_sub Will the sub state machine be run.
775 * \param[in] next_id The next id the machine will move to.
776 * \param[in] thunk Thunk passed in when enabling the trace.
777 */
778typedef void (*M_state_machine_trace_cb)(M_state_machine_trace_t trace, M_uint64 mndescr, const char *mdescr, M_uint64 sndescr, const char *sdescr, const char *fdescr, M_uint64 id, M_state_machine_status_t status, M_bool run_sub, M_uint64 next_id, void *thunk);
779
780
781/*! State callback.
782 *
783 * This is what the state machine calls when entering a given state.
784 *
785 * \param[in,out] data An opaque data type storing data that should be passed to the cb.
786 * \param[out] next The next id the state machine should transition to. When operating in linear or a hybrid manner
787 * this will be set to the next linear state. Changing this will change what state is next.
788 *
789 * \return The status.
790 */
791typedef M_state_machine_status_t (*M_state_machine_state_cb)(void *data, M_uint64 *next);
792
793
794/*! Cleanup state callback.
795 *
796 * This is what a cleanup state machine calls when entering a given cleanup state.
797 *
798 * \param[in,out] data An opaque data type storing data that should be passed to the cb.
799 * \param[in] reason The reason cleanup is being run.
800 * \param[out] next The next id the state machine should transition to. When operating in linear or a hybrid manner
801 * this will be set to the next linear state. Changing this will change what state is next.
802 *
803 * \return The status.
804 */
806
807
808/*! Sub state machine pre (initialization) callback.
809 *
810 * This will be called before starting a sub state machine.
811 *
812 * \param[in,out] data An opaque data type storing data that should be passed to the cb.
813 * \param[out] status Used when not running the sub state machine. This is the status of the state. Defaults
814 * to M_STATE_MACHINE_STATUS_NEXT if not specified.
815 * \param[out] next Used when not running the sub state machine. If set the next id the state machine should
816 * transition to. When operating in linear or a hybrid manner this will be set to the next
817 * linear state. Changing this will change what state is next.
818 *
819 * \return M_TRUE if the sub state machine should run. M_FALSE if the sub state machine should not run.
820 */
821typedef M_bool (*M_state_machine_pre_cb)(void *data, M_state_machine_status_t *status, M_uint64 *next);
822
823
824/*! Sub state machine post (de-initialization) callback.
825 *
826 * The sub_status argument is the status returned by the sub state machine. Possible status:
827 *
828 * - M_STATE_MACHINE_STATUS_DONE
829 * - M_STATE_MACHINE_STATUS_ERROR_*
830 *
831 * The sub_status will next be M_STATE_MACHINE_STATUS_NEXT or similar. Thus,
832 * the sub_status should not be blindly returned from the post function as it
833 * will stop processing the parent state machine. If processing needs to
834 * continue the sub_status should be checked and M_STATE_MACHINE_STATUS_NEXT or
835 * similar should be returned. M_STATE_MACHINE_STATUS_DONE is the only
836 * successful sub_status that can be set, so patterns that check against
837 * M_STATE_MACHINE_STATUS_DONE should be used. For example:
838 *
839 * \code{c}
840 * if (sub_status == M_STATE_MACHINE_STATUS_DONE) {
841 * // Success and continue.
842 * return M_STATE_MACHINE_STATUS_NEXT;
843 * }
844 * ...
845 * \endcode
846 *
847 * \code{c}
848 * if (sub_status != M_STATE_MACHINE_STATUS_DONE) {
849 * // Error of some kind. Propagate it up.
850 * return sub_status;
851 * }
852 * ...
853 * \endcode
854 *
855 * \code{c}
856 * if (stop_condition) {
857 * // Some kind of external stop condition was encountered.
858 * // Return the sub_status because it will stop processing
859 * // and we should maintain status from the sub state machine.
860 * return sub_status; // Status is error or done.
861 * }
862 * ...
863 * \endcode
864 *
865 * \param[in,out] data An opaque data type storing data that should be passed to the cb.
866 * \param[out] sub_status The status of the last state in the sub state machine.
867 * \param[out] next The next id the state machine should transition to. When operating in linear or a hybrid manner
868 * this will be set to the next linear state. Changing this will change what state is next.
869 *
870 * \return The status.
871 */
872typedef M_state_machine_status_t (*M_state_machine_post_cb)(void *data, M_state_machine_status_t sub_status, M_uint64 *next);
873
874
875/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
876
877/*! Create a new state machine.
878 *
879 * \param[in] ndescr A numeric description of the state machine. Can be 0.
880 * \param[in] descr A textual description of the state machine. Can be NULL.
881 * \param[in] flags M_state_machine_flags_t flags to control the behavior of the state machine.
882 *
883 * \return The state machine.
884 */
885M_API M_state_machine_t *M_state_machine_create(M_uint64 ndescr, const char *descr, M_uint32 flags);
886
887
888/*! Destroy a state machine.
889 *
890 * This does not call the cleanup state machines associated with each state. State cleanups are only called when the
891 * state machine finishes running.
892 *
893 * \param[in] m The state machine.
894 */
896
897
898/*! Create a new cleanup state machine.
899 *
900 * A cleanup state machine is very similar to a regular state machine and is only called when associated with
901 * a regular state machine state's cleanup parameter. This cannot be run directly but supports all options a
902 * regular state machine supports for execution.
903 *
904 * When run error returns from a cleanup state machine will not be propagated back to the caller.
905 * To handle errors it is possible to have a cleanup state machine's state to have an associated cleanup
906 * state machine.
907 *
908 * \param[in] ndescr A numeric description of the cleanup state machine. Can be 0.
909 * \param[in] descr A textual description of the cleanup state machine. Can be NULL.
910 * \param[in] flags M_state_machine_flags_t flags to control the behavior of the cleanup state machine.
911 *
912 * \return The cleanup state machine.
913 */
914M_API M_state_machine_cleanup_t *M_state_machine_cleanup_create(M_uint64 ndescr, const char *descr, M_uint32 flags);
915
916
917/*! Destroy a cleanup state machine.
918 *
919 * \param[in] m The cleanup state machine.
920 */
922
923
924/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
925
926/*! Add a state to the state machine.
927 *
928 * \param[in,out] m The state machine.
929 * \param[in] id The id associated with this state. Must be unique.
930 * \param[in] ndescr A numeric description of the state. Can be 0.
931 * \param[in] descr A textual description of the state. Can be NULL.
932 * \param[in] func The state function to call. Cannot be NULL.
933 * \param[in] cleanup The cleanup state machine to call. Can be NULL if no cleanup is necessary for this state.
934 * \param[in] next_ids A list of valid transitions for this state. Can be NULL to denote all states are
935 * valid transitions. If not NULL the state machine takes ownership of next_ids.
936 *
937 * \return M_TRUE if the state was added. Otherwise M_FALSE.
938 */
939M_API M_bool M_state_machine_insert_state(M_state_machine_t *m, M_uint64 id, M_uint64 ndescr, const char *descr, M_state_machine_state_cb func, M_state_machine_cleanup_t *cleanup, M_list_u64_t *next_ids);
940
941
942/*! Add a state machine as a state to the state machine.
943 *
944 * The state machine will duplicate the sub state machine and keep a copy.
945 *
946 * The sub state machine will run though all states in the sub state machine. The state machine will return
947 * M_STATE_MACHINE_STATUS_WAIT from the sub state machine and resume the sub state machine when started again.
948 *
949 * The sub state machine's final status will be passed to the post function if one is given. If a post function is
950 * not set, a status of M_STATE_MACHINE_STATUS_DONE will be returned as M_STATE_MACHINE_STATUS_NEXT. This
951 * is to prevent a M_STATE_MACHINE_STATUS_DONE from the sub state machine from accidentally stopping the calling
952 * state machine. If M_STATE_MACHINE_STATUS_DONE is needed as the result of the sub state machine's run then a
953 * post function is necessary.
954 *
955 * \param[in,out] m The state machine.
956 * \param[in] id The id associated with this state. Must be unique.
957 * \param[in] ndescr A numeric description of the state. Can be 0.
958 * \param[in] descr A textual description of the state. Can be NULL.
959 * \param[in] subm The state machine that should be called from this one. Cannot be NULL.
960 * \param[in] pre A function to call before the sub state machine is started. Can be NULL.
961 * \param[in] post A function to call after the sub state machine is finished. Can be NULL.
962 * \param[in] cleanup The cleanup state machine to call. Can be NULL if no cleanup is necessary for this state.
963 * \param[in] next_ids A list of valid transitions for this state. Can be NULL to denote all states are
964 * valid transitions. If not NULL the state machine takes ownership of next_ids.
965 *
966 * \return M_TRUE if the sub state machine was added. Otherwise M_FALSE.
967 */
968M_API M_bool M_state_machine_insert_sub_state_machine(M_state_machine_t *m, M_uint64 id, M_uint64 ndescr, const char *descr, const M_state_machine_t *subm, M_state_machine_pre_cb pre, M_state_machine_post_cb post, M_state_machine_cleanup_t *cleanup, M_list_u64_t *next_ids);
969
970
971/*! Add add a state to run interleaved sub state machines.
972 *
973 * An interleaved state will have one or more sub state machines added to it.
974 * The sub state machines will be run interleaved as one returns wait the next
975 * will be started until it returns wait and so forth. Sub state machines process
976 * in a non liner order and do not match state counts between one another.
977 *
978 * A single state machine that never returns wait will fully run before any others
979 * are. Interleaving relies on wait states. Typically, this is used for near concurrent
980 * processing for operations that may require waiting on external resources. For example,
981 * a system that receives, processes and sends data. A example flow: read -> process ->
982 * Interleave write and read -> process... This allows the read buffer to be filled while
983 * the write buffer is emptying. With a large amount of data read and write operations
984 * may need to wait for OS level network buffers.
985 *
986 * States are run interleaved and _not_ concurrently. They are run on the same
987 * thread. Locking of thunk resources is not necessary since only one state,
988 * regardless of sub state machine, will be running at any given time.
989 *
990 * All running sub state machines will stop if an error is returned by any sub state machine.
991 * Unless the `M_STATE_MACHINE_INTERNOABORT` flag is set which will alter this behavior.
992 *
993 * \param[in,out] m The state machine.
994 * \param[in] id The id associated with this interleaved state. Must be unique.
995 * All interleaved sub state machines will attach to this id.
996 * \param[in] ndescr A numeric description of the state. Can be 0.
997 * \param[in] descr A textual description of the state. Can be NULL.
998 * \param[in] pre A function to call before the sub state machines are started. Can be NULL.
999 * \param[in] post A function to call after the sub state machines are finished. Can be NULL.
1000 * Called when the last sub state machine is finished or they are aborted due to error.
1001 * Will have an `M_STATE_MACHINE_STATUS_ERROR_*` if any of the sub state machines return
1002 * an error status. If all sub state machines ran successfully will return
1003 * `M_STATE_MACHINE_STATUS_DONE`. It is up to the caller to store information in the
1004 * `thunk` argument passed to run if information about which (possibly multiple) sub
1005 * state machines failed.
1006 * \param[in] cleanup The cleanup state machine to call. Can be NULL if no cleanup is necessary for this state.
1007 * \param[in] next_ids A list of valid transitions for this state. Can be NULL to denote all states are
1008 * valid transitions. If not NULL the state machine takes ownership of next_ids.
1009 *
1010 * \return M_TRUE if the interleaved state was added. Otherwise M_FALSE.
1011 */
1012M_API M_bool M_state_machine_insert_state_interleaved(M_state_machine_t *m, M_uint64 id, M_uint64 ndescr, const char *descr, M_state_machine_pre_cb pre, M_state_machine_post_cb post, M_state_machine_cleanup_t *cleanup, M_list_u64_t *next_ids);
1013
1014
1015/*! Add a state machine as a state to the state machine for interleaved processing.
1016 *
1017 * The state machine will duplicate the sub state machine and keep a copy.
1018 *
1019 * \param[in,out] m The state machine.
1020 * \param[in] id The id for the interleaved state as specified by `M_state_machine_insert_state_interleaved`.
1021 * \param[in] subm The state machine that should be called from this one. Cannot be NULL.
1022 *
1023 * \return M_TRUE if the sub state machine was added. Otherwise M_FALSE.
1024 */
1026
1027
1028/*! Remove a state from the state machine.
1029 *
1030 * \param[in,out] m The state machine.
1031 * \param[in] id The id of the state.
1032 *
1033 * \return M_TRUE if the state was found and removed. Otherwise M_FALSE.
1034 */
1036
1037
1038/*! Does the state machine contain the given state id.
1039 *
1040 * \param[in] m The state machine.
1041 * \param[in] id The id of the state.
1042 *
1043 * \return M_TRUE if the state machine has the state id. Otherwise M_FALSE.
1044 */
1045M_API M_bool M_state_machine_has_state(const M_state_machine_t *m, M_uint64 id);
1046
1047
1048/*! List all state ids the state machine holds.
1049 *
1050 * \param[in] m The state machine.
1051 *
1052 * \return a List of state ids or NULL if the state machine as no states.
1053 */
1055
1056
1057/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1058
1059/*! Add a cleanup state to a cleanup state machine.
1060 *
1061 * \param[in,out] m The cleanup state machine.
1062 * \param[in] id The id associated with this state. Must be unique.
1063 * \param[in] ndescr A numeric description of the state. Can be 0.
1064 * \param[in] descr A textual description of the state. Can be NULL.
1065 * \param[in] func The state cleanup function to call. Cannot be NULL.
1066 * \param[in] cleanup The cleanup state machine to call. Can be NULL if no cleanup is necessary for this state.
1067 * \param[in] next_ids A list of valid transitions for this state. Can be NULL to denote all states are
1068 * valid transitions. If not NULL the state machine takes ownership of next_ids.
1069 *
1070 * \return M_TRUE if the state was added. Otherwise M_FALSE.
1071 */
1072M_API M_bool M_state_machine_cleanup_insert_state(M_state_machine_cleanup_t *m, M_uint64 id, M_uint64 ndescr, const char *descr, M_state_machine_cleanup_cb func, M_state_machine_cleanup_t *cleanup, M_list_u64_t *next_ids);
1073
1074
1075/*! Add a cleanup state machine as a state to the cleanup state machine.
1076 *
1077 * The state machine will duplicate the sub state machine and keep a copy.
1078 *
1079 * The sub state machine will run though all states in the sub state machine. The state machine will return
1080 * M_STATE_MACHINE_STATUS_WAIT from the sub state machine and resume the sub state machine when started again.
1081 *
1082 * The sub state machine's final status will be passed to the post function if one is given. If a post function is
1083 * not set, a status of M_STATE_MACHINE_STATUS_DONE will be returned as M_STATE_MACHINE_STATUS_NEXT. This
1084 * is to prevent a M_STATE_MACHINE_STATUS_DONE from the sub state machine from accidentally stopping the calling
1085 * state machine. If M_STATE_MACHINE_STATUS_DONE is needed as the result of the sub state machine's run then a
1086 * post function is necessary.
1087 *
1088 * \param[in,out] m The cleanup state machine.
1089 * \param[in] id The id associated with this state. Must be unique.
1090 * \param[in] ndescr A numeric description of the state. Can be 0.
1091 * \param[in] descr A textual description of the state. Can be NULL.
1092 * \param[in] subm The cleanup state machine that should be called from this one. Cannot be NULL.
1093 * \param[in] pre A function to call before the sub state machine is started. Can be NULL.
1094 * \param[in] post A function to call after the sub state machine is finished. Can be NULL.
1095 * \param[in] cleanup The cleanup state machine to call. Can be NULL if no cleanup is necessary for this state.
1096 * \param[in] next_ids A list of valid transitions for this state. Can be NULL to denote all states are
1097 * valid transitions. If not NULL the state machine takes ownership of next_ids.
1098 *
1099 * \return M_TRUE if the sub state machine was added. Otherwise M_FALSE.
1100 */
1102
1103
1104/*! Add a state machine as a state to the cleanup state machine.
1105 *
1106 * The state machine will duplicate the sub state machine and keep a copy.
1107 *
1108 * The sub state machine will run though all states in the sub state machine. The state machine will return
1109 * M_STATE_MACHINE_STATUS_WAIT from the sub state machine and resume the sub state machine when started again.
1110 *
1111 * The sub state machine's final status will be passed to the post function if one is given. If a post function is
1112 * not set, a status of M_STATE_MACHINE_STATUS_DONE will be returned as M_STATE_MACHINE_STATUS_NEXT. This
1113 * is to prevent a M_STATE_MACHINE_STATUS_DONE from the sub state machine from accidentally stopping the calling
1114 * state machine. If M_STATE_MACHINE_STATUS_DONE is needed as the result of the sub state machine's run then a
1115 * post function is necessary.
1116 *
1117 * \param[in,out] m The cleanup state machine.
1118 * \param[in] id The id associated with this state. Must be unique.
1119 * \param[in] ndescr A numeric description of the state. Can be NULL.
1120 * \param[in] descr A textual description of the state. Can be NULL.
1121 * \param[in] subm The state machine that should be called from this one. Cannot be NULL.
1122 * \param[in] pre A function to call before the sub state machine is started. Can be NULL.
1123 * \param[in] post A function to call after the sub state machine is finished. Can be NULL.
1124 * \param[in] cleanup The cleanup state machine to call. Can be NULL if no cleanup is necessary for this state.
1125 * \param[in] next_ids A list of valid transitions for this state. Can be NULL to denote all states are
1126 * valid transitions. If not NULL the state machine takes ownership of next_ids.
1127 *
1128 * \return M_TRUE if the sub state machine was added. Otherwise M_FALSE.
1129 */
1130M_API M_bool M_state_machine_cleanup_insert_sub_state_machine(M_state_machine_cleanup_t *m, M_uint64 id, M_uint64 ndescr, const char *descr, const M_state_machine_t *subm, M_state_machine_pre_cb pre, M_state_machine_post_cb post, M_state_machine_cleanup_t *cleanup, M_list_u64_t *next_ids);
1131
1132
1133/*! Remove a state from the cleanup state machine.
1134 *
1135 * \param[in,out] m The state machine.
1136 * \param[in] id The id of the state.
1137 *
1138 * \return M_TRUE if the state was found and removed. Otherwise M_FALSE.
1139 */
1141
1142
1143/*! Does the cleanup state machine contain the given state id.
1144 *
1145 * \param[in] m The cleanup state machine.
1146 * \param[in] id The id of the state.
1147 *
1148 * \return M_TRUE if the state machine has the state id. Otherwise M_FALSE.
1149 */
1151
1152
1153/*! List all state ids the cleanup state machine holds.
1154 *
1155 * \param[in] m The cleanup state machine.
1156 *
1157 * \return a List of state ids or NULL if the state machine as no states.
1158 */
1160
1161
1162/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1163
1164/*! Enabling tracing of state machine flow.
1165 *
1166 * \param[in] m The state machine.
1167 * \param[in] cb Trace callback.
1168 * \param[in] thunk Thunk to be passed to callback.
1169 */
1171
1172
1173/*! Enabling tracing of cleanup state machine flow.
1174 *
1175 * \param[in] m The cleanup state machine.
1176 * \param[in] cb Trace callback.
1177 * \param[in] thunk Thunk to be passed to callback.
1178 */
1180
1181
1182/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1183
1184/*! Run the state machine.
1185 *
1186 * This may need to be called multiple times. A state can run non-blocking (poll based) where
1187 * the state can return a wait state. The wait state means it finished processing but has
1188 * more to do.
1189 *
1190 * On error the cleanup state machine for the state will be called. When returning from a sub state machine
1191 * which had clean up run the post function which can override and ignore an error can stop the cleanup
1192 * process. Thus cleanup can be stopped and the state machine can recover from the error that started
1193 * the process.
1194 *
1195 * \param[in,out] m The state machine to run.
1196 * \param[in,out] data State specific data that can be used and or manipulated by each state.
1197 *
1198 * \return Result. The done and wait returns are the only successful results (wait requiring additional calls).
1199 * All other results are error conditions.
1200 */
1202
1203
1204/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1205
1206/*! Rest a running state machine.
1207 *
1208 * A condition outside of the state machine could determine it needs to restart while it was
1209 * in a running state. Not specifically running but in the middle of a run; having returned from
1210 * a wait state for example. This will reset the state machine's internal process state so that
1211 * it can be started from the beginning again.
1212 *
1213 * This will not run cleanup immediately if requested but instead sets the state machine to start
1214 * cleanup on next run. The sub state machine post function will not allow overriding the cleanup
1215 * result and prevents the state machine from stopping cleanup. M_state_machine_run *MUST* be called.
1216 * Also, remember that cleanup state machines can call wait so it may be necessary to run multiple times.
1217 *
1218 * \param[in,out] m The state machine.
1219 * \param[in] reason Whether state cleanups should run. Cleanup callbacks told cleanup is due to the
1220 * reason code. Use M_STATE_MACHINE_CLEANUP_REASON_NONE to prevent cleanup.
1221 */
1223
1224
1225/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1226
1227/*! Get the numeric description of the state machine
1228 *
1229 * \param[in] m The state machine.
1230 * \param[in] recurse Recurs into each running sub state machine and return the description for the one running.
1231 *
1232 * \return The ndescr specified creating the state machine.
1233 */
1234M_API M_uint64 M_state_machine_ndescr(const M_state_machine_t *m, M_bool recurse);
1235
1236
1237/*! Get the description of the state machine
1238 *
1239 * \param[in] m The state machine.
1240 * \param[in] recurse Recurse into each running sub state machine and return the description for the one running.
1241 *
1242 * \return The descr text specified creating the state machine.
1243 */
1244M_API const char *M_state_machine_descr(const M_state_machine_t *m, M_bool recurse);
1245
1246
1247/*! Get the active sub state machine that is currently running.
1248 *
1249 * \param[in] m The state machine.
1250 * \param[in] recurse Recurse into each running sub state machine and return the last one that is running.
1251 *
1252 * \return Sub state machine if one is currently running. Otherwise, NULL.
1253 */
1255
1256
1257/*! Get state of the state machine.
1258 *
1259 * This only returns information about the given state machine. It does not look into sub state machines if one
1260 * is running.
1261 *
1262 * \param[in] m The state machine.
1263 * \param[out] id The id of the state currently being run. Optional pass NULL if only checking whether the
1264 * state machine is running.
1265 *
1266 * \return M_TRUE if the state machine has been started and the id is a valid state id. Otherwise M_FALSE.
1267 */
1268M_API M_bool M_state_machine_active_state(const M_state_machine_t *m, M_uint64 *id);
1269
1270
1271/*! Get the numeric description for the currently running state.
1272 *
1273 * \param[in] m The state machine.
1274 * \param[in] recurse Recurse into each running sub state machine and return the description for the one running.
1275 *
1276 * \return The ndescr specified when adding the state to the state machine.
1277 */
1278M_API M_uint64 M_state_machine_active_state_ndescr(const M_state_machine_t *m, M_bool recurse);
1279
1280
1281/*! Get the description text for the currently running state.
1282 *
1283 * \param[in] m The state machine.
1284 * \param[in] recurse Recurse into each running sub state machine and return the description for the one running.
1285 *
1286 * \return The descr text specified when adding the state to the state machine.
1287 */
1288M_API const char *M_state_machine_active_state_descr(const M_state_machine_t *m, M_bool recurse);
1289
1290
1291/*! Get a textual representation of state machine and it's current state.
1292 *
1293 * \param[in] m The state machine.
1294 * \param[in] show_id M_TRUE if the numeric representation of state ids should be included.
1295 *
1296 * \return A compound description of every machine and state.
1297 */
1298M_API char *M_state_machine_descr_full(const M_state_machine_t *m, M_bool show_id);
1299
1300
1301/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1302
1303/*! Duplicate an existing state machine.
1304 *
1305 * \param[in] m State machine to duplicate.
1306 *
1307 * \return New state machine.
1308 */
1310
1311/*! @} */
1312
1313__END_DECLS
1314
1315/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1316
1317#endif /* __M_STATE_MACHINE_H__ */
1318
struct M_list_u64 M_list_u64_t
Definition: m_list_u64.h:78
M_bool M_state_machine_has_state(const M_state_machine_t *m, M_uint64 id)
void M_state_machine_reset(M_state_machine_t *m, M_state_machine_cleanup_reason_t reason)
void M_state_machine_enable_trace(M_state_machine_t *m, M_state_machine_trace_cb cb, void *thunk)
void M_state_machine_cleanup_destroy(M_state_machine_cleanup_t *m)
M_bool M_state_machine_cleanup_remove_state(M_state_machine_cleanup_t *m, M_uint64 id)
const M_list_u64_t * M_state_machine_list_states(const M_state_machine_t *m)
M_bool M_state_machine_remove_state(M_state_machine_t *m, M_uint64 id)
M_state_machine_status_t(* M_state_machine_state_cb)(void *data, M_uint64 *next)
Definition: m_state_machine.h:791
M_state_machine_cleanup_t * M_state_machine_cleanup_create(M_uint64 ndescr, const char *descr, M_uint32 flags)
M_state_machine_flags_t
Definition: m_state_machine.h:632
const char * M_state_machine_descr(const M_state_machine_t *m, M_bool recurse)
M_state_machine_status_t M_state_machine_run(M_state_machine_t *m, void *data)
M_bool(* M_state_machine_pre_cb)(void *data, M_state_machine_status_t *status, M_uint64 *next)
Definition: m_state_machine.h:821
const M_state_machine_t * M_state_machine_active_sub(const M_state_machine_t *m, M_bool recurse)
M_bool M_state_machine_cleanup_insert_sub_state_machine(M_state_machine_cleanup_t *m, M_uint64 id, M_uint64 ndescr, const char *descr, const M_state_machine_t *subm, M_state_machine_pre_cb pre, M_state_machine_post_cb post, M_state_machine_cleanup_t *cleanup, M_list_u64_t *next_ids)
M_bool M_state_machine_insert_state(M_state_machine_t *m, M_uint64 id, M_uint64 ndescr, const char *descr, M_state_machine_state_cb func, M_state_machine_cleanup_t *cleanup, M_list_u64_t *next_ids)
M_state_machine_t * M_state_machine_create(M_uint64 ndescr, const char *descr, M_uint32 flags)
void M_state_machine_cleanup_enable_trace(M_state_machine_cleanup_t *m, M_state_machine_trace_cb cb, void *thunk)
M_bool M_state_machine_cleanup_insert_state(M_state_machine_cleanup_t *m, M_uint64 id, M_uint64 ndescr, const char *descr, M_state_machine_cleanup_cb func, M_state_machine_cleanup_t *cleanup, M_list_u64_t *next_ids)
M_bool M_state_machine_cleanup_has_state(const M_state_machine_cleanup_t *m, M_uint64 id)
M_bool M_state_machine_insert_sub_state_machine(M_state_machine_t *m, M_uint64 id, M_uint64 ndescr, const char *descr, const M_state_machine_t *subm, M_state_machine_pre_cb pre, M_state_machine_post_cb post, M_state_machine_cleanup_t *cleanup, M_list_u64_t *next_ids)
M_state_machine_status_t(* M_state_machine_post_cb)(void *data, M_state_machine_status_t sub_status, M_uint64 *next)
Definition: m_state_machine.h:872
M_state_machine_trace_t
Definition: m_state_machine.h:685
M_state_machine_status_t(* M_state_machine_cleanup_cb)(void *data, M_state_machine_cleanup_reason_t reason, M_uint64 *next)
Definition: m_state_machine.h:805
M_state_machine_cleanup_reason_t
Definition: m_state_machine.h:671
const M_list_u64_t * M_state_machine_cleanup_list_states(const M_state_machine_cleanup_t *m)
M_state_machine_status_t
Definition: m_state_machine.h:565
M_state_machine_t * M_state_machine_duplicate(const M_state_machine_t *m) M_MALLOC
char * M_state_machine_descr_full(const M_state_machine_t *m, M_bool show_id)
M_bool M_state_machine_insert_sub_state_machine_interleaved(M_state_machine_t *m, M_uint64 id, const M_state_machine_t *subm)
M_uint64 M_state_machine_active_state_ndescr(const M_state_machine_t *m, M_bool recurse)
M_uint64 M_state_machine_ndescr(const M_state_machine_t *m, M_bool recurse)
struct M_state_machine M_state_machine_t
Definition: m_state_machine.h:556
M_bool M_state_machine_active_state(const M_state_machine_t *m, M_uint64 *id)
struct M_state_machine_cleanup M_state_machine_cleanup_t
Definition: m_state_machine.h:559
const char * M_state_machine_active_state_descr(const M_state_machine_t *m, M_bool recurse)
M_bool M_state_machine_cleanup_insert_cleanup_sub_state_machine(M_state_machine_cleanup_t *m, M_uint64 id, M_uint64 ndescr, const char *descr, const M_state_machine_cleanup_t *subm, M_state_machine_pre_cb pre, M_state_machine_post_cb post, M_state_machine_cleanup_t *cleanup, M_list_u64_t *next_ids)
void(* M_state_machine_trace_cb)(M_state_machine_trace_t trace, M_uint64 mndescr, const char *mdescr, M_uint64 sndescr, const char *sdescr, const char *fdescr, M_uint64 id, M_state_machine_status_t status, M_bool run_sub, M_uint64 next_id, void *thunk)
Definition: m_state_machine.h:778
M_bool M_state_machine_insert_state_interleaved(M_state_machine_t *m, M_uint64 id, M_uint64 ndescr, const char *descr, M_state_machine_pre_cb pre, M_state_machine_post_cb post, M_state_machine_cleanup_t *cleanup, M_list_u64_t *next_ids)
void M_state_machine_destroy(M_state_machine_t *m)
@ M_STATE_MACHINE_DONE_CLEANUP
Definition: m_state_machine.h:644
@ M_STATE_MACHINE_NONE
Definition: m_state_machine.h:633
@ M_STATE_MACHINE_SINGLE_PREV
Definition: m_state_machine.h:634
@ M_STATE_MACHINE_LINEAR_END
Definition: m_state_machine.h:656
@ M_STATE_MACHINE_SELF_CALL
Definition: m_state_machine.h:642
@ M_STATE_MACHINE_EXPLICIT_NEXT
Definition: m_state_machine.h:647
@ M_STATE_MACHINE_INTERNOABORT
Definition: m_state_machine.h:660
@ M_STATE_MACHINE_CONTINUE_LOOP
Definition: m_state_machine.h:636
@ M_STATE_MACHINE_ONE_CLEANUP
Definition: m_state_machine.h:645
@ M_STATE_MACHINE_TRACE_CLEANUP
Definition: m_state_machine.h:753
@ M_STATE_MACHINE_TRACE_STATE_FINISH
Definition: m_state_machine.h:707
@ M_STATE_MACHINE_TRACE_MACHINEEXIT
Definition: m_state_machine.h:692
@ M_STATE_MACHINE_TRACE_NONE
Definition: m_state_machine.h:686
@ M_STATE_MACHINE_TRACE_POST_START
Definition: m_state_machine.h:736
@ M_STATE_MACHINE_TRACE_MACHINEENTER
Definition: m_state_machine.h:687
@ M_STATE_MACHINE_TRACE_STATE_START
Definition: m_state_machine.h:698
@ M_STATE_MACHINE_TRACE_POST_FINISH
Definition: m_state_machine.h:744
@ M_STATE_MACHINE_TRACE_PRE_FINISH
Definition: m_state_machine.h:726
@ M_STATE_MACHINE_TRACE_PRE_START
Definition: m_state_machine.h:717
@ M_STATE_MACHINE_CLEANUP_REASON_CANCEL
Definition: m_state_machine.h:678
@ M_STATE_MACHINE_CLEANUP_REASON_RESET
Definition: m_state_machine.h:676
@ M_STATE_MACHINE_CLEANUP_REASON_ERROR
Definition: m_state_machine.h:675
@ M_STATE_MACHINE_CLEANUP_REASON_DONE
Definition: m_state_machine.h:674
@ M_STATE_MACHINE_CLEANUP_REASON_NONE
Definition: m_state_machine.h:672
@ M_STATE_MACHINE_STATUS_STOP_CLEANUP
Definition: m_state_machine.h:611
@ M_STATE_MACHINE_STATUS_PREV
Definition: m_state_machine.h:571
@ M_STATE_MACHINE_STATUS_ERROR_INVALID
Definition: m_state_machine.h:614
@ M_STATE_MACHINE_STATUS_WAIT
Definition: m_state_machine.h:598
@ M_STATE_MACHINE_STATUS_ERROR_INF_CONT
Definition: m_state_machine.h:627
@ M_STATE_MACHINE_STATUS_ERROR_BAD_NEXT
Definition: m_state_machine.h:621
@ M_STATE_MACHINE_STATUS_NONE
Definition: m_state_machine.h:567
@ M_STATE_MACHINE_STATUS_ERROR_NO_PREV
Definition: m_state_machine.h:625
@ M_STATE_MACHINE_STATUS_PAUSE
Definition: m_state_machine.h:601
@ M_STATE_MACHINE_STATUS_CONTINUE
Definition: m_state_machine.h:584
@ M_STATE_MACHINE_STATUS_NEXT
Definition: m_state_machine.h:568
@ M_STATE_MACHINE_STATUS_ERROR_NO_NEXT
Definition: m_state_machine.h:617
@ M_STATE_MACHINE_STATUS_ERROR_SELF_NEXT
Definition: m_state_machine.h:623
@ M_STATE_MACHINE_STATUS_ERROR_BAD_ID
Definition: m_state_machine.h:615
@ M_STATE_MACHINE_STATUS_DONE
Definition: m_state_machine.h:608
@ M_STATE_MACHINE_STATUS_ERROR_STATE
Definition: m_state_machine.h:595