stm.c - pequeño motor de maquina de estados
La interfaz es muy simple, y no es un ADT. Los estados se identifican con un número entero (típicamente proveniente de un enum). - El usuario instancia un `struct state_machine' - Describe la maquina de estados: - describe el estado inicial en `initial' - todos los posibles estados en `states' (el orden debe coincidir con el identificador) - describe la cantidad de estados en `states'. Provee todas las funciones necesitadas en un `struct fd_handler'
This commit is contained in:
parent
15ca9ae0c3
commit
8a8d30453f
|
@ -0,0 +1,103 @@
|
||||||
|
/**
|
||||||
|
* stm.c - pequeño motor de maquina de estados donde los eventos son los
|
||||||
|
* del selector.c
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "stm.h"
|
||||||
|
|
||||||
|
#define N(x) (sizeof(x)/sizeof((x)[0]))
|
||||||
|
|
||||||
|
void
|
||||||
|
stm_init(struct state_machine *stm) {
|
||||||
|
// verificamos que los estados son correlativos, y que están bien asignados.
|
||||||
|
for(unsigned i = 0 ; i <= stm->max_state; i++) {
|
||||||
|
if(i != stm->states[i].state) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stm->initial < stm->max_state) {
|
||||||
|
stm->current = NULL;
|
||||||
|
} else {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void
|
||||||
|
handle_first(struct state_machine *stm, struct selector_key *key) {
|
||||||
|
if(stm->current == NULL) {
|
||||||
|
stm->current = stm->states + stm->initial;
|
||||||
|
if(NULL != stm->current->on_arrival) {
|
||||||
|
stm->current->on_arrival(stm->current->state, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static
|
||||||
|
void jump(struct state_machine *stm, unsigned next, struct selector_key *key) {
|
||||||
|
if(next > stm->max_state) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if(stm->current != stm->states + next) {
|
||||||
|
if(stm->current != NULL && stm->current->on_departure != NULL) {
|
||||||
|
stm->current->on_departure(stm->current->state, key);
|
||||||
|
}
|
||||||
|
stm->current = stm->states + next;
|
||||||
|
|
||||||
|
if(NULL != stm->current->on_arrival) {
|
||||||
|
stm->current->on_arrival(stm->current->state, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
stm_handler_read(struct state_machine *stm, struct selector_key *key) {
|
||||||
|
handle_first(stm, key);
|
||||||
|
if(stm->current->on_read_ready == 0) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
const unsigned int ret = stm->current->on_read_ready(key);
|
||||||
|
jump(stm, ret, key);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
stm_handler_write(struct state_machine *stm, struct selector_key *key) {
|
||||||
|
handle_first(stm, key);
|
||||||
|
if(stm->current->on_write_ready == 0) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
const unsigned int ret = stm->current->on_write_ready(key);
|
||||||
|
jump(stm, ret, key);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
stm_handler_block(struct state_machine *stm, struct selector_key *key) {
|
||||||
|
handle_first(stm, key);
|
||||||
|
if(stm->current->on_block_ready == 0) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
const unsigned int ret = stm->current->on_block_ready(key);
|
||||||
|
jump(stm, ret, key);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
stm_handler_close(struct state_machine *stm, struct selector_key *key) {
|
||||||
|
if(stm->current != NULL && stm->current->on_departure != NULL) {
|
||||||
|
stm->current->on_departure(stm->current->state, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
stm_state(struct state_machine *stm) {
|
||||||
|
unsigned ret = stm->initial;
|
||||||
|
if(stm->current != NULL) {
|
||||||
|
ret= stm->current->state;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
#ifndef STM_H_wL7YxN65ZHqKGvCPrNbPtMJgL8B
|
||||||
|
#define STM_H_wL7YxN65ZHqKGvCPrNbPtMJgL8B
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stm.c - pequeño motor de maquina de estados donde los eventos son los
|
||||||
|
* del selector.c
|
||||||
|
*
|
||||||
|
* La interfaz es muy simple, y no es un ADT.
|
||||||
|
*
|
||||||
|
* Los estados se identifican con un número entero (típicamente proveniente de
|
||||||
|
* un enum).
|
||||||
|
*
|
||||||
|
* - El usuario instancia un `struct state_machine'
|
||||||
|
* - Describe la maquina de estados:
|
||||||
|
* - describe el estado inicial en `initial'
|
||||||
|
* - todos los posibles estados en `states' (el orden debe coincidir con
|
||||||
|
* el identificador)
|
||||||
|
* - describe la cantidad de estados en `states'.
|
||||||
|
*
|
||||||
|
* Provee todas las funciones necesitadas en un `struct fd_handler'
|
||||||
|
* de selector.c.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct state_machine {
|
||||||
|
/** declaración de cual es el estado inicial */
|
||||||
|
unsigned initial;
|
||||||
|
/**
|
||||||
|
* declaracion de los estados: deben estar ordenados segun .[].state.
|
||||||
|
*/
|
||||||
|
const struct state_definition *states;
|
||||||
|
/** cantidad de estados */
|
||||||
|
unsigned max_state;
|
||||||
|
/** estado actual */
|
||||||
|
const struct state_definition *current;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct selector_key *key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* definición de un estado de la máquina de estados
|
||||||
|
*/
|
||||||
|
struct state_definition {
|
||||||
|
/**
|
||||||
|
* identificador del estado: típicamente viene de un enum que arranca
|
||||||
|
* desde 0 y no es esparso.
|
||||||
|
*/
|
||||||
|
unsigned state;
|
||||||
|
|
||||||
|
/** ejecutado al arribar al estado */
|
||||||
|
void (*on_arrival) (const unsigned state, struct selector_key *key);
|
||||||
|
/** ejecutado al salir del estado */
|
||||||
|
void (*on_departure) (const unsigned state, struct selector_key *key);
|
||||||
|
/** ejecutado cuando hay datos disponibles para ser leidos */
|
||||||
|
unsigned (*on_read_ready) (struct selector_key *key);
|
||||||
|
/** ejecutado cuando hay datos disponibles para ser escritos */
|
||||||
|
unsigned (*on_write_ready)(struct selector_key *key);
|
||||||
|
/** ejecutado cuando hay una resolución de nombres lista */
|
||||||
|
unsigned (*on_block_ready)(struct selector_key *key);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** inicializa el la máquina */
|
||||||
|
void
|
||||||
|
stm_init(struct state_machine *stm);
|
||||||
|
|
||||||
|
/** obtiene el identificador del estado actual */
|
||||||
|
unsigned
|
||||||
|
stm_state (struct state_machine *stm);
|
||||||
|
|
||||||
|
/** indica que ocurrió el evento read. retorna nuevo id de nuevo estado. */
|
||||||
|
unsigned
|
||||||
|
stm_handler_read(struct state_machine *stm, struct selector_key *key);
|
||||||
|
|
||||||
|
/** indica que ocurrió el evento write. retorna nuevo id de nuevo estado. */
|
||||||
|
unsigned
|
||||||
|
stm_handler_write(struct state_machine *stm, struct selector_key *key);
|
||||||
|
|
||||||
|
/** indica que ocurrió el evento block. retorna nuevo id de nuevo estado. */
|
||||||
|
unsigned
|
||||||
|
stm_handler_block(struct state_machine *stm, struct selector_key *key);
|
||||||
|
|
||||||
|
/** indica que ocurrió el evento close. retorna nuevo id de nuevo estado. */
|
||||||
|
void
|
||||||
|
stm_handler_close(struct state_machine *stm, struct selector_key *key);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,143 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <check.h>
|
||||||
|
#include "selector.h"
|
||||||
|
#include "stm.h"
|
||||||
|
|
||||||
|
enum test_states {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct data {
|
||||||
|
bool arrived [3];
|
||||||
|
bool departed[3];
|
||||||
|
unsigned i;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_arrival(const unsigned state, struct selector_key *key) {
|
||||||
|
struct data *d = (struct data *)key->data;
|
||||||
|
d->arrived[state] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_departure(const unsigned state,struct selector_key *key) {
|
||||||
|
struct data *d = (struct data *)key->data;
|
||||||
|
d->departed[state] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
on_read_ready(struct selector_key *key) {
|
||||||
|
struct data *d = (struct data *)key->data;
|
||||||
|
unsigned ret;
|
||||||
|
|
||||||
|
if(d->i < C) {
|
||||||
|
ret = ++d->i;
|
||||||
|
} else {
|
||||||
|
ret = C;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
on_write_ready(struct selector_key *key) {
|
||||||
|
return on_read_ready(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct state_definition statbl[] = {
|
||||||
|
{
|
||||||
|
.state = A,
|
||||||
|
.on_arrival = on_arrival,
|
||||||
|
.on_departure = on_departure,
|
||||||
|
.on_read_ready = on_read_ready,
|
||||||
|
.on_write_ready = on_write_ready,
|
||||||
|
},{
|
||||||
|
.state = B,
|
||||||
|
.on_arrival = on_arrival,
|
||||||
|
.on_departure = on_departure,
|
||||||
|
.on_read_ready = on_read_ready,
|
||||||
|
.on_write_ready = on_write_ready,
|
||||||
|
},{
|
||||||
|
.state = C,
|
||||||
|
.on_arrival = on_arrival,
|
||||||
|
.on_departure = on_departure,
|
||||||
|
.on_read_ready = on_read_ready,
|
||||||
|
.on_write_ready = on_write_ready,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//static bool init = false;
|
||||||
|
|
||||||
|
START_TEST (test_buffer_misc) {
|
||||||
|
struct state_machine stm = {
|
||||||
|
.initial = A,
|
||||||
|
.max_state = C,
|
||||||
|
.states = statbl,
|
||||||
|
};
|
||||||
|
struct data data = {
|
||||||
|
.i = 0,
|
||||||
|
};
|
||||||
|
struct selector_key key = {
|
||||||
|
.data = &data,
|
||||||
|
};
|
||||||
|
stm_init(&stm);
|
||||||
|
ck_assert_uint_eq(A, stm_state(&stm));
|
||||||
|
ck_assert_uint_eq(false, data.arrived[A]);
|
||||||
|
ck_assert_uint_eq(false, data.arrived[B]);
|
||||||
|
ck_assert_uint_eq(false, data.arrived[C]);
|
||||||
|
ck_assert_ptr_null(stm.current);
|
||||||
|
|
||||||
|
stm_handler_read(&stm, &key);
|
||||||
|
ck_assert_uint_eq(B, stm_state(&stm));
|
||||||
|
ck_assert_uint_eq(true, data.arrived[A]);
|
||||||
|
ck_assert_uint_eq(true, data.arrived[B]);
|
||||||
|
ck_assert_uint_eq(false, data.arrived[C]);
|
||||||
|
ck_assert_uint_eq(true, data.departed[A]);
|
||||||
|
ck_assert_uint_eq(false, data.departed[B]);
|
||||||
|
ck_assert_uint_eq(false, data.departed[C]);
|
||||||
|
|
||||||
|
stm_handler_write(&stm, &key);
|
||||||
|
ck_assert_uint_eq(C, stm_state(&stm));
|
||||||
|
ck_assert_uint_eq(true, data.arrived[A]);
|
||||||
|
ck_assert_uint_eq(true, data.arrived[B]);
|
||||||
|
ck_assert_uint_eq(true, data.arrived[C]);
|
||||||
|
ck_assert_uint_eq(true, data.departed[A]);
|
||||||
|
ck_assert_uint_eq(true, data.departed[B]);
|
||||||
|
ck_assert_uint_eq(false, data.departed[C]);
|
||||||
|
|
||||||
|
stm_handler_read(&stm, &key);
|
||||||
|
ck_assert_uint_eq(C, stm_state(&stm));
|
||||||
|
ck_assert_uint_eq(true, data.arrived[A]);
|
||||||
|
ck_assert_uint_eq(true, data.arrived[B]);
|
||||||
|
ck_assert_uint_eq(true, data.arrived[C]);
|
||||||
|
ck_assert_uint_eq(true, data.departed[A]);
|
||||||
|
ck_assert_uint_eq(true, data.departed[B]);
|
||||||
|
ck_assert_uint_eq(false, data.departed[C]);
|
||||||
|
|
||||||
|
stm_handler_close(&stm, &key);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
Suite *
|
||||||
|
suite(void) {
|
||||||
|
Suite *s = suite_create("nio_stm");
|
||||||
|
TCase *tc = tcase_create("nio_stm");
|
||||||
|
|
||||||
|
tcase_add_test(tc, test_buffer_misc);
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void) {
|
||||||
|
SRunner *sr = srunner_create(suite());
|
||||||
|
int number_failed;
|
||||||
|
|
||||||
|
srunner_run_all(sr, CK_NORMAL);
|
||||||
|
number_failed = srunner_ntests_failed(sr);
|
||||||
|
srunner_free(sr);
|
||||||
|
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
}
|
Loading…
Reference in New Issue