Skuld.Coroutine (skuld v0.28.0)

View Source

Cooperative fiber primitive for the FiberPool scheduler.

A Fiber wraps a computation that can be run incrementally, suspended when it yields, and resumed with a value. This is the fundamental building block for cooperative concurrency in Skuld.

Sum Type

A fiber is always exactly one of these states — no status atom, no nil fields:

  • %Coroutine.Pending{id, computation, env} — ready to start
  • %Coroutine.InternalSuspended{id, k, suspend, env} — suspended, needs scheduler
  • %Coroutine.ExternalSuspended{id, k, env} — suspended, external callback
  • %Coroutine.Completed{id, result, env} — finished successfully
  • %Coroutine.Errored{id, error, env} — finished with error
  • %Coroutine.Cancelled{id, reason, env} — cancelled before completion

Two entry points: call vs run

Following Comp.call/Comp.run convention:

  • call/1,2 — raw step, no ISentinel.run. Returns typed states directly. For use inside the FiberPool scheduler, which applies ISentinel.run at its own boundary.
  • run/1,2 — step + ISentinel.run. Applies transform_suspend and leave_scope before returning typed states. For standalone use (AsyncCoroutine, SerializableCoroutine, etc.).

Lifecycle

  1. Create with new/2 — returns %Pending{}
  2. Step with call/1 or run/1 — returns %Completed{}, %InternalSuspended{}, %ExternalSuspended{}, or %Errored{}
  3. Resume with call/2 or run/2 — match on %InternalSuspended{} or %ExternalSuspended{}, pass the resume value
  4. Cancel with cancel/2 — invokes leave_scope cleanup, returns %Cancelled{}

Example (standalone, with run)

fiber
|> Coroutine.new(comp, env)
|> Coroutine.run()
|> Coroutine.run(result1)
|> Coroutine.run(result2)
|> Coroutine.cancel()

Summary

Types

t()

A fiber in any state.

Functions

Step a fiber without applying ISentinel.run.

Cancel a fiber, invoking leave_scope cleanup for suspended fibers.

Create a new fiber from a computation.

Step a pending fiber, or resume a suspended fiber with a value.

Check if a fiber is in a terminal state (completed, cancelled, or errored).

Types

t()

A fiber in any state.

Callers pattern-match on the struct name to determine state: %Pending{}, %InternalSuspended{}, %ExternalSuspended{}, %Completed{}, %Errored{}, %Cancelled{}.

Functions

call(fiber)

Step a fiber without applying ISentinel.run.

Like run/1,2 but produces raw typed states without transform_suspend or leave_scope. For use inside the FiberPool scheduler, which applies ISentinel.run at its own boundary.

Examples

fiber = Coroutine.new(my_comp, env)
fiber = Coroutine.call(fiber)
case fiber do
  %Coroutine.InternalSuspended{} -> :needs_scheduler
  %Coroutine.ExternalSuspended{k: k, env: env} -> handle_external(k, env)
end

call(fiber, value)

cancel(fiber, reason \\ :cancelled)

@spec cancel(t(), term()) :: t()

Cancel a fiber, invoking leave_scope cleanup for suspended fibers.

For suspended fibers, creates a %Cancelled{} sentinel and runs it through the leave_scope chain, giving scoped effects an opportunity to clean up resources.

For %Pending{} fibers, no scopes have been entered yet so no cleanup is needed.

For already-terminal fibers (%Completed{}, %Cancelled{}, %Errored{}), cancel is a no-op — the fiber is returned unchanged.

Parameters

  • fiber - The fiber to cancel
  • reason - The cancellation reason (default: :cancelled)

Example

fiber = Coroutine.cancel(fiber, :timeout)
assert match?(%Coroutine.Cancelled{}, fiber)

new(comp, env)

Create a new fiber from a computation.

The fiber starts as %Pending{} with the computation and env stored, ready to be run with run/1 or call/1.

Parameters

  • comp - The computation to run as a fiber
  • env - The environment to run in (typically inherited from parent)

Example

fiber = Coroutine.new(my_comp, env)
assert match?(%Coroutine.Pending{}, fiber)

run(fiber)

Step a pending fiber, or resume a suspended fiber with a value.

Clauses dispatch on the fiber's sum-type state:

  • %Pending{} — starts the computation, returns the fiber in its new state
  • %InternalSuspended{} — resumes with value, returns the fiber in its new state
  • %ExternalSuspended{} — resumes with value, returns the fiber in its new state

Returns one of: %Completed{}, %InternalSuspended{}, %ExternalSuspended{}, or %Errored{}. Raises for terminal or invalid states.

Examples

fiber = Coroutine.new(my_comp, env)
fiber = Coroutine.run(fiber)
# ... later, when we have a result ...
fiber = Coroutine.run(fiber, result)
case fiber do
  %Coroutine.Completed{result: result} -> result
  %Coroutine.InternalSuspended{} -> :still_waiting
  %Coroutine.Errored{error: error} -> {:error, error}
end

run(fiber, value)

terminal?(arg1)

@spec terminal?(t()) :: boolean()

Check if a fiber is in a terminal state (completed, cancelled, or errored).