module Fiber::ExecutionContext

Overview

An execution context creates and manages a dedicated pool of one or more schedulers where fibers will be running in. Each context manages the rules to run, suspend and swap fibers internally.

EXPERIMENTAL Execution contexts are an experimental feature, implementing RFC 2. It's opt-in and requires the compiler flags -Dpreview_mt -Dexecution_context.

An execution context groups fibers together. Instead of associating a fiber to a specific system thread, we associate a fiber to an execution context, abstracting which system thread(s) the fibers will run on.

Applications can create any number of execution contexts in parallel. Fibers running in any context can communicate and synchronize with any other fiber running in any context through the usual synchronization primitives such as Channel, WaitGroup or Sync.

When spawning a fiber with ::spawn, it spawns into the execution context of the current fiber, so child fibers execute in the same context as their parent, unless told otherwise (see ExecutionContext#spawn).

Fibers are scoped to the execution context they are spawned into. Once spawned, a fiber cannot move to another execution context, and is always resumed in the same execution context.

Context types

The standard library provides a number of execution context implementations for common use cases.

Again, any number of execution contexts can be created (as far as the computer can physically allow). An advantage of starting execution contexts is that it creates execution boundaries, the OS thread scheduler can for example preempt a system thread, allowing fibers in other system threads to run.

The default execution context

The Crystal runtime starts a default execution context exposed as Fiber::ExecutionContext.default. This is where the main fiber is running.

Its parallelism is set to 1 for backwards compatibility reasons; Crystal used to be single-threaded and concurrent only. You can increase the parallelism at any time using Parallel#resize, for example:

count = Fiber::ExecutionContext.default_workers_count
Fiber::ExecutionContext.default.resize(count)

Relationship with system threads

Execution contexts control when and how fibers run, and on which system thread they execute. The term parallelism is the maximum number of fibers that can run in parallel (maximum number of schedulers) but there can be less or more system threads running in practice, for example when a fiber is blocked on a syscall.

There are no guarantees on how a fiber will run on system threads. A fiber can start in thread A, then be resumed and terminated on thread A, B or C. This is true for both the Parallel and Concurrent contexts.

Notable exception: Isolated guarantees that its fiber will always run on the same system thread. During its lifetime, the fiber owns the thread, but only for the fiber's lifetime.

Threads are kept in a thread pool: threads can be started, attached and detached from any context at any time. Threads can be detached from a context and reattached to the same execution context or to another one (Concurrent, Parallel or Isolated).

EXPERIMENTAL

Direct including types

Defined in:

fiber/execution_context.cr
fiber/execution_context/concurrent.cr
fiber/execution_context/global_queue.cr
fiber/execution_context/isolated.cr
fiber/execution_context/monitor.cr
fiber/execution_context/parallel.cr
fiber/execution_context/parallel/scheduler.cr
fiber/execution_context/runnables.cr
fiber/execution_context/scheduler.cr
fiber/execution_context/thread_pool.cr

Constructors

Class Method Summary

Instance Method Summary

Constructor Detail

def self.current : ExecutionContext #

Returns the ExecutionContext the current fiber is running in.


[View source]

Class Method Detail

def self.current? : ExecutionContext | Nil #

[View source]
def self.default : ExecutionContext::Parallel #

Returns the default ExecutionContext for the process, automatically started when the program started.

The default execution context is currently Parallel but only starts with parallelism set to 1. The parallelism can be changed using Parallel#resize.


[View source]
def self.default_workers_count : Int32 #

Returns the default maximum parallelism. Can be used to resize the default parallel execution context for example.

Respects the CRYSTAL_WORKERS environment variable if present and valid, and otherwise defaults to the number of logical CPUs available to the process or on the computer.


[View source]
def self.each(&) : Nil #

Iterates all execution contexts.


[View source]
def self.thread_keepalive : Time::Span #

How long a parked thread will be kept waiting in the thread pool. Defaults to 5 minutes.


[View source]
def self.thread_keepalive=(thread_keepalive : Time::Span) #

How long a parked thread will be kept waiting in the thread pool. Defaults to 5 minutes.


[View source]

Instance Method Detail

def spawn(*, name : String | Nil = nil, &block : -> ) : Fiber #

Creates a new fiber then enqueues it to the execution context.

May be called from any ExecutionContext (i.e. must be thread-safe).


[View source]