@@ -147,7 +147,10 @@ init([CaseId, Bytecode, Options]) ->
147147 result = undefined
148148 },
149149
150- % % Transition to running state (initializing complete)
150+ % % Transition to running state and trigger first execution via self-cast
151+ Self = self (),
152+ Self ! {start_execution },
153+ Timeout = maps :get (timeout , StateData # state_data .options , 5000 ),
151154 {ok , running , StateData , [{state_timeout , Timeout , overall_timeout }]}.
152155
153156% % @private Initializing state: setup complete, transition to running
@@ -164,21 +167,35 @@ initializing(cast, {set_trace, Level, Sink}, StateData) ->
164167 put (wf_trace_state , TraceState2 ),
165168 {next_state , initializing , StateData # state_data {trace_state = TraceState2 }};
166169
167- initializing (info , Msg , _StateData ) ->
170+ initializing (info , Msg , StateData ) ->
168171 log_unexpected_message (initializing , Msg ),
169- {keep_state_and_data };
172+ {keep_state , StateData };
170173
171174initializing (EventType , EventContent , StateData ) ->
172175 handle_common_event (EventType , EventContent , initializing , StateData ).
173176
174177% % @private Running state: execute bytecode quanta
178+ running (info , {start_execution }, StateData ) ->
179+ % % First execution triggered by init
180+ Result = execute_quantum (StateData ),
181+ % % execute_quantum may return a gen_statem tuple for state changes
182+ case element (1 , Result ) of
183+ next_state -> Result ;
184+ _ -> {next_state , running , Result }
185+ end ;
186+
175187running (cast , {signal , Signal }, StateData ) ->
176188 % % Handle external signal
177189 ExecState0 = StateData # state_data .exec_state ,
178190 Ctx0 = ExecState0 # exec_state .ctx ,
179191 Ctx1 = maps :put (last_signal , Signal , Ctx0 ),
180192 ExecState1 = ExecState0 # exec_state {ctx = Ctx1 },
181- {next_state , running , execute_quantum (StateData # state_data {exec_state = ExecState1 })};
193+ Result = execute_quantum (StateData # state_data {exec_state = ExecState1 }),
194+ % % execute_quantum may return a gen_statem tuple for state changes
195+ case element (1 , Result ) of
196+ next_state -> Result ;
197+ _ -> {next_state , running , Result }
198+ end ;
182199
183200running (cast , cancel , StateData ) ->
184201 % % Cancel entire case
@@ -211,25 +228,30 @@ running({call, From}, status, StateData) ->
211228 step_count => ExecState # exec_state .step_count ,
212229 status => ExecState # exec_state .status
213230 },
214- {keep_state_and_data , [{reply , From , {ok , running , StatusInfo }}]};
231+ {keep_state , StateData , [{reply , From , {ok , running , StatusInfo }}]};
215232
216233running (state_timeout , overall_timeout , StateData ) ->
217234 % % Overall case timeout
218235 notify_done (StateData # state_data .caller_pid , {error , timeout }),
219236 {next_state , cancelled , StateData };
220237
221- running (info , Msg , _StateData ) ->
238+ running (info , Msg , StateData ) ->
222239 % % Handle unexpected info messages
223240 log_unexpected_message (running , Msg ),
224- {keep_state_and_data };
241+ {keep_state , StateData };
225242
226243running (EventType , EventContent , StateData ) ->
227244 handle_common_event (EventType , EventContent , running , StateData ).
228245
229246% % @private Waiting for effect response
230247waiting_effect (cast , {effect_response , _Result }, StateData ) ->
231248 % % Resume execution with effect result
232- {next_state , running , execute_quantum (StateData )};
249+ Result = execute_quantum (StateData ),
250+ % % execute_quantum may return a gen_statem tuple for state changes
251+ case element (1 , Result ) of
252+ next_state -> Result ;
253+ _ -> {next_state , running , Result }
254+ end ;
233255
234256waiting_effect (cast , cancel , StateData ) ->
235257 % % Cancel while waiting for effect
@@ -241,9 +263,9 @@ waiting_effect(state_timeout, overall_timeout, StateData) ->
241263 notify_done (StateData # state_data .caller_pid , {error , timeout }),
242264 {next_state , cancelled , StateData };
243265
244- waiting_effect (info , Msg , _StateData ) ->
266+ waiting_effect (info , Msg , StateData ) ->
245267 log_unexpected_message (waiting_effect , Msg ),
246- {keep_state_and_data };
268+ {keep_state , StateData };
247269
248270waiting_effect (EventType , EventContent , StateData ) ->
249271 handle_common_event (EventType , EventContent , waiting_effect , StateData ).
@@ -255,7 +277,12 @@ waiting_signal(cast, {signal, Signal}, StateData) ->
255277 Ctx0 = ExecState0 # exec_state .ctx ,
256278 Ctx1 = maps :put (last_signal , Signal , Ctx0 ),
257279 ExecState1 = ExecState0 # exec_state {ctx = Ctx1 },
258- {next_state , running , execute_quantum (StateData # state_data {exec_state = ExecState1 })};
280+ Result = execute_quantum (StateData # state_data {exec_state = ExecState1 }),
281+ % % execute_quantum may return a gen_statem tuple for state changes
282+ case element (1 , Result ) of
283+ next_state -> Result ;
284+ _ -> {next_state , running , Result }
285+ end ;
259286
260287waiting_signal (cast , cancel , StateData ) ->
261288 % % Cancel while waiting for signal
@@ -267,9 +294,9 @@ waiting_signal(state_timeout, overall_timeout, StateData) ->
267294 notify_done (StateData # state_data .caller_pid , {error , timeout }),
268295 {next_state , cancelled , StateData };
269296
270- waiting_signal (info , Msg , _StateData ) ->
297+ waiting_signal (info , Msg , StateData ) ->
271298 log_unexpected_message (waiting_signal , Msg ),
272- {keep_state_and_data };
299+ {keep_state , StateData };
273300
274301waiting_signal (EventType , EventContent , StateData ) ->
275302 handle_common_event (EventType , EventContent , waiting_signal , StateData ).
@@ -279,27 +306,44 @@ cancelled(cast, cancel, StateData) ->
279306 % % Already cancelled
280307 {stop , normal , StateData };
281308
282- cancelled ({call , From }, status , _StateData ) ->
309+ cancelled ({call , From }, status , StateData ) ->
283310 % % Return cancelled status
284- StatusInfo = #{state => cancelled },
285- {keep_state_and_data , [{reply , From , {ok , cancelled , StatusInfo }}]};
311+ ExecState = StateData # state_data .exec_state ,
312+ StatusInfo = #{
313+ state => cancelled ,
314+ ip => ExecState # exec_state .ip ,
315+ step_count => ExecState # exec_state .step_count ,
316+ status => ExecState # exec_state .status
317+ },
318+ {keep_state , StateData , [{reply , From , {ok , cancelled , StatusInfo }}]};
286319
287- cancelled (info , Msg , _StateData ) ->
320+ cancelled (info , Msg , StateData ) ->
288321 log_unexpected_message (cancelled , Msg ),
289- {keep_state_and_data };
322+ {keep_state , StateData };
290323
291324cancelled (EventType , EventContent , StateData ) ->
292325 handle_common_event (EventType , EventContent , cancelled , StateData ).
293326
294327% % @private Done state: return result and terminate
328+ done (cast , cancel , StateData ) ->
329+ % % Already done, ignore cancel or transition to cancelled
330+ {next_state , cancelled , StateData };
331+
295332done ({call , From }, status , StateData ) ->
296- % % Return done status with result
297- StatusInfo = #{state => done , result => StateData # state_data .result },
298- {keep_state_and_data , [{reply , From , {ok , done , StatusInfo }}]};
333+ % % Return done status with result and execution info
334+ ExecState = StateData # state_data .exec_state ,
335+ StatusInfo = #{
336+ state => done ,
337+ ip => ExecState # exec_state .ip ,
338+ step_count => ExecState # exec_state .step_count ,
339+ status => ExecState # exec_state .status ,
340+ result => StateData # state_data .result
341+ },
342+ {keep_state , StateData , [{reply , From , {ok , done , StatusInfo }}]};
299343
300- done (info , Msg , _StateData ) ->
344+ done (info , Msg , StateData ) ->
301345 log_unexpected_message (done , Msg ),
302- {keep_state_and_data };
346+ {keep_state , StateData };
303347
304348done (EventType , EventContent , StateData ) ->
305349 handle_common_event (EventType , EventContent , done , StateData ).
@@ -313,10 +357,10 @@ handle_common_event({call, From}, status, CurrentState, StateData) ->
313357 step_count => ExecState # exec_state .step_count ,
314358 status => ExecState # exec_state .status
315359 },
316- {keep_state_and_data , [{reply , From , {ok , CurrentState , StatusInfo }}]};
360+ {keep_state , StateData , [{reply , From , {ok , CurrentState , StatusInfo }}]};
317361
318- handle_common_event (_EventType , _EventContent , _CurrentState , _StateData ) ->
319- {keep_state_and_data }.
362+ handle_common_event (_EventType , _EventContent , _CurrentState , StateData ) ->
363+ {keep_state , StateData }.
320364
321365% % @private gen_statem terminate
322366terminate (_Reason , _StateName , # state_data {case_id = CaseId }) ->
@@ -389,16 +433,12 @@ execute_quantum(StateData) ->
389433 end .
390434
391435% % @doc Transition to new state
392- enter_state (TargetState , StateData ) ->
393- % % Set state timeout if not done/cancelled
394- case TargetState of
395- done ->
396- StateData ;
397- cancelled ->
398- StateData ;
399- _Other ->
400- StateData
401- end .
436+ enter_state (done , StateData ) ->
437+ {next_state , done , StateData };
438+ enter_state (cancelled , StateData ) ->
439+ {next_state , cancelled , StateData };
440+ enter_state (_Other , StateData ) ->
441+ {keep_state , StateData }.
402442
403443% % @doc Notify caller of completion
404444-spec notify_done (pid () | undefined , term ()) -> ok .
0 commit comments