Skip to content

Commit 50d4f9f

Browse files
committed
straighten out the implementation
1 parent e06ce36 commit 50d4f9f

File tree

3 files changed

+142
-10
lines changed

3 files changed

+142
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ and this project adheres to
4141
### Technical Details
4242

4343
- **Breaking Change**: None - fully backwards compatible
44-
- **Dependency Reduction**: Removed `gen_state_machine` dependency, now uses native Erlang `:gen_statem`
44+
- **Dependency Reduction**: Removed `gen_state_machine` dependency, now uses
45+
native Erlang `:gen_statem`
4546
- **Test Coverage**: All 314 tests passing, including complex virtual time
4647
simulations
4748
- **Performance**: Maintained existing performance characteristics while adding

lib/virtual_time_gen_state_machine.ex

Lines changed: 139 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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
276286
end
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

mix.exs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,7 @@ defmodule GenServerVirtualTime.MixProject do
8282

8383
# Mutation testing
8484
{:muzak, "~> 1.1", only: :test, runtime: false},
85-
{:exavier, "~> 0.3.0", only: :test, runtime: false},
86-
85+
{:exavier, "~> 0.3.0", only: :test, runtime: false}
8786
]
8887
end
8988

0 commit comments

Comments
 (0)