diff --git a/src/stm.c b/src/stm.c new file mode 100644 index 0000000..081f55a --- /dev/null +++ b/src/stm.c @@ -0,0 +1,103 @@ +/** + * stm.c - pequeño motor de maquina de estados donde los eventos son los + * del selector.c + */ +#include +#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; +} diff --git a/src/stm.h b/src/stm.h new file mode 100644 index 0000000..f20a68f --- /dev/null +++ b/src/stm.h @@ -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 diff --git a/src/stm_test.c b/src/stm_test.c new file mode 100644 index 0000000..6b937d9 --- /dev/null +++ b/src/stm_test.c @@ -0,0 +1,143 @@ +#include +#include +#include +#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; +}