# module Fiber::ExecutionContext

##
Overview

An execution context creates and manages a dedicated pool of 1 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`.

Applications can create any number of execution contexts in parallel. These contexts are isolated but they can communicate with the usual synchronization primitives such as `Channel` or `Mutex`.

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.

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).

Once spawned, a fiber cannot move to another execution context. It always resumes 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). Communication with fibers in other contexts requires thread-safe primitives. 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. a GUI main loop, a game loop, or CPU heavy computation). The event-loop works normally (when the fiber sleeps, it pauses the thread). Communication with fibers in other contexts requires thread-safe primitives.

##
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)

EXPERIMENTAL

##
Direct including types

- Fiber::ExecutionContext::Isolated
- Fiber::ExecutionContext::Parallel

##
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

##
Constructors

- .current : ExecutionContext

  Returns the `ExecutionContext` the current fiber is running in.

- .default : ExecutionContext

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

##
Class Method Summary

- .current? : ExecutionContext | Nil
- .default_workers_count : Int32

  Returns the default maximum parallelism.

- .each(&) : Nil

  Iterates all execution contexts.

##
Instance Method Summary

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

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

##
Constructor Detail

def self.current : ExecutionContext #

Returns the `ExecutionContext` the current fiber is running in.

def self.default : ExecutionContext #

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`.

##
Class Method Detail

def self.current? : ExecutionContext | Nil #
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.

def self.each(&) : Nil #

Iterates all execution contexts.

##
Instance Method Detail

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

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

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