# `VirtualTimeGenServer`
[🔗](https://github.com/d-led/gen_server_virtual_time/blob/v1.1.0/packages/gen_server_virtual_time/lib/virtual_time_gen_server.ex#L163)

A behavior module for GenServers with virtual time support.

This module wraps GenServer and provides a `send_after/3` function that can
work with either real time (production) or virtual time (testing).

## Example

    defmodule MyTimedServer do
      use VirtualTimeGenServer

      def start_link(opts) do
        VirtualTimeGenServer.start_link(__MODULE__, :ok, opts)
      end

      @impl true
      def init(:ok) do
        # Schedule a tick every 1000ms
        schedule_tick()
        {:ok, %{count: 0}}
      end

      @impl true
      def handle_info(:tick, state) do
        new_count = state.count + 1
        schedule_tick()
        {:noreply, %{state | count: new_count}}
      end

      defp schedule_tick do
        VirtualTimeGenServer.send_after(self(), :tick, 1000)
      end
    end

## Testing with Virtual Time - Global Clock (Coordinated Simulation)

    test "server ticks correctly" do
      {:ok, clock} = VirtualClock.start_link()
      VirtualTimeGenServer.set_virtual_clock(clock)

      {:ok, server} = MyTimedServer.start_link([])

      # Advance virtual time by 5 seconds
      VirtualClock.advance(clock, 5000)

      # Server will have ticked 5 times
      assert get_count(server) == 5
    end

## Testing with Virtual Time - Local Clock (Isolated Simulation)

    test "isolated simulation with local clock" do
      {:ok, clock} = VirtualClock.start_link()

      # Pass clock directly to this specific server
      {:ok, server} = VirtualTimeGenServer.start_link(MyTimedServer, :ok, virtual_clock: clock)

      # Advance only this server's timeline
      VirtualClock.advance(clock, 5000)

      assert get_count(server) == 5
    end

## Clock Configuration Options

The virtual clock can be configured in three ways (in priority order):

1. **Local Clock Injection** - Pass `virtual_clock: clock_pid` to `start_link/3`:
   - Highest priority, overrides global settings
   - Useful for isolated simulations or testing components independently
   - Each server can have its own timeline

2. **Global Clock** - Use `set_virtual_clock/1`:
   - Inherited by all child processes
   - Essential for coordinated actor systems where timing relationships matter
   - All actors share the same timeline

3. **Real Time** - Pass `real_time: true` or use `use_real_time/0`:
   - Default behavior, uses `Process.send_after/3`
   - For production or integration tests with external systems

See the "Development Documentation" for a detailed explanation of when to use
global vs local clocks.

## Observing a server

Counting messages, tracing them or drawing diagrams of them is a different
concern from controlling time, so this library does none of it. It offers a
port instead: a module implementing `VirtualTimeGenServer.Instrumentation`
sees every message a server receives and sends, and `snapshot/1` asks it what
it collected.

# `__using__`
*macro* 

Turns the module into a virtual-time server.

The options are handed to `GenServer`, so `use VirtualTimeGenServer, restart:
:transient` means what `use GenServer, restart: :transient` means — which is
also what makes `use GenServer` a mechanical rewrite.

# `call`

Makes a synchronous call to a server.

If the calling process is instrumented (see `instrument/1`), the message is
reported to the observer before it leaves. Otherwise this is a direct
passthrough to `GenServer.call/3`.

# `cancel_timer`

Cancels a timer created with send_after/3.
Uses the appropriate backend based on the current process configuration.

# `cast`

Sends an asynchronous request to a server.

If the calling process is instrumented (see `instrument/1`), the message is
reported to the observer before it leaves. Otherwise this is a direct
passthrough to `GenServer.cast/2`.

# `get_time_backend`

Gets the current time backend.

# `instrument`

```elixir
@spec instrument(VirtualTimeGenServer.Instrumentation.t()) :: :ok
```

Instruments the calling process with `instrumentation`.

Every message the calling process sends through `call/3` or `cast/2` is then
reported to the observer's `on_send/2` callback. Starting a server with the
`:instrumentation` option instruments that server in the same way, so a server
reports both what it sends and what it receives.

`instrumentation` is a module implementing `VirtualTimeGenServer.Instrumentation`,
or `{module, context}` when the callbacks need context. Pass `nil` to stop
observing.

## Example

    defmodule MyServer do
      use VirtualTimeGenServer
      # ...
    end

    {:ok, clock} = VirtualClock.start_link()
    VirtualTimeGenServer.instrument({MessageCounter, %{label: :test}})

    {:ok, server} =
      VirtualTimeGenServer.start_link(MyServer, :ok, virtual_clock: clock)

    VirtualTimeGenServer.call(server, :ping)

# `send_after`

Sends a message to a process after a delay in milliseconds.
Uses the appropriate backend based on the current process configuration.

# `send_immediately`

Sends a message immediately in virtual time.

With virtual time, this schedules the message for the current virtual time,
ensuring it gets processed in the next event cycle.
With real time, this sends the message immediately.

This is useful for triggering immediate responses or state changes
within the virtual time simulation.

## Examples

    # Send immediate message to self
    VirtualTimeGenServer.send_immediately(self(), :process_now)

    # Send immediate message to another process
    VirtualTimeGenServer.send_immediately(other_pid, {:urgent, data})

# `set_virtual_clock`

Sets the virtual clock for the current process.
All child processes will inherit this setting.

## Example

    iex> {:ok, clock} = VirtualClock.start_link()
    iex> VirtualTimeGenServer.set_virtual_clock(clock)
    VirtualTimeBackend
    iex> VirtualTimeGenServer.get_time_backend()
    VirtualTimeBackend

# `set_virtual_clock`

Sets the virtual clock for the current process without emitting warnings.

Use this when you intentionally want global virtual clock behavior and understand
the implications. The explanation message should describe why global clock is needed.

## Example

    iex> {:ok, clock} = VirtualClock.start_link()
    iex> VirtualTimeGenServer.set_virtual_clock(clock, :i_know_what_i_am_doing, "coordinated simulation")
    VirtualTimeBackend

# `sleep`

Sleeps for the specified duration in milliseconds.

With virtual time, this advances the actor's position in the timeline without
consuming real time. With real time, this blocks using Process.sleep/1.

This is useful for simulating work or processing delays within actors.

## Examples

    # In a GenServer callback - simulate 100ms of processing time
    def handle_call(:compute, _from, state) do
      VirtualTimeGenServer.sleep(100)  # Simulated work
      {:reply, :done, state}
    end

    # With virtual time: returns instantly but advances virtual clock
    # With real time: blocks for 100ms

# `snapshot`

```elixir
@spec snapshot(GenServer.server()) :: {:ok, term()} | {:error, :not_instrumented}
```

Asks an instrumented server what its observer collected.

The observer's `snapshot/1` runs in the server process, so it can read state
the observer keeps there. The result is the observer's own term, handed back
unchanged.

## Example

    {:ok, clock} = VirtualClock.start_link()

    {:ok, server} =
      VirtualTimeGenServer.start_link(MyServer, :ok,
        virtual_clock: clock,
        instrumentation: MessageCounter
      )

    VirtualTimeGenServer.call(server, :ping)
    {:ok, %{received: 1}} = VirtualTimeGenServer.snapshot(server)

# `start`

# `start_link`

# `stop`

# `use_real_time`

Uses real time (default behavior).

# `use_real_time`

Uses real time without emitting warnings.

Use this when you intentionally want global real time behavior and understand
the implications. The explanation message should describe why global real time is needed.

## Example

    iex> VirtualTimeGenServer.use_real_time(:i_know_what_i_am_doing, "production mode")
    RealTimeBackend

---

*Consult [api-reference.md](api-reference.md) for complete listing*
