Browse the docs

silk.concurrency.fiber.

Fibers: handles to child tasks, with typed outcomes and one observer each.

When to use#

OperationUse it to
forkChildStart structured child work under the current Scheduler
joinWait for a child, and raise its failure in the current effect
awaitWait for a child, and get success, failure or cancellation as data
yieldNowLet other ready tasks run before the current task continues

Run the root program with silk.concurrency.local_scheduler, which provides the Scheduler service.

Details#

A Fiber has one completion observer. await and join consume the Fiber, so a Fiber has at most one waiter. A successful fork publishes the complete child before it returns the Fiber. The child body starts only when the scheduler drives it, not during the fork.

Completion, cancellation and yields notify an Execution with no allocation.

The other items (CompletionProducer, CompletionCanceller, PreparedFiber and the completion functions) are for scheduler providers, not for application code.

Errors#

  • forkChild fails with OutOfMemoryError or TaskIdExhaustedError.

  • join raises the typed failure of the child unchanged, or Cancelled.

Gotchas#

Dropping a Fiber stops the observation. It does not cancel the task. The scheduler cancels unfinished children when the root finishes.

Examples#

Fork two children and join them#

silk
import silk.mem.allocator { OutOfMemoryError }
import silk.core.effect { Effect }
import silk.concurrency.fiber { Cancelled, Fiber }
import silk.concurrency.local_scheduler { LocalScheduler, StalledError }
import silk.system.monotonic_clock { MonotonicClock }
import silk.system.monotonic_clock_native { OsMonotonicClock }
import silk.concurrency.scheduler { Scheduler, TaskIdExhaustedError }

effect fn square(value: i32) -> i32 {
  // Lets the other ready task run first.
  run Fiber.yieldNow()
  return value * value
}

effect fn root() -> i32
  ! OutOfMemoryError | TaskIdExhaustedError | Cancelled
  ? &mut Scheduler | &mut MonotonicClock {
  let three = run Fiber<i32, never>.forkChild(square(3))
  let four = run Fiber<i32, never>.forkChild(square(4))

  // join parks the root until the child finishes.
  let first = run three.join()
  let second = run four.join()
  if first + second != 25 {
    return 1
  }
  return 0
}

effect fn program() -> i32
  ! OutOfMemoryError | TaskIdExhaustedError | Cancelled | StalledError {
  let mut clock = OsMonotonicClock.make()
  let mut scheduler = LocalScheduler.make()
  return run scheduler.execute<i32, OutOfMemoryError | TaskIdExhaustedError | Cancelled>(root())
    |> Effect.provideMut<MonotonicClock>(&mut clock)
}

effect fn failed<E>(error: E) -> i32 {
  drop error
  return 1
}

pub fn main() -> i32 {
  return run Effect.catchAll(program(), failed)
}

Get a child failure as data#

silk
import silk.mem.allocator { OutOfMemoryError }
import silk.core.effect { Effect }
import silk.concurrency.fiber { Failure, Fiber, Outcome }
import silk.concurrency.local_scheduler { LocalScheduler, StalledError }
import silk.system.monotonic_clock { MonotonicClock }
import silk.system.monotonic_clock_native { OsMonotonicClock }
import silk.concurrency.scheduler { Scheduler, TaskIdExhaustedError }

struct Invalid {
  code: i32,
}

effect fn check(value: i32) -> i32
  ! Invalid {
  if value < 0 {
    fail Invalid { code: value }
  }
  return value
}

effect fn root() -> i32
  ! OutOfMemoryError | TaskIdExhaustedError
  ? &mut Scheduler | &mut MonotonicClock {
  let child = run Fiber<i32, Invalid>.forkChild(check(-7))

  // await gives the outcome as data. It does not raise the failure.
  let outcome = run child.await()
  let Outcome<i32, Invalid> { value } = move outcome
  return match move value {
    Failure<Invalid> { error } => {
      if error.code != -7 {
        return 1
      }
      return 0
    }
    _ => 1
  }
}

effect fn program() -> i32
  ! OutOfMemoryError | TaskIdExhaustedError | StalledError {
  let mut clock = OsMonotonicClock.make()
  let mut scheduler = LocalScheduler.make()
  return run scheduler.execute<i32, OutOfMemoryError | TaskIdExhaustedError>(root())
    |> Effect.provideMut<MonotonicClock>(&mut clock)
}

effect fn failed<E>(error: E) -> i32 {
  drop error
  return 1
}

pub fn main() -> i32 {
  return run Effect.catchAll(program(), failed)
}

Import as Fiber with import silk.concurrency.fiber { Fiber }.

Public declarations: 8.

CompletionProducer#

silk
pub struct CompletionProducer<A, E>

The producer endpoint for one Fiber result.

Details#

A Scheduler provider owns this endpoint until it calls completeSuccess or completeFailure. Both operations consume the endpoint and notify one parked observer.

CompletionCanceller#

silk
pub struct CompletionCanceller

The type-erased cancellation endpoint for one Fiber result.

Details#

A task store can retain this endpoint without storing the Fiber success or failure types. cancel consumes the endpoint and notifies one parked observer.

Fiber#

silk
pub struct Fiber<A, E>

An affine handle that observes one task result or cancellation.

When to use#

Get one from forkChild, which starts structured child work under the current Scheduler. Use join to wait for the child and raise its failure in the current effect, or await to get success, failure or cancellation as data. Run the root program with silk.concurrency.local_scheduler, which provides the Scheduler service.

Details#

A Fiber has one completion observer. await and join consume the handle, so a Fiber has at most one waiter. The child body starts only when the scheduler drives it, not during the fork.

Gotchas#

Dropping the handle stops the observation. It does not cancel its task. The scheduler cancels unfinished children when the root finishes.

Implementation Fiber<A, E>#

silk
impl<A, E> Fiber<A, E>

Associated function prepare#

silk
pub effect fn prepare() -> PreparedFiber<A, E>
  ! OutOfMemoryError
  ? &mut Allocator

Allocates the producer, canceller, and affine Fiber endpoints for one task.

When to use#

Use this provider operation before a Scheduler constructs and publishes a task.

Details#

No endpoint becomes visible until both Shared completion cells exist. Observation and notification allocate no further storage.

Associated function completeSuccess#

silk
pub fn completeSuccess(producer: CompletionProducer<A, E>, value: A) -> ()

Completes one Fiber with value and notifies its observer.

Details#

This operation stores value before it publishes readiness. It consumes the producer endpoint and allocates no storage.

Associated function completeFailure#

silk
pub fn completeFailure(producer: CompletionProducer<A, E>, error: E) -> ()

Completes one Fiber with error and notifies its observer.

Details#

This operation stores error before it publishes readiness. It consumes the producer endpoint and allocates no storage.

Associated function cancel#

silk
pub fn cancel(canceller: CompletionCanceller) -> ()

Publishes cancellation and notifies one parked Fiber observer.

Details#

This operation is independent of the Fiber success and failure types. It consumes the cancellation endpoint and allocates no storage.

Method await#

silk
pub effect<'env> fn await<'env>(self: Self) -> Outcome<A, E>

Consumes a Fiber and returns its success, typed failure, or cancellation as data.

Details#

A terminal Fiber returns immediately. A pending Fiber parks the current Execution and installs exactly one Wake. Observation and resumption allocate no storage.

Method join#

silk
pub effect<'env> fn join<'env>(self: Self) -> A
  ! E | Cancelled

Consumes a Fiber, returns its success value, and propagates failure or cancellation.

Details#

A typed task failure is raised unchanged. Cancellation raises Cancelled. A pending Fiber parks with the same allocation-free one-observer protocol as await.

Associated function yieldNow#

silk
pub effect fn yieldNow() -> ()

Relinquishes the current Execution once and immediately reports that it is ready.

When to use#

Use this operation to let tasks that are already ready run before the current task continues under a FIFO provider.

Details#

This operation consumes its Wake during registration. It needs no Scheduler service and allocates no storage. The provider selects the next activation order.

Outcome#

silk
pub struct Outcome<A, E>

The three possible terminal observations of a Fiber.

Field value#

silk
pub value: Success<A> | Failure<E> | Cancelled

The completed success, typed failure, or cancellation value.

Success#

silk
pub struct Success<A>

A successful Fiber outcome.

Field value#

silk
pub value: A

The value returned by the task.

Failure#

silk
pub struct Failure<E>

A typed failed Fiber outcome.

Field error#

silk
pub error: E

The error raised by the task.

Cancelled#

silk
pub struct Cancelled

A Fiber outcome that reports structured task cancellation.

Implementation Debug for Cancelled#

silk
impl Debug for Cancelled

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes Cancelled.

PreparedFiber#

silk
pub struct PreparedFiber<A, E>

The three endpoints created for one future Fiber result.

Details#

A Scheduler provider gives fiber to the caller, captures producer in the task, and stores canceller in its type-erased task entry.

Field producer#

silk
pub producer: CompletionProducer<A, E>

The endpoint that publishes a success or typed failure.

Field canceller#

silk
pub canceller: CompletionCanceller

The type-erased endpoint that publishes cancellation.

Field fiber#

silk
pub fiber: Fiber<A, E>

The affine result handle returned after task publication succeeds.

Implementation Fiber<A, E>#

silk
impl<A: 'static, E: 'static> Fiber<A, E>

Associated function forkChild#

silk
pub effect fn forkChild(
    child: once Effect<'static;
      A
      ! E
      ? &mut Scheduler | &mut MonotonicClock
    >
  ) -> Fiber<A, E>
  ! OutOfMemoryError | TaskIdExhaustedError
  ? &mut Scheduler

Prepares and atomically publishes one child task, then returns its affine Fiber.

When to use#

Use this operation to run closed work or work that uses the child Scheduler and MonotonicClock providers.

Details#

Preparation finishes its exclusive Scheduler dispatch before publication starts. Publication returns the Fiber only after the provider accepts the complete child. The provider records that acceptance and wakes the parent before it notifies the child's initial readiness. The child body starts only when the Scheduler later drives it.

Gotchas#

Allocation refusal or task-identity exhaustion raises the exact preparation failure: OutOfMemoryError or TaskIdExhaustedError. Publication refusal raises OutOfMemoryError, returns no Fiber, and does not activate the child. Dropping the Fiber does not cancel the child. The scheduler cancels unfinished children when the root finishes.

Category: Concurrency.

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.

View the source of this page