#pragma once /* * Generic declarative state machine reconciler. * * Each managed resource is described as a directed graph of states * with labelled transitions. The reconciler finds the shortest path * (BFS) from a resource's current state to its wanted state and * executes one transition per tick. * * Dependencies between resources prevent a resource from advancing * past a threshold state until a prerequisite resource reaches a * minimum state. * * Usage: * struct Reconciler *r = reconciler_create(); * * static const struct Rec_Transition dev_trans[] = { * {0, 1, open_device}, * {1, 0, close_device}, * {1, 2, start_capture}, * {2, 1, stop_capture}, * {-1, -1, NULL} * }; * static const char *dev_states[] = {"CLOSED", "OPEN", "STREAMING"}; * struct Rec_Resource *dev = reconciler_add_resource(r, "device", * dev_trans, 3, dev_states, 0, &my_device); * * reconciler_set_wanted(dev, 2); * while (!reconciler_is_stable(r)) { * reconciler_tick(r); * } */ /* Transition table entry. Sentinel: {-1, -1, NULL}. * action: return 1 on success, 0 on failure. * On failure the resource stays in 'from' state. */ struct Rec_Transition { int from; int to; int (*action)(void *userdata); }; typedef enum { REC_STATUS_STABLE, /* current == wanted */ REC_STATUS_WORKING, /* current != wanted, next transition is eligible */ REC_STATUS_BLOCKED, /* current != wanted, a dependency is unsatisfied */ REC_STATUS_NO_PATH, /* current != wanted, no transition path exists */ } Rec_Status; struct Reconciler; struct Rec_Resource; /* Optional log callback — called after each transition attempt. */ typedef void (*Rec_Log_Fn)( const struct Rec_Resource *res, int from, int to, int success, void *userdata); struct Reconciler *reconciler_create(void); void reconciler_destroy(struct Reconciler *r); /* Set a log callback. Called after every transition attempt. */ void reconciler_set_log(struct Reconciler *r, Rec_Log_Fn fn, void *userdata); /* Add a resource. * transitions: caller-owned, sentinel-terminated {-1,-1,NULL}. * state_names: optional array of state_count strings; NULL for numeric display. * initial_state: sets both current and wanted initially. */ struct Rec_Resource *reconciler_add_resource( struct Reconciler *r, const char *name, const struct Rec_Transition *transitions, int state_count, const char **state_names, int initial_state, void *userdata); /* Add a dependency: resource cannot reach state >= blocked_below * unless dep is currently in state >= dep_min_state. */ void reconciler_add_dep( struct Rec_Resource *resource, int blocked_below, struct Rec_Resource *dep, int dep_min_state); void reconciler_set_wanted(struct Rec_Resource *r, int wanted_state); int reconciler_get_current(const struct Rec_Resource *r); int reconciler_get_wanted(const struct Rec_Resource *r); const char *reconciler_get_name(const struct Rec_Resource *r); const char *reconciler_state_name(const struct Rec_Resource *r, int state); Rec_Status reconciler_get_status(const struct Rec_Resource *r); /* Run one reconciliation pass over all resources. * Returns number of transitions attempted (success or failure). */ int reconciler_tick(struct Reconciler *r); /* Returns 1 if all resources have current == wanted. */ int reconciler_is_stable(const struct Reconciler *r);