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