sync/gen_statemachine.ysl2
author Edouard Tisserant <edouard@pep-project.org>
Sun, 04 Dec 2016 02:51:59 +0100
branchENGINE-133
changeset 1460 66ceb5a7f718
parent 1445 5d233bcdde76
child 1467 ff7c60d14af0
permissions -rw-r--r--
ENGINE-133 added state 'payload' pointer to data attached to the state, allocated/dealocated on transitions. This state payload allows spontaneous transitions having no context (i.e. timeout) to get some context about the state. It can be used in other cases like checking that received group keys are from the partner we expect (TODO)
     1 // generate state machine code
     2 
     3 // Copyleft (c) 2016, p≡p foundation
     4 
     5 // Written by Volker Birk
     6 
     7 include yslt.yml2
     8 
     9 tstylesheet {
    10     include standardlib.ysl2
    11     include ./functions.ysl2
    12 
    13     template "/protocol" {
    14         document "../src/Makefile.protocols", "text"
    15             apply "fsm", 0, mode="make";
    16         apply "fsm", 0, mode=gen;
    17     }
    18 
    19     template "fsm", mode=make
    20     ||
    21     «@filename»_fsm.c: ../sync/devicegroup.fsm
    22     \tmake -C ../«@filename»
    23     ||
    24 
    25     template "fsm", mode=gen {
    26         document "../src/{@filename}_fsm.h", "text" {
    27         ||
    28         #pragma once
    29 
    30         // state machine for «@name»
    31 
    32         #include "message_api.h"
    33         
    34         #ifdef __cplusplus
    35         extern "C" {
    36         #endif
    37 
    38         // types
    39 
    40         typedef pEp_identity * Identity;
    41         typedef stringlist_t * Stringlist;
    42         typedef union _param { Identity partner; stringlist_t *keylist; } param_t;
    43 
    44         // error values
    45 
    46         typedef enum _fsm_error {
    47             // these error values are corresponding to
    48             // PEP_SYNC_STATEMACHINE_ERROR - value
    49             invalid_state = -2,
    50             invalid_event = -3,
    51             invalid_condition = -4,
    52             invalid_action = -5,
    53 
    54             // out of memory condition
    55             invalid_out_of_memory = -128
    56         } fsm_error;
    57 
    58         // conditions
    59 
    60         `` for "func:distinctName(condition)" | int «@name»(PEP_SESSION session`apply "parm", 0`);
    61 
    62         // states
    63 
    64         typedef enum _«@name»_state {
    65             // error values also in this namespace
    66             «@name»_state_invalid_state = (int) invalid_state,
    67             «@name»_state_invalid_event = (int) invalid_event,
    68             «@name»_state_invalid_condition = (int) invalid_condition,
    69             «@name»_state_invalid_action = (int) invalid_action,
    70             «@name»_state_invalid_out_of_memory = (int) invalid_out_of_memory,
    71 
    72             «@name»_state_NONE = 0,
    73         `` for "func:distinctName(state)" |> «@name»`if "position()!=last()" > , `
    74         } «@name»_state;
    75 
    76         // events
    77 
    78         typedef enum _«@name»_event {
    79             «@name»_event_NONE = 0,
    80         ||
    81         for "func:distinctName(state/event[not(not(/protocol/fsm/tag/@name=@name))])" {
    82             const "name", "@name";
    83             |> «$name» = «/protocol/fsm/tag[@name=$name]/@id»,
    84         }
    85         for "func:distinctName(state/event[not(/protocol/fsm/tag/@name=@name)])"
    86             |> «@name»`if "position()!=last()" > , `
    87         ||
    88         } «@name»_event;
    89 
    90         // actions
    91 
    92         `` const "name", "@name"
    93         `` for "func:distinctName(//action)" | PEP_STATUS «@name»(PEP_SESSION session, «$name»_state state, Identity partner, void *extra);
    94 
    95         // event injector
    96 
    97         PEP_STATUS inject_DeviceState_event(
    98             PEP_SESSION session, 
    99             DeviceState_event event,
   100             Identity partner,
   101             void *extra);
   102 
   103         // message receiver
   104         
   105         PEP_STATUS receive_DeviceState_msg(
   106                 PEP_SESSION session, 
   107                 message *src, 
   108                 PEP_rating rating, 
   109                 stringlist_t *keylist
   110             );
   111 
   112         // state machine
   113 
   114         «@name»_state fsm_«@name»(
   115                 PEP_SESSION session,
   116                 «@name»_state state,
   117                 «@name»_event event,
   118                 Identity partner,
   119                 void *extra,
   120                 time_t *timeout
   121             );
   122 
   123         // driver
   124 
   125         DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
   126                 PEP_SESSION session,
   127                 «@name»_event event,
   128                 Identity partner,
   129                 void *extra,
   130                 time_t *timeout
   131             );
   132 
   133         #ifdef __cplusplus
   134         }
   135         #endif
   136 
   137         ||
   138         }
   139         document "../src/{@filename}_driver.c", "text"
   140         ||
   141         // Driver for «@name» state machine
   142 
   143         #include <assert.h>
   144         #include "pEp_internal.h"
   145 
   146 
   147         DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
   148                 PEP_SESSION session,
   149                 «@name»_event event,
   150                 Identity partner,
   151                 void *extra,
   152                 time_t *timeout
   153             )
   154         {
   155             assert(session);
   156             if (!session)
   157                 return PEP_ILLEGAL_VALUE;
   158 
   159             while(true)
   160             {
   161                 «@name»_state new_state = fsm_«@name»(session,
   162                     session->«@filename»_state, event, partner, extra, timeout);
   163 
   164                 if (new_state == «@name»_state_invalid_out_of_memory)
   165                     return PEP_OUT_OF_MEMORY;
   166 
   167                 if (new_state < 0)
   168                     return PEP_SYNC_STATEMACHINE_ERROR - new_state;
   169                 
   170                 if (new_state == session->«@filename»_state)
   171                     break;
   172 
   173                 event = Init;
   174                 extra = NULL;
   175                 session->«@filename»_state = new_state;
   176             } 
   177 
   178             return PEP_STATUS_OK;
   179         }
   180 
   181         ||
   182         document "../src/{@filename}_fsm.c", "text"
   183         ||
   184         #include "«@filename»_fsm.h"
   185         #include "pEp_internal.h"
   186 
   187         // state machine for «@name»
   188 
   189         «@name»_state fsm_«@name»(
   190                 PEP_SESSION session,
   191                 «@name»_state state,
   192                 «@name»_event event,
   193                 Identity partner,
   194                 void *extra,
   195                 time_t *timeout
   196             )
   197         {
   198             int cond_result;
   199             PEP_STATUS status = PEP_STATUS_OK;
   200 
   201             switch (state) {
   202             `` apply "state", 2
   203                 default:
   204                     return («@name»_state) invalid_state;
   205             }
   206 
   207             return state;
   208         }
   209 
   210         ||
   211     }
   212 
   213     template "state"
   214     ||
   215     case «@name»:
   216     {
   217         `` if "count(parm) > 1" error | # TODO composite state payload 
   218         `` apply "parm", 1 mode="stateParm"
   219         *timeout = «@timeout»;
   220         switch (event) {
   221         `` if "not(event[@name='Init'])" |>> case Init: break;
   222         `` apply "event", 2
   223             default:
   224                 return («../@name»_state) invalid_event;
   225         }
   226         break;
   227     }
   228     ||
   229 
   230     template "parm" mode="stateParm" 
   231     {
   232         | «name(*[1])» «name(*[2])» = («name(*[1])»)session->sync_state_payload;
   233     }
   234 
   235     template "event"
   236     ||
   237     case «@name»:
   238     {
   239     `` apply "action|transition|condition";
   240     `` if "name(*[position()=last()]) != 'transition'" |> break;
   241     }
   242     ||
   243 
   244     template "action" {
   245         indent(0);
   246         > status = «@name»(session, state, 
   247         choose {
   248             when "parm" > «name(parm/*)»
   249             otherwise > NULL
   250         }
   251         choose {
   252             when "parm[2]" > , extra /*«name(parm[2]/*)»*/
   253             otherwise > , NULL
   254         }
   255         > );\n
   256         | if (status == PEP_OUT_OF_MEMORY)
   257         |> return (int) invalid_out_of_memory;
   258         | if (status != PEP_STATUS_OK)
   259         |> return (int) invalid_action;
   260     }
   261 
   262     template "condition" {
   263         | cond_result = «@name»(session`apply "parm", 0`);
   264         | if (cond_result < 0)
   265         |> return cond_result;
   266         | if (cond_result) {
   267         apply "action|transition|condition";
   268         | }
   269     }
   270 
   271     template "parm" choose {
   272         when "count(*) = 1"
   273             > , «name(*)»
   274         otherwise
   275             > , «name(*[1])» «name(*[2])»
   276     }
   277 
   278     template "transition"{
   279         const "stateparm", "ancestor::state/child::parm";
   280         if "$stateparm" {
   281             | if(session->sync_state_payload){
   282             |     free_«yml:lcase(name($stateparm[1]/*))»((«name($stateparm[1]/*)»)session->sync_state_payload);
   283             |     session->sync_state_payload = NULL;
   284             | }
   285         }
   286         if "parm" {
   287             const "nextstatename", "@target";
   288             const "nextstateparm", "ancestor::fsm/child::state[@name = $nextstatename]/child::parm";
   289             | session->sync_state_payload = «yml:lcase(name($nextstateparm/*))»_dup(«name(parm/*)»);
   290         }
   291         | return «@target»;
   292     }
   293 }
   294