Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief Enduro/X State Machine (moore machine)
0003  *
0004  * @file exsm.c
0005  */
0006 /* -----------------------------------------------------------------------------
0007  * Enduro/X Middleware Platform for Distributed Transaction Processing
0008  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0009  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0010  * This software is released under one of the following licenses:
0011  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0012  * See LICENSE file for full text.
0013  * -----------------------------------------------------------------------------
0014  * AGPL license:
0015  *
0016  * This program is free software; you can redistribute it and/or modify it under
0017  * the terms of the GNU Affero General Public License, version 3 as published
0018  * by the Free Software Foundation;
0019  *
0020  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0021  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0022  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0023  * for more details.
0024  *
0025  * You should have received a copy of the GNU Affero General Public License along 
0026  * with this program; if not, write to the Free Software Foundation, Inc.,
0027  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0028  *
0029  * -----------------------------------------------------------------------------
0030  * A commercial use license is available from Mavimax, Ltd
0031  * contact@mavimax.com
0032  * -----------------------------------------------------------------------------
0033  */
0034 /*---------------------------Includes-----------------------------------*/
0035 #include <ndrx_config.h>
0036 #include <ndrstandard.h>
0037 #include <ndebug.h>
0038 #include <exsm.h>
0039 /*---------------------------Externs------------------------------------*/
0040 /*---------------------------Macros-------------------------------------*/
0041 /*---------------------------Enums--------------------------------------*/
0042 /*---------------------------Typedefs-----------------------------------*/
0043 
0044 /**
0045  * handle for accessing state info
0046  * must match fields of NDRX_SM_T
0047  */
0048 typedef struct
0049 {
0050     int state; /**< state number */
0051     char state_name[32]; /**< state name */
0052     int (*func)(void *data); /**< state function (callback) */
0053     int flags;              /**< internal flags of the state */
0054     ndrx_sm_tran_t transitions[0]; /**< transitions */
0055 } ndrx_sm_state_handle_t;
0056 /*---------------------------Globals------------------------------------*/
0057 /*---------------------------Statics------------------------------------*/
0058 /*---------------------------Prototypes---------------------------------*/
0059 
0060 /**
0061  * Get state handle
0062  * @param sm state machine (basically according to ndrx_sm_state_handle_t,
0063  * but having dynamic number of transitions)
0064  * @param nr_tran number of transitions per state (must be same for all states)
0065  * @param state state number
0066  * @return state handle
0067  */
0068 exprivate ndrx_sm_state_handle_t * ndrx_sm_state_get(void *sm, int nr_tran, int state)
0069 {
0070     return (ndrx_sm_state_handle_t *)( 
0071             (char *)sm + (sizeof(ndrx_sm_state_handle_t)+nr_tran*sizeof(ndrx_sm_tran_t))*state);
0072 }
0073 
0074 /**
0075  * return size of the single state
0076  * @param nr_tran number of transitions per state (must be same for all states)
0077  * @return state object size in bytes
0078  */
0079 exprivate size_t ndrx_sm_state_size(int nr_tran)
0080 {
0081     return (sizeof(ndrx_sm_state_handle_t) + nr_tran*sizeof(ndrx_sm_tran_t));
0082 }
0083 
0084 /**
0085  * Run state machine.
0086  * @param sm state machine (basically according to ndrx_sm_state_handle_t,
0087  *  but having dynamic number of transitions)
0088  * @param nr_tran number of transitions per state (must be same for all states)
0089  * @param data data to pass to state functions
0090  * @param log_topic LOG_FACILITY_NDRX or LOG_FACILITY_TP
0091  * @return last event passed to NDRX_SM_ST_RETURN (or -1 if error)
0092  */
0093 expublic int ndrx_sm_run(void *sm, int nr_tran, int entry_state, void *data, int log_topic)
0094 {
0095     int ret = EXSUCCEED;
0096     ndrx_sm_state_handle_t *cur = ndrx_sm_state_get(sm, nr_tran, entry_state);
0097 
0098     while (NDRX_SM_ST_RETURN!=cur->state)
0099     {
0100         int i;
0101         int next_state = -1;
0102         int event = -1;
0103         
0104         /* run state function */
0105         event = cur->func(data);
0106         
0107         /* find next state */
0108         if (cur->flags & NDRX_SM_FLAGS_SCAN)
0109         {
0110             for (i=0; i<nr_tran; i++)
0111             {
0112                  if (NDRX_SM_EV_EOF==cur->transitions[i].event)
0113                  {
0114                     break;
0115                  }
0116                  else if (cur->transitions[i].event == event)
0117                  {
0118                     break;
0119                  }
0120             }
0121         }
0122         else if (cur->flags & NDRX_SM_FLAGS_INDEX)
0123         {
0124             if (event>=0 && event<nr_tran)
0125             {
0126                 i = event;
0127             }
0128             else
0129             {
0130                 /* event ouf of the rnage: */
0131                 if (LOG_FACILITY_TP==log_topic)
0132                 {
0133                     TP_LOG(log_error, "sm: ERROR ! state %s (%d), event %d: "
0134                         "no transition found!", cur->state_name, cur->state, event);
0135                 }
0136                 else
0137                 {
0138                     NDRX_LOG(log_error, "sm: ERROR ! state %s (%d), event %d: "
0139                         "no transition found!", cur->state_name, cur->state, event);
0140                 }
0141                 userlog("sm: ERROR ! state %s (%d), event %d: "
0142                         "no transition found!",cur->state_name, cur->state, event);
0143                 EXFAIL_OUT(ret);
0144             }
0145         }
0146         else
0147         {
0148             if (LOG_FACILITY_TP==log_topic)
0149             {
0150                 TP_LOG(log_error, "sm: ERROR ! machine not compiled (flags=%x)",
0151                         cur->flags);
0152             }
0153             else
0154             {
0155                 NDRX_LOG(log_error, "sm: ERROR ! machine not compiled (flags=%x)",
0156                         cur->flags);
0157             }
0158             userlog("sm: ERROR ! machine not compiled (flags=%x)",
0159                     cur->flags);
0160             EXFAIL_OUT(ret);
0161         }
0162 
0163         if (NDRX_SM_EV_EOF==cur->transitions[i].event)
0164         {
0165             if (LOG_FACILITY_TP==log_topic)
0166             {
0167                 TP_LOG(log_error, "sm: ERROR ! state %s (%d), event %d: "
0168                         "no transition found!", cur->state_name, cur->state, event);
0169             }
0170             else
0171             {
0172                 NDRX_LOG(log_error, "sm: ERROR ! state %s (%d), event %d: "
0173                         "no transition found!", cur->state_name, cur->state, event);
0174             }
0175             userlog("sm: ERROR ! state %s (%d), event %d: "
0176                     "no transition found!",cur->state_name, cur->state, event);
0177             EXFAIL_OUT(ret);
0178         }
0179         else if (cur->transitions[i].event == event)
0180         {
0181             if (LOG_FACILITY_TP==log_topic)
0182             {
0183                 TP_LOG(log_debug , "sm: %s, event %s", 
0184                     cur->state_name, cur->transitions[i].event_name);
0185             }
0186             else
0187             {
0188                 NDRX_LOG(log_debug , "sm: %s, event %s", 
0189                     cur->state_name, cur->transitions[i].event_name);
0190             }
0191             next_state = cur->transitions[i].next_state;
0192         }
0193 
0194         if (NDRX_SM_ST_RETURN==next_state)
0195         {
0196             if (LOG_FACILITY_TP==log_topic)
0197             {
0198                 TP_LOG(log_debug, "sm: return from %s, with ret=%d", 
0199                     cur->state_name, event);
0200             }
0201             else
0202             {
0203                 NDRX_LOG(log_debug, "sm: return from %s, with ret=%d", 
0204                     cur->state_name, event);
0205             }
0206             
0207             ret=event;
0208             goto out;
0209         }
0210         else if (NDRX_SM_ST_RETURN0==next_state)
0211         {
0212             if (LOG_FACILITY_TP==log_topic)
0213             {
0214                 TP_LOG(log_debug, "sm: return from %s, with ev %d as ret=0",
0215                     cur->state_name, event);
0216             }
0217             else
0218             {
0219                 NDRX_LOG(log_debug, "sm: return from %s, with ev %d as ret=0",
0220                     cur->state_name, event);
0221             }
0222             
0223             ret=0;
0224             goto out;
0225         }
0226         
0227         cur = ndrx_sm_state_get(sm, nr_tran, next_state);       
0228     }
0229 out:
0230     return ret;
0231 }
0232 
0233 /**
0234  * Compile + Validate state machine.
0235  * Compilation makes converts structures for mapping events to array indexes for transitions
0236  * (where possible)
0237  * !!!! This assumes that SM buffer size is enough to handle largest state number.
0238  * thus machine must be allocated with last state in mind.
0239  * Checks:
0240  *  - states are in sequence with memory arrays
0241  *  - every state transitions ends with NDRX_SM_EV_EOF (may cause core dump if not found)
0242  *  - every state transitions count is not greater than nr_tran
0243  *  - every state transitions point to valid state (i.e. state nr is less than last_state)
0244  * No gaps in states are allowed.
0245  * @param sm state machine (basically according to ndrx_sm_state_handle_t,
0246  * but having dynamic number of transitions)
0247  * @param nr_state number of total states
0248  * @param nr_tran number of transitions per state (must be same for all states)
0249  * @param last_last last state used of the machine, so that remainder can be reset.
0250  * @return 0 if ok, -1 if error
0251  */
0252 expublic int ndrx_sm_comp(void *sm, int nr_state, int nr_tran, int last_state)
0253 {
0254     int i;
0255     int tran;
0256     int ret = EXSUCCEED;
0257     int got_eof;
0258     int in_range;
0259     size_t state_size = ndrx_sm_state_size(nr_tran);
0260     char tmp[ndrx_sm_state_size(nr_tran)*nr_state];
0261 
0262     /* fix the sm.., reset remainder states, as array space is larger than
0263      * number of states
0264      */
0265     got_eof=EXFALSE;
0266     for (i=0; i<nr_state; i++)
0267     {
0268         ndrx_sm_state_handle_t *cur;
0269         cur = ndrx_sm_state_get(sm, nr_tran, i);
0270 
0271         if (got_eof)
0272         {
0273             cur->state=EXFAIL;
0274         }
0275         else if (cur->state==last_state)
0276         {
0277             got_eof=EXTRUE;
0278         }
0279         else if (cur->state >=nr_state)
0280         {
0281             NDRX_LOG(log_error, "sm: ERROR ! got state %d, but max allowed is %d!",
0282                     cur->state, nr_state-1);
0283             EXFAIL_OUT(ret);
0284         }
0285     }
0286 
0287     memcpy(tmp, sm, sizeof(tmp));
0288 
0289     /* reset SM */
0290     got_eof=EXFALSE;
0291     for (i=0; i<nr_state; i++)
0292     {
0293         ndrx_sm_state_handle_t *cur;
0294         cur = ndrx_sm_state_get(sm, nr_tran, i);
0295         cur->state=i;
0296         cur->func=NULL;
0297         if (tran>0)
0298         {
0299             cur->transitions[0].event=NDRX_SM_EV_EOF;
0300         }
0301     }
0302 
0303     /* generate new mapping */
0304     for (i=0; i<nr_state; i++)
0305     {
0306         ndrx_sm_state_handle_t *cur;
0307         ndrx_sm_state_handle_t *tmp_cur;
0308 
0309         tmp_cur = ndrx_sm_state_get(tmp, nr_tran, i);
0310 
0311         if (tmp_cur->state==EXFAIL)
0312         {
0313             /* we are done... */
0314             break;
0315         }
0316         
0317         /* map out the states to indexes */
0318         cur = ndrx_sm_state_get(sm, nr_tran, tmp_cur->state);
0319 
0320         memcpy(cur, tmp_cur, state_size);
0321     }
0322 
0323 
0324     /* validate indexes */
0325     for (i=0; i<nr_state; i++)
0326     {
0327         ndrx_sm_state_handle_t *cur = ndrx_sm_state_get(sm, nr_tran, i);
0328 
0329         /* check that state is intialized */
0330 
0331         if (cur->state != i)
0332         {
0333             NDRX_LOG(log_error, "sm: ERROR ! state %s (%d) is not in sequence!", 
0334                     cur->state_name, cur->state);
0335             EXFAIL_OUT(ret);
0336         }
0337 
0338         got_eof=EXFALSE;
0339         in_range=EXTRUE;
0340 
0341         for (tran=0; tran<nr_tran; tran++)
0342         {
0343             if (NDRX_SM_EV_EOF==cur->transitions[tran].event)
0344             {
0345                 /* ok */
0346                 got_eof=EXTRUE;
0347                 break;
0348             }
0349             else if ( (cur->transitions[tran].next_state <0 ||
0350                 cur->transitions[tran].next_state >nr_state-1) &&
0351                 NDRX_SM_ST_RETURN!=cur->transitions[tran].next_state &&
0352                 NDRX_SM_ST_RETURN0!=cur->transitions[tran].next_state
0353                 )
0354             {
0355                 NDRX_LOG(log_error, "sm: ERROR ! state %s (%d), event %s (%d): "
0356                         "next state %d is out of range [%d..%d]!",
0357                         cur->state_name, cur->state, cur->transitions[tran].event_name,
0358                         cur->transitions[tran].event, cur->transitions[tran].next_state,
0359                         0, nr_state-1);
0360                 EXFAIL_OUT(ret);
0361             }
0362             else if (
0363                 NDRX_SM_ST_RETURN!=cur->transitions[tran].next_state &&
0364                 NDRX_SM_ST_RETURN0!=cur->transitions[tran].next_state &&
0365                 NULL==ndrx_sm_state_get(sm, nr_tran, cur->transitions[tran].next_state)->func
0366                 )
0367             {
0368                 NDRX_LOG(log_error, "sm: ERROR ! state %s (%d), event %s (%d): "
0369                         "next state %d is not used!",
0370                         cur->state_name, cur->state, cur->transitions[tran].event_name,
0371                         cur->transitions[tran].event, cur->transitions[tran].next_state);
0372                 EXFAIL_OUT(ret);
0373             }
0374             else if (cur->transitions[tran].event<0 || cur->transitions[tran].event>=nr_tran)
0375             {
0376                 in_range=EXFALSE;
0377             }
0378         }
0379 
0380         /* build index  */
0381         if (in_range)
0382         {
0383             ndrx_sm_tran_t tmp_trn[nr_tran];
0384             got_eof=EXFALSE;
0385             /* copy current table */
0386             memcpy(tmp_trn, cur->transitions, sizeof(tmp_trn));
0387 
0388             cur->flags |= NDRX_SM_FLAGS_INDEX;
0389             /* generate index table... */
0390             for (tran=0; tran<nr_tran; tran++)
0391             {
0392                 /* reset all to EOF*/
0393                 cur->transitions[tran].event = NDRX_SM_EV_EOF;
0394             }
0395             for (tran=0; tran<nr_tran; tran++)
0396             {
0397                 if (NDRX_SM_EV_EOF==tmp_trn[tran].event)
0398                 {
0399                     /* mapping done. */
0400                     break;
0401                 }
0402 
0403                 cur->transitions[tmp_trn[tran].event] = tmp_trn[tran];
0404             }
0405         }
0406         else
0407         {
0408             cur->flags |= NDRX_SM_FLAGS_SCAN;
0409         }
0410     }
0411 
0412 out:
0413     return ret;
0414 }
0415 
0416 /* vim: set ts=4 sw=4 et smartindent: */