/* $Id: mcl_fsm.cpp,v 1.1.1.1 2003/09/03 12:45:43 chneuman Exp $ */ /* * Copyright (c) 1999-2003 INRIA - Universite Paris 6 - All rights reserved * (main author: Vincent Roca - vincent.roca@inrialpes.fr) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #include "mcl_includes.h" static char * mcl_fsm_print_tx_state (mclcb_t *mclcb, tx_states_t state); static char * mcl_fsm_print_tx_event (mclcb_t *mclcb, tx_events_t event); static char * mcl_fsm_print_rx_state (mclcb_t *mclcb, rx_states_t state); static char * mcl_fsm_print_rx_event (mclcb_t *mclcb, rx_events_t event); /* * SENDER's FSM (Finite State Machine) * * NIL * | * | appli issues mcl_open() * mcl_abort() v appli issues mcl_close() * +----------- READY --------------------------------------------+ * | | | * | | first ADU submission | * | v | * | IN_TX : send NEW_ADU; send DU | * | | | | ^ | * | | | | | continue to tx or wait new ADU | * +------------+ | +-----+ | * | | | * | | appli issues mcl_close() and is blocked | * | v | * | FINISH_TO_TX : send NO_NEW_ADU; send DU | * | | | | ^ | * | | | | | there are DUs remaining | * +------------+ | +-----+ | * | | | * | | all DUs have been tx | * | | | * +------------+ | +---------------------------------------------+ * | | | * v v v * END : send CLOSE * | * | nothing else to do * v * CLOSED : mcl_close() returns * | * v * NIL */ /*static tx_states_t tx_state = TSTATE_NIL;*/ /* state in the FSM graph */ #define xx TSTATE_INVALID /* for clarity */ static const tx_states_t next_tx_state[6/*state*/][9/*event*/] = { /* NIL OPEN_CALLED NEW_ADU ALL_DU_SENT CLOSE_CALLED CLOSE_SENT CLOSE_RETURN ABORT RESET*/ /* NIL */ {xx, TSTATE_READY, xx, xx, xx, xx, xx, xx, xx}, /* READY */ {xx, xx, TSTATE_IN_TX, xx, TSTATE_END, xx, xx, TSTATE_END, TSTATE_READY}, /* IN_TX */ {xx, xx, TSTATE_IN_TX, xx, TSTATE_FINISH_TX,xx, xx, TSTATE_END, TSTATE_READY}, /* FINISH_TX*/ {xx, xx, xx, TSTATE_END, xx, xx, xx, TSTATE_END, TSTATE_READY}, /* END */ {xx, xx, xx, xx, xx, TSTATE_CLOSED, xx, TSTATE_END, TSTATE_READY}, /* CLOSED */ {xx, xx, xx, xx, xx, xx, TSTATE_NIL, TSTATE_NIL, xx} }; #undef xx /* * RECEIVER's FSM (Finite State Machine) * * NIL * | * | appli issues mcl_open() * v * READY * | | | * +----+ | +-------------------------------+ * recv | | | appli calls * CLOSE | | first NEW_ADU received | mcl_close() * | v | * | IN_RX : | * | | | | | ^ | * | | | | | | recv NEW_ADU; recv DU | * +---+ | | +-----+ | * recv | | +--------------------------------+ * CLOSE | | | appli calls * | | recv NO_NEW_ADU from peer | mcl_close() * | v | * | FINISH_RX : | * | | | | | ^ | * | | | | | | recv DU | * +----+ | | +-----+ | * recv | | +-------------------------------+ * CLOSE | | | appli calls * | | all DUs have been received | mcl_close() * | v | * | END : | * | | | | ^ | * | | | | | recv DU (ignored) | * recv +-----+ | +-----+ | * CLOSE | | | * | | | * v | | * CLOSE_RX | | * | | | * +-------+--------------------------------+ * | appli calls mcl_close() * | * v * CLOSED : mcl_close() returns * | * v * NIL * * State END: * we have received everything and the sender will not send any new ADU * we may still receive (duplicated) DUs but we can stop at any time * this is the normal (almost) final state when everything is ok */ /*static rx_states_t rx_state = RSTATE_NIL;*/ /* state in the FSM graph */ #define xx RSTATE_INVALID /* for clarity */ static const rx_states_t next_rx_state[7/*state*/][9/*event*/] = { /* NIL, OPEN_CALLED NEW_ADU NO_NEW_ADU DU_RECV ALL_DU_RECV CLOSE_CALLED CLOSE_RECV CLOSE_RETURN*/ /* NIL */ {xx, RSTATE_READY, xx, xx, xx, xx, xx, xx, xx}, /* READY */ {xx, xx, RSTATE_IN_RX, xx, xx, xx, RSTATE_CLOSED, RSTATE_CLOSE_RX,xx}, /* IN_RX */ {xx, xx, RSTATE_IN_RX, RSTATE_FINISH_RX,RSTATE_IN_RX, xx, RSTATE_CLOSED, RSTATE_CLOSE_RX,xx}, /* FINISH_RX*/ {xx, xx, xx, RSTATE_FINISH_RX,RSTATE_FINISH_RX,RSTATE_END, RSTATE_CLOSED, RSTATE_CLOSE_RX,xx}, /* END */ {xx, xx, xx, RSTATE_END, RSTATE_END, xx, RSTATE_CLOSED, RSTATE_CLOSE_RX,xx}, /*CLOSE_RX*/ {xx, xx, xx, xx, xx, xx, RSTATE_CLOSED, RSTATE_CLOSE_RX, RSTATE_NIL}, /* CLOSED */ {xx, xx, xx, xx, xx, xx, xx, RSTATE_CLOSED, RSTATE_NIL} }; #undef xx /* * strings for debug */ static char * tx_states_msg[] = { "TSTATE_NIL", "TSTATE_READY", "TSTATE_IN_TX", "TSTATE_FINISH_TX", "TSTATE_END", "TSTATE_CLOSED", "*INVALID*"}; static char * tx_events_msg[] = { "TEVENT_NIL", "TEVENT_OPEN_CALLED", "TEVENT_NEW_ADU", "TEVENT_ALL_DU_SENT", "TEVENT_CLOSE_CALLED", "TEVENT_CLOSE_SENT", "TEVENT_CLOSE_RETURN", "TEVENT_ABORT", "TEVENT_RESET"}; static char * rx_states_msg[] = { "RSTATE_NIL", "RSTATE_READY", "RSTATE_IN_RX", "RSTATE_FINISH_RX", "RSTATE_END", "RSTATE_CLOSE_RX", "RSTATE_CLOSED", "*INVALID*"}; static char * rx_events_msg[] = { "REVENT_NIL", "REVENT_OPEN_CALLED", "REVENT_NEW_ADU", "REVENT_NO_NEW_ADU", "REVENT_DU_RECV", "REVENT_ALL_DU_RECV", "REVENT_CLOSE_CALLED", "REVENT_CLOSE_RECV", "REVENT_CLOSE_RETURN", "REVENT_RESET"}; /****** Public functions ******************************************************/ /* * sender and receiver function */ int mcl_fsm_get_state (mclcb_t *mclcb, int sender) { if (sender) return (int)mclcb->tx_state; else return (int)mclcb->rx_state; } /* * sender and receiver function */ char * mcl_fsm_print_state (mclcb_t *mclcb, int sender) { if (sender) return mcl_fsm_print_tx_state (mclcb, mclcb->tx_state); else return mcl_fsm_print_rx_state (mclcb, mclcb->rx_state); } /* * is the local endpoint opened ? * sender and receiver function */ int mcl_fsm_opened (mclcb_t *mclcb, int sender) { if (sender) return (mclcb->tx_state != TSTATE_NIL) ? 1 : 0; else return (mclcb->rx_state != RSTATE_NIL) ? 1 : 0; } /* * is the sender in a state where data can be sent ? * sender specific function */ int mcl_fsm_can_send_data (mclcb_t *mclcb) { ASSERT(mclcb->sender); return (mclcb->tx_state == TSTATE_IN_TX || mclcb->tx_state == TSTATE_FINISH_TX) ? 1 : 0; } /* * is the receiver in a state where data can be received ? * receiver specific function */ int mcl_fsm_can_recv_data (mclcb_t *mclcb) { ASSERT(mclcb->receiver); return (mclcb->rx_state == RSTATE_READY || /* req to wait for 1st NEW_ADU */ mclcb->rx_state == RSTATE_IN_RX || mclcb->rx_state == RSTATE_FINISH_RX) ? 1 : 0; } /* * do we expect new ADUs or not ? * sender and receiver function */ int mcl_fsm_no_new_adu (mclcb_t *mclcb, int sender) { if (sender) return (mclcb->tx_state == TSTATE_FINISH_TX || mclcb->tx_state == TSTATE_END || mclcb->tx_state == TSTATE_CLOSED) ? 1 : 0; else return (mclcb->rx_state == RSTATE_FINISH_RX || mclcb->rx_state == RSTATE_END || mclcb->rx_state == RSTATE_CLOSE_RX || mclcb->rx_state == RSTATE_CLOSED) ? 1 : 0; } /* * do we expect new DUs or do we have everything we need ? * receiver specific function */ int mcl_fsm_no_new_undup_du (mclcb_t *mclcb) { ASSERT(mclcb->receiver); return (mclcb->rx_state == RSTATE_END || mclcb->rx_state == RSTATE_CLOSE_RX || mclcb->rx_state == RSTATE_CLOSED) ? 1 : 0; } /* * did we already receive a CLOSE sig from the source or did we * already receive all the expected DUs and appli issued a close() ? * if yes, then we can ignore a new CLOSE sig. * receiver specific function */ int mcl_fsm_close_already_rx (mclcb_t *mclcb) { ASSERT(mclcb->receiver); return (mclcb->rx_state == RSTATE_CLOSE_RX || mclcb->rx_state == RSTATE_CLOSED) ? 1 : 0; } /* * sender and receiver function */ int mcl_fsm_close_can_return (mclcb_t *mclcb, int sender) { if (sender) return (mclcb->tx_state == TSTATE_CLOSED) ? 1 : 0; else return (mclcb->rx_state == RSTATE_CLOSED) ? 1 : 0; } /* * sender and receiver function */ int mcl_fsm_closed (mclcb_t *mclcb, int sender) { if (sender) return (mclcb->tx_state == TSTATE_CLOSED || mclcb->tx_state == TSTATE_NIL) ? 1 : 0; else return (mclcb->rx_state == RSTATE_CLOSED || mclcb->rx_state == RSTATE_NIL) ? 1 : 0; } /* * sender and receiver function */ void mcl_fsm_update_state (mclcb_t *mclcb, int sender, tx_events_t tevent, rx_events_t revent) { TRACELVL(5, (mcl_stdout, "-> mcl_fsm_update_state: tevent=%d, revent=%d\n", tevent, revent)) if (sender) { tx_states_t new_state; ASSERT (tevent >= TEVENT_OPEN_CALLED && tevent <= TEVENT_RESET); if ((new_state = next_tx_state[mclcb->tx_state][tevent]) == TSTATE_INVALID) { PRINT_ERR((mcl_stderr, "mcl_fsm_update_state: ERROR, event %s invalid in state %s\n", mcl_fsm_print_tx_event(mclcb, tevent), mcl_fsm_print_tx_state(mclcb, mclcb->tx_state))) mcl_exit(1); } mclcb->tx_state = new_state; } else { rx_states_t new_state; ASSERT (revent >= REVENT_OPEN_CALLED && revent <= REVENT_CLOSE_RETURN); if ((new_state = next_rx_state[mclcb->rx_state][revent]) == RSTATE_INVALID) { PRINT_ERR((mcl_stderr, "mcl_fsm_update_state: ERROR, event %s invalid in state %s\n", mcl_fsm_print_rx_event(mclcb, revent), mcl_fsm_print_rx_state(mclcb, mclcb->rx_state))) mcl_exit(1); } mclcb->rx_state = new_state; } TRACELVL(5, (mcl_stdout, "<- mcl_fsm_update_state: new_state %s\n", sender ? mcl_fsm_print_tx_state(mclcb, mclcb->tx_state) : mcl_fsm_print_rx_state(mclcb, mclcb->rx_state))) } /****** Private functions *****************************************************/ /* * sender specific function * WARNING: the string buffer cannot be freed */ char * mcl_fsm_print_tx_state (mclcb_t *mclcb, tx_states_t state) { #ifdef DEBUG if (state < 0 || state > TSTATE_INVALID) { PRINT_ERR((mcl_stderr, "mcl_fsm_print_tx_state: ERROR, invalid state %d\n", state)) mcl_exit(1); } #endif /* DEBUG */ return tx_states_msg[state]; } /* * sender specific function * WARNING: the string buffer cannot be freed */ char * mcl_fsm_print_tx_event (mclcb_t *mclcb, tx_events_t event) { #ifdef DEBUG if (event < 0 || event > TEVENT_ABORT) { PRINT_ERR((mcl_stderr, "mcl_fsm_print_tx_event: ERROR, invalid event %d\n", event)) mcl_exit(1); } #endif /* DEBUG */ return tx_events_msg[event]; } /* * receiver specific function * WARNING: the string buffer cannot be freed */ char * mcl_fsm_print_rx_state (mclcb_t *mclcb, rx_states_t state) { #ifdef DEBUG if (state < 0 || state > RSTATE_INVALID) { PRINT_ERR((mcl_stderr, "mcl_fsm_print_rx_state: ERROR, invalid state %d\n", state)) mcl_exit(1); } #endif /* DEBUG */ return rx_states_msg[state]; } /* * receiver specific function * WARNING: the string buffer cannot be freed */ char * mcl_fsm_print_rx_event (mclcb_t *mclcb, rx_events_t event) { #ifdef DEBUG if (event < 0 || event > REVENT_CLOSE_RETURN) { PRINT_ERR((mcl_stderr, "mcl_fsm_print_rx_event: ERROR, invalid event %d\n", event)) mcl_exit(1); } #endif /* DEBUG */ return rx_events_msg[event]; }