Last active
June 30, 2016 22:52
-
-
Save courtc/f4c2c35b59eff514733c to your computer and use it in GitHub Desktop.
HSM, simplified
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include "hsm.h" | |
#define container_of(x, y, z) (y *)((char *)(x) - ((unsigned long)&((y *)0)->z)) | |
enum { | |
STATE_FILL, | |
STATE_WRITE, | |
STATE_COMPLAIN, | |
}; | |
struct sm { | |
struct hsm hsm; | |
char *str; | |
}; | |
struct sm_data { | |
struct hsm_data data; | |
const char *str; | |
}; | |
static int sm_enter(struct hsm *hsm) | |
{ | |
struct sm *p = container_of(hsm, struct sm, hsm); | |
p->str = malloc(256); | |
return -(p->str == NULL); | |
} | |
static int sm_leave(struct hsm *hsm) | |
{ | |
struct sm *p = container_of(hsm, struct sm, hsm); | |
free(p->str); | |
return 0; | |
} | |
static int sm_write_prepare(struct hsm *hsm) | |
{ | |
struct sm *p = container_of(hsm, struct sm, hsm); | |
p->str[0] = 0; | |
return 0; | |
} | |
static int sm_write(struct hsm *hsm, struct hsm_data *data) | |
{ | |
struct sm *p = container_of(hsm, struct sm, hsm); | |
struct sm_data *d = container_of(data, struct sm_data, data); | |
strcpy(p->str, d->str); | |
printf("\"%s\"\n", p->str); | |
hsm_xit(hsm, STATE_COMPLAIN); | |
return 0; | |
} | |
static int sm_complain(struct hsm *hsm) | |
{ | |
printf("gosh\n"); | |
return 0; | |
} | |
static const struct hsm_state sm_states[] = { | |
[STATE_FILL] = { | |
.name = "filling", | |
.parent = HSM_STATE_NULL, | |
.enter = sm_enter, | |
.leave = sm_leave, | |
}, | |
[STATE_WRITE] = { | |
.name = "writing", | |
.parent = STATE_FILL, | |
.enter = sm_write_prepare, | |
.trigger = sm_write, | |
}, | |
[STATE_COMPLAIN] = { | |
.name = "complain", | |
.parent = HSM_STATE_NULL, | |
.enter = sm_complain, | |
}, | |
}; | |
int main(int argc, char **argv) | |
{ | |
struct sm sm; | |
struct sm_data data; | |
data.str = argv[0]; | |
hsm_init(&sm.hsm, sm_states); | |
hsm_xit(&sm.hsm, STATE_WRITE); | |
hsm_fire(&sm.hsm, &data.data); | |
hsm_fini(&sm.hsm); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <errno.h> | |
#include "hsm.h" | |
#define HSM_STATE_XITING (-2) | |
void hsm_init(struct hsm *hsm, persistent const struct hsm_state *states) | |
{ | |
hsm->states = states; | |
hsm->state = HSM_STATE_NULL; | |
} | |
void hsm_fini(struct hsm *hsm) | |
{ | |
hsm_xit(hsm, HSM_STATE_NULL); | |
} | |
#if DEBUG | |
#include <stdio.h> | |
#define LOGD(x, ...) fprintf(stderr, x "\n", ##__VA_ARGS__) | |
#else | |
#define LOGD(...) | |
#endif | |
static enum hsm_state_enum hsm_next(const struct hsm_state *states, | |
enum hsm_state_enum from, enum hsm_state_enum to) | |
{ | |
/* does the target have an ancestor which is us? */ | |
for (; to != HSM_STATE_NULL; to = states[to].parent) { | |
if (states[to].parent == from) | |
return to; | |
} | |
/* the target is not our (grand)* child, the direction to go is up */ | |
return states[from].parent; | |
} | |
static int hsm_xit_function(struct hsm *hsm, | |
enum hsm_state_enum from, enum hsm_state_enum to) | |
{ | |
int (* fn)(struct hsm *); | |
if (to == HSM_STATE_NULL) { | |
LOGD("-: %s -> NULL", hsm->states[from].name); | |
fn = hsm->states[from].leave; | |
} else if (from == HSM_STATE_NULL) { | |
LOGD("+: NULL -> %s", hsm->states[to].name); | |
fn = hsm->states[to].enter; | |
} else if (hsm->states[from].parent == to) { /* exiting */ | |
LOGD("-: %s -> %s", hsm->states[from].name, hsm->states[to].name); | |
fn = hsm->states[from].leave; | |
} else { | |
LOGD("+: %s -> %s", hsm->states[from].name, hsm->states[to].name); | |
fn = hsm->states[to].enter; | |
} | |
return fn ? (* fn)(hsm) : 0; | |
} | |
int hsm_xit_one(struct hsm *hsm, enum hsm_state_enum *reqa, unsigned int nreq) | |
{ | |
enum hsm_state_enum current; | |
enum hsm_state_enum next; | |
unsigned int i; | |
int rc = 0; | |
if (hsm->state == HSM_STATE_XITING) { | |
/* The reasoning here is that there's not a defined correct | |
* way to behave, so it's best to not have any behavior. | |
* | |
* What do we do if the requested event is already along the | |
* path to our desired destination. Do we call 'enter' again, | |
* if called from an enter function of the target? What | |
* happens when upon 'enter' a state calls 'xit' twice? Is | |
* the first path canceled? On that note, should this | |
* cancel paths at all? | |
* | |
* Ergo we error. | |
*/ | |
return -EINVAL; | |
} | |
current = hsm->state; | |
hsm->state = HSM_STATE_XITING; | |
for (i = 0; i < nreq; ++i) { | |
enum hsm_state_enum req = reqa[i]; | |
while (current != req) { | |
next = hsm_next(hsm->states, current, req); | |
rc = hsm_xit_function(hsm, current, next); | |
if (rc) | |
break; | |
current = next; | |
} | |
if (current == req) | |
break; | |
} | |
hsm->state = current; | |
return rc; | |
} | |
int hsm_xit(struct hsm *hsm, enum hsm_state_enum req) | |
{ | |
return hsm_xit_one(hsm, &req, 1); | |
} | |
int hsm_fire(struct hsm *hsm, struct hsm_data *data) | |
{ | |
enum hsm_state_enum current; | |
int rc = -EAGAIN; | |
if (hsm->state == HSM_STATE_XITING) | |
return -EINVAL; | |
current = hsm->state; | |
while (current != HSM_STATE_NULL && rc == -EAGAIN) { | |
const struct hsm_state *state = &hsm->states[current]; | |
rc = state->trigger(hsm, data); | |
current = state->parent; | |
} | |
return rc; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _HSM_H_ | |
#define _HSM_H_ | |
#define persistent | |
/** HSM state index. | |
* Custom state indices should start at 0. | |
*/ | |
enum hsm_state_enum { | |
HSM_STATE_NULL = -1 /**< Parent null state. */ | |
}; | |
/** HSM data structure. | |
*/ | |
struct hsm_data { | |
unsigned int id; | |
}; | |
struct hsm; | |
/** HSM state definition. */ | |
struct hsm_state { | |
const char *name; /**< State name. */ | |
enum hsm_state_enum parent; /**< Parent/Super state. */ | |
/** Event trigger callback. | |
* @param hsm HSM structure. | |
* @param data Event type. | |
* @return 0 on successful transition, !0 on failure. | |
* | |
* If -EAGAIN is returned from the handler, the event is deferred to | |
* the parent state, if one exists. | |
*/ | |
int (* trigger)(struct hsm *hsm, struct hsm_data *data); | |
/** State enter callback. | |
* @param hsm HSM structure. | |
* @return 0 on success; !0 to cancel. | |
*/ | |
int (* enter)(struct hsm *hsm); | |
/** State exit callback. | |
* @param hsm HSM structure. | |
* @return 0 on success; !0 to cancel. | |
*/ | |
int (* leave)(struct hsm *hsm); | |
}; | |
/** HSM structure */ | |
struct hsm { | |
const struct hsm_state *states; /**< State table. */ | |
enum hsm_state_enum state; /**< Current state. */ | |
}; | |
/** Initialize a HSM. | |
* @param hsm HSM structure. | |
* @param states Array of HSM states to be indexed by state. | |
*/ | |
void hsm_init(struct hsm *hsm, persistent const struct hsm_state *states); | |
/** Finalize a HSM. | |
* @param hsm HSM structure. | |
*/ | |
void hsm_fini(struct hsm *hsm); | |
/** Transition to the first successful requested state. | |
* @param hsm HSM structure. | |
* @param reqa Array of requested states. | |
* @param nreq Number of items in the requested state array. | |
* @return 0 on successful transition; return value from handler on failure. | |
* | |
* This will not revert to the original state on failure. If desired, this | |
* can be done by caching the original state. | |
*/ | |
int hsm_xit_one(struct hsm *hsm, enum hsm_state_enum *reqa, unsigned int nreq); | |
/** Transition to requested state. | |
* @param HSM structure. | |
* @param req Requested state. | |
* @return 0 on success; return value from handler on failure. | |
* | |
* As this function may make several sub-transitions in the process | |
* of reaching the desired state, the resulting state on failure may be | |
* somewhere between the initial state and the requested state. No attempt | |
* to unwind the transition is made on failure. | |
*/ | |
int hsm_xit(struct hsm *hsm, enum hsm_state_enum req); | |
/** Send an event to the currently active state. | |
* @param hsm HSM structure. | |
* @param data Userdata | |
* @return 0 on success, !0 on failure. | |
* | |
* The return value from the handler is returned on failure. | |
*/ | |
int hsm_fire(struct hsm *hsm, struct hsm_data *data); | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment