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.
-
ExecutionContext::Concurrent: Fully concurrent with limited parallelism.Fibers run concurrently to each other, never in parallel (only one fiber at a time). They can use simpler and faster synchronization primitives internally (no atomics, limited thread safety), however communication with fibers in other contexts must be safe (e.g.
Channel,Sync`, ...). A blocking fiber blocks the entire thread and all other fibers in the context. -
ExecutionContext::Parallel: Fully concurrent, fully parallel.Fibers running in this context can be resumed by multiple system threads in this context. They run concurrently and in parallel to each other (multiple fibers at a time), in addition to running in parallel to any fibers in other contexts. Schedulers steal work from each other. The parallelism can grow and shrink dynamically.
-
ExecutionContext::Isolated: Single fiber in a single system thread without concurrency.This is useful for tasks that can block thread execution for a long time (e.g. CPU heavy computation) or must be reactive (e.g. a GUI or game loop). The event-loop works normally and so does communication and synchronization with fibers in other contexts (
Channel,WaitGroup,Sync, ...). When the fiber needs to wait, it pauses the thread.
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.crfiber/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
-
.current : ExecutionContext
Returns the
ExecutionContextthe current fiber is running in.
Class Method Summary
- .current? : ExecutionContext | Nil
-
.default : ExecutionContext::Parallel
Returns the default
ExecutionContextfor the process, automatically started when the program started. -
.default_workers_count : Int32
Returns the default maximum parallelism.
-
.each(&) : Nil
Iterates all execution contexts.
-
.thread_keepalive : Time::Span
How long a parked thread will be kept waiting in the thread pool.
-
.thread_keepalive=(thread_keepalive : Time::Span)
How long a parked thread will be kept waiting in the thread pool.
Instance Method Summary
-
#spawn(*, name : String | Nil = nil, &block : -> ) : Fiber
Creates a new fiber then enqueues it to the execution context.
Constructor Detail
Class Method Detail
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.
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.
How long a parked thread will be kept waiting in the thread pool. Defaults to 5 minutes.
How long a parked thread will be kept waiting in the thread pool. Defaults to 5 minutes.
Instance Method Detail
Creates a new fiber then enqueues it to the execution context.
May be called from any ExecutionContext (i.e. must be thread-safe).