Process Scheduler
Relevant source files
The following files were used as context for generating this wiki page:
The Process Scheduler is a core component of AtomVM responsible for managing the execution, lifecycle, and pre-emptive interruption of lightweight Erlang processes. It ensures that multiple processes can run concurrently on a single or multiple CPU cores, simulating the massively concurrent actor model of the BEAM virtual machine.
The scheduler handles the transition of processes between different states (running, ready, waiting) and manages the execution loop where BEAM opcodes are interpreted or executed natively. In SMP (Symmetric Multi-Processing) builds, the scheduler is capable of running multiple scheduler threads—typically one per core—to leverage hardware parallelism.
Sources: doc/src/atomvm-internals.md:37-45, doc/src/atomvm-internals.md:329-335
Architecture and Process Management
The scheduler operates on processes encapsulated in Context structures. These structures are organized into several lists within the GlobalContext, which represents the root of the VM's runtime state.
Process States and Lists
The scheduler manages processes using four primary linked-list structures in the GlobalContext:
| List Name | Description |
|---|---|
processes_table | Contains all processes currently existing in the system. |
running_processes | Processes currently being executed by a scheduler thread. |
ready_processes | Processes that are eligible to run and waiting for a scheduler thread. |
waiting_processes | Processes suspended while waiting for a message, timeout, or event. |
Sources: doc/src/atomvm-internals.md:105-115
Process Lifecycle Flow
The following diagram illustrates how the scheduler moves a process through different states during its lifecycle.
The scheduler picks a process from the ready_processes list. Once executed, a process yields if it has consumed its reduction budget or if it enters a waiting state for a message.
Sources: doc/src/atomvm-internals.md:116-125, doc/src/atomvm-internals.md:337-340
Execution Loop and Yielding
The scheduler executes a process until it yields. Yielding occurs under two primary conditions:
- Reduction Exhaustion: Erlang processes are assigned a "reduction" budget (simulating CPU cycles). Once these are elapsed, the process yields to allow other processes to run.
- Waiting for Messages: If a process attempts to receive a message and the mailbox is empty, it yields and is moved to the
waiting_processeslist.
Native processes (ports) behave slightly differently; they yield when their handler function returns after consuming available messages.
Sources: doc/src/atomvm-internals.md:337-340
Event Polling
When a scheduler thread has no ready processes to execute, it enters an event polling state using sys_poll_events.
If one thread is already polling the system for events, other idle scheduler threads will wait. If a process is made ready by an external event, sys_signal is used to interrupt the wait in sys_poll_events.
Sources: doc/src/atomvm-internals.md:342-353
SMP and Synchronization
In SMP builds, AtomVM starts scheduler threads on demand, typically one per core. All threads are considered equal, with no "main" thread except during system shutdown.
Sources: doc/src/atomvm-internals.md:329-335
Synchronization Mechanisms
To manage shared data structures like the ready_processes list and GlobalContext across multiple threads, the scheduler utilizes various synchronization primitives:
- Mutexes and Condition Variables: Provided by the host SDK (e.g., FreeRTOS or POSIX).
- Spinlocks: Used for short-duration locks, implemented on top of Atomics or critical sections.
- RW Locks: Used for structures like ETS tables where multiple readers are permitted but writers must be exclusive.
- Atomics: Used for lock-free mailbox operations via CAS (Compare-And-Swap).
Sources: doc/src/atomvm-internals.md:363-372
Mailboxes and Signals
The scheduler facilitates inter-process communication through mailboxes and signals. Every process has a mailbox consisting of two parts to allow for lock-free interaction.
Mailbox Architecture
Messages are first enqueued into an outer mailbox using atomic operations. The recipient process, when scheduled, moves messages from the outer mailbox to the inner mailbox for processing.
Sources: doc/src/atomvm-internals.md:394-403
Signal Handling
Signals are high-priority messages enqueued in the outer mailbox used for management tasks like:
- Garbage collection requests on another process.
- Responding to
process_info/1,2requests. - Handling process termination or traps.
When a process needs information from another (e.g., process_info), it may "Trap," meaning it is blocked until the target process is scheduled and sends the required information back via a signal.
Sources: doc/src/atomvm-internals.md:405-419
WebAssembly and Platform Specifics
On the WebAssembly (Wasm) port, the scheduler behaves differently due to the browser's single-threaded nature for DOM access. The main function runs in a Web Worker (via proxy-to-pthread), allowing the scheduler loop to run without blocking the browser's UI thread.
If an Erlang process calls emscripten:run_script/2 with the [main_thread] flag, the scheduler dispatches the request to the browser's main thread. The Erlang process is suspended (waiting), but the scheduler thread remains free to execute other ready Erlang processes.
Sources: doc/src/atomvm-internals.md:571-582
Summary
The AtomVM Process Scheduler provides a robust, pre-emptive multitasking environment tailored for memory-constrained microcontrollers. By managing process states, handling reduction-based yielding, and utilizing efficient lock-free mailbox structures, it enables complex concurrent Erlang/Elixir applications to run on devices with as little as 128KB of RAM.
Sources: doc/src/welcome-to-atomvm.md:73-80, doc/src/atomvm-internals.md:88-95