@@ -142,15 +142,25 @@ defmodule VirtualTimeGenStateMachine do
142142 # Priority: local options > global Process dictionary
143143 { final_clock , final_backend } = determine_time_config ( virtual_clock , real_time )
144144
145- # Set virtual clock in current process before starting
146- if final_clock do
147- Process . put ( :virtual_clock , final_clock )
148- end
145+ # Use a wrapper to inject virtual clock into spawned process
146+ init_fun = fn ->
147+ if final_clock do
148+ Process . put ( :virtual_clock , final_clock )
149+ end
149150
150- Process . put ( :time_backend , final_backend )
151+ Process . put ( :time_backend , final_backend )
151152
152- # Start with the original module using native gen_statem
153- :gen_statem . start_link ( module , init_arg , opts )
153+ # Call the module's init function
154+ case module . init ( init_arg ) do
155+ { :ok , state , data } -> { :ok , state , data }
156+ { :ok , state , data , timeout } -> { :ok , state , data , timeout }
157+ { :ok , state , data , { :continue , arg } } -> { :ok , state , data , { :continue , arg } }
158+ other -> other
159+ end
160+ end
161+
162+ # Start with a wrapper that injects the virtual clock
163+ :gen_statem . start_link ( VirtualTimeGenStateMachine.Wrapper , { init_fun , module } , opts )
154164 end
155165
156166 @ doc """
@@ -274,3 +284,125 @@ defmodule VirtualTimeGenStateMachine do
274284 # Note: Users should call set_virtual_clock/1 BEFORE starting the GenStateMachine
275285 # Child processes will inherit the Process dictionary containing the virtual clock
276286end
287+
288+ defmodule VirtualTimeGenStateMachine.Wrapper do
289+ @ moduledoc false
290+ @ behaviour :gen_statem
291+
292+ def callback_mode do
293+ # Get the original module's callback mode
294+ module = Process . get ( :__vtgsm_module__ )
295+
296+ if module && function_exported? ( module , :callback_mode , 0 ) do
297+ module . callback_mode ( )
298+ else
299+ :handle_event_function
300+ end
301+ end
302+
303+ def init ( { init_fun , module } ) do
304+ # Store the module reference
305+ Process . put ( :__vtgsm_module__ , module )
306+
307+ # Call the init function which injects the virtual clock
308+ case init_fun . ( ) do
309+ { :ok , state , data } -> { :ok , state , data }
310+ { :ok , state , data , timeout } -> { :ok , state , data , timeout }
311+ { :ok , state , data , { :continue , arg } } -> { :ok , state , data , { :continue , arg } }
312+ other -> other
313+ end
314+ end
315+
316+ def handle_event ( event_type , event_content , state , data ) do
317+ # Get the original module
318+ module = Process . get ( :__vtgsm_module__ )
319+
320+ if module do
321+ # Delegate to the original module's handle_event
322+ module . handle_event ( event_type , event_content , state , data )
323+ else
324+ { :keep_state_and_data , [ ] }
325+ end
326+ end
327+
328+ # Dynamic dispatch for state functions
329+ def closed ( event_type , event_content , data ) do
330+ module = Process . get ( :__vtgsm_module__ )
331+
332+ if module && function_exported? ( module , :closed , 3 ) do
333+ module . closed ( event_type , event_content , data )
334+ else
335+ { :keep_state_and_data , [ ] }
336+ end
337+ end
338+
339+ def open ( event_type , event_content , data ) do
340+ module = Process . get ( :__vtgsm_module__ )
341+
342+ if module && function_exported? ( module , :open , 3 ) do
343+ module . open ( event_type , event_content , data )
344+ else
345+ { :keep_state_and_data , [ ] }
346+ end
347+ end
348+
349+ def locked ( event_type , event_content , data ) do
350+ module = Process . get ( :__vtgsm_module__ )
351+
352+ if module && function_exported? ( module , :locked , 3 ) do
353+ module . locked ( event_type , event_content , data )
354+ else
355+ { :keep_state_and_data , [ ] }
356+ end
357+ end
358+
359+ def waiting ( event_type , event_content , data ) do
360+ module = Process . get ( :__vtgsm_module__ )
361+
362+ if module && function_exported? ( module , :waiting , 3 ) do
363+ module . waiting ( event_type , event_content , data )
364+ else
365+ { :keep_state_and_data , [ ] }
366+ end
367+ end
368+
369+ def working ( event_type , event_content , data ) do
370+ module = Process . get ( :__vtgsm_module__ )
371+
372+ if module && function_exported? ( module , :working , 3 ) do
373+ module . working ( event_type , event_content , data )
374+ else
375+ { :keep_state_and_data , [ ] }
376+ end
377+ end
378+
379+ def aborting ( event_type , event_content , data ) do
380+ module = Process . get ( :__vtgsm_module__ )
381+
382+ if module && function_exported? ( module , :aborting , 3 ) do
383+ module . aborting ( event_type , event_content , data )
384+ else
385+ { :keep_state_and_data , [ ] }
386+ end
387+ end
388+
389+ def terminate ( reason , state , data ) do
390+ module = Process . get ( :__vtgsm_module__ )
391+
392+ if module && function_exported? ( module , :terminate , 3 ) do
393+ module . terminate ( reason , state , data )
394+ else
395+ :ok
396+ end
397+ end
398+
399+ def code_change ( old_vsn , state , data , extra ) do
400+ module = Process . get ( :__vtgsm_module__ )
401+
402+ if module && function_exported? ( module , :code_change , 4 ) do
403+ module . code_change ( old_vsn , state , data , extra )
404+ else
405+ { :ok , state , data }
406+ end
407+ end
408+ end
0 commit comments