defmodule Skuld.Coroutine.ForeignSuspensions do
@moduledoc """
Aggregate of all pending foreign suspensions, returned to the caller
(e.g., the Hologram JS runtime) when the FiberPool scheduler exhausts
internal work.
The caller extracts the individual `ForeignSuspend` values from the
`:suspensions` list, resolves them via the foreign platform's event loop,
and resumes the computation by calling `Coroutine.call/2` with a map of
`%{suspend_id => resolved_value}`.
The `:resume` field is a closure created by the FiberPool scheduler that
captures the `FiberPoolState` and scheduler loop. It takes the resolved
map, wakes all resolved fibers at once by enqueuing them on the run queue,
and returns the next Coroutine state.
## Fields
- `id` — identifier for this aggregate
- `suspensions` — the list of `ForeignSuspend` values to resolve
- `env` — the computation environment
- `resume` — closure `(%{id => value}) -> Coroutine.t()` that batch-wakes fibers
"""
alias Skuld.Comp.Env
alias Skuld.Comp.ForeignSuspend
@type t :: %__MODULE__{
id: term(),
suspensions: [ForeignSuspend.t()],
env: Env.t(),
resume: (map() -> t())
}
defstruct [:id, :suspensions, :env, :resume]
defimpl Skuld.Comp.ISentinel do
def run(suspensions, env) do
{suspensions, env}
end
def run!(%Skuld.Coroutine.ForeignSuspensions{}) do
raise "Computation suspended on foreign resources — must be handled by a foreign scheduler"
end
def sentinel?(_), do: true
def suspend?(_), do: true
def error?(_), do: false
def serializable_payload(%Skuld.Coroutine.ForeignSuspensions{}) do
raise "ForeignSuspensions cannot be serialized — payloads are opaque foreign-platform handles"
end
end
end