How the Parallel Module Works for Multi-Threaded Computations in Nelson

Nelson's parallel module implements multi-threaded computations using a C++17 thread pool that executes functions asynchronously through Future objects, enabling MATLAB-compatible parfeval syntax for background task execution.

The parallel module in Nelson provides asynchronous, multi-threaded evaluation of functions for the nelson-lang/nelson open-source interpreter. By leveraging a custom C++ thread pool and Future-based task management, this module enables users to offload computations to background threads while maintaining a MATLAB-compatible API.

Core Architecture of the Parallel Module

Thread-Pool Implementation with BS::pause_thread_pool

At the foundation lies a header-only C++ thread pool implementation located in [modules/parallel/src/include/BS_thread_pool.hpp](https://github.com/nelson-lang/nelson/blob/master/modules/parallel/src/include/BS_thread_pool.hpp). This file wraps Barak Shoshany's modern C++17/20 thread pool, providing the BS::pause_thread_pool class that Nelson uses for worker thread management.

The pool supports pausing and unpausing operations, which the Nelson runtime employs to temporarily halt background work while the interpreter executes critical sections or undergoes state changes.

Singleton Background Pool Management

Nelson maintains a single global thread pool through the BackgroundPoolObject singleton, defined in [modules/parallel/src/include/BackgroundPoolObject.hpp](https://github.com/nelson-lang/nelson/blob/master/modules/parallel/src/include/BackgroundPoolObject.hpp) and implemented in [modules/parallel/src/cpp/BackgroundPoolObject.cpp](https://github.com/nelson-lang/nelson/blob/master/modules/parallel/src/cpp/BackgroundPoolObject.cpp).

Upon construction, this singleton creates a pause_thread_pool whose size is determined by NelsonConfiguration::getMaxNumCompThreads(), allowing users to configure thread count through Nelson's standard configuration mechanisms.

How Asynchronous Task Execution Works

Future Objects and State Management

Every asynchronous computation in Nelson is represented by a Future object. The base class FutureObject in [modules/parallel/src/include/FutureObject.hpp](https://github.com/nelson-lang/nelson/blob/master/modules/parallel/src/include/FutureObject.hpp) stores:

  • The function name and arguments
  • The result vector
  • Execution state (QUEUED, RUNNING, FINISHED, CANCELLED)
  • Time stamps for creation and completion

For function evaluation specifically, Nelson uses FevalFutureObject ([modules/parallel/src/include/FevalFutureObject.hpp](https://github.com/nelson-lang/nelson/blob/master/modules/parallel/src/include/FevalFutureObject.hpp) and [modules/parallel/src/cpp/FevalFutureObject.cpp](https://github.com/nelson-lang/nelson/blob/master/modules/parallel/src/cpp/FevalFutureObject.cpp)), which knows how to execute a Nelson FunctionDef in the background.

Task Submission via feval

The core mechanism for scheduling work resides in BackgroundPoolObject::feval. When you call parfeval from an .m script, it ultimately invokes this C++ method, which submits a lambda to the thread pool:

// Excerpt from BackgroundPoolObject::feval
auto future = threadPool->submit_task(
    [retFuture, fptr, nLhs, argIn]() {
        retFuture->evaluateFunction(fptr, nLhs, argIn, true);
    });

The submit_task method belongs to BS::pause_thread_pool and enqueues the lambda for execution on a worker thread. The lambda captures a newly allocated FevalFutureObject along with the function pointer and arguments, executing evaluateFunction in the background.

Global Future Queue Tracking

Every created future registers with the FevalQueueObject ([modules/parallel/src/include/FevalQueueObject.hpp](https://github.com/nelson-lang/nelson/blob/master/modules/parallel/src/include/FevalQueueObject.hpp) and [modules/parallel/src/cpp/FevalQueueObject.cpp](https://github.com/nelson-lang/nelson/blob/master/modules/parallel/src/cpp/FevalQueueObject.cpp)). This global queue allows the interpreter to:

  • Query outstanding futures
  • Cancel pending or running tasks
  • Retrieve results through fetchNext or fetchOutputs

MATLAB-Compatible API: parfeval and Background Pool

The parallel module exposes a MATLAB-compatible interface through built-in functions defined in modules/parallel/builtin/include/*Builtin.hpp (such as parfevalBuiltin.hpp). The parfeval function connects .m scripts to the C++ core:

f = parfeval(pool, @myFunction, nLhs, arg1, arg2, ...);

Here, pool is a handle to the singleton background pool obtained via backgroundPool. The call returns a Future handle that supports:

  • fetchOutputs(f) – Retrieve results (blocks until finished)
  • wait(f) – Block until completion without retrieving results
  • cancel(f) – Mark the future as cancelled
  • f.State – Query current execution state

Practical Examples

Simple Asynchronous Call

This example demonstrates scheduling a function that sleeps for 2 seconds while the main thread continues working:

% Create a reference to the global background pool
pool = backgroundPool;    % built-in handle

% Schedule a function that sleeps 2 seconds and returns the value 42
f = parfeval(pool, @() pause(2); 0, 42);   % 0 lhs because we just want the result

% Do other work while the background thread sleeps
disp('Main thread is busy...');
tic;
while ~strcmp(f.State, 'finished')
    pause(0.5);               % poll every 0.5 s
    fprintf('...still waiting (%.1f s elapsed)\n', toc);
end

% Retrieve the result (throws if the future failed)
result = fetchOutputs(f);
fprintf('Result from background thread: %g\n', result);

Relevant sources: parfeval → BackgroundPoolObject::feval (see the submit_task lambda), FevalFutureObject stores the result, fetchOutputs reads it.

Parallel Array of Futures

Run multiple independent computations simultaneously and collect results:

% Run three independent calls in parallel
pool = backgroundPool;
f(1) = parfeval(pool, @sin, 1, pi/6);
f(2) = parfeval(pool, @cos, 1, pi/6);
f(3) = parfeval(pool, @tan, 1, pi/6);

% Wait for all to finish (equivalent to MATLAB's wait for all)
wait(f);          % blocks until every future is FINISHED

% Collect results
s = fetchOutputs(f(1));
c = fetchOutputs(f(2));
t = fetchOutputs(f(3));
fprintf('sin=%.3f, cos=%.3f, tan=%.3f\n', s, c, t);

Relevant sources: FevalQueueObject::add (queues each future), wait implementation in Future_waitBuiltin.hpp that repeatedly checks future->state.

Canceling Long-Running Computations

Terminate background tasks that are no longer needed:

pool = backgroundPool;
% Start a heavy loop that never ends unless cancelled
f = parfeval(pool, @() while true; pause(0.1); end, 0);

% Let it run for a second, then cancel
pause(1);
cancel(f);                 % marks the future as CANCELLED
% Fetch will now raise an exception
try
    fetchOutputs(f);
catch e
    disp(['Computation cancelled: ' e.message]);
end

Relevant sources: FutureObject::cancel (sets state = CANCELLED), FevalFutureObject::evaluateFunction checks changeState flag and aborts if cancelled.

Key Source Files

The parallel module spans several critical files in the nelson-lang/nelson repository:

Summary

  • Nelson's parallel module utilizes a header-only C++17 thread pool (BS::pause_thread_pool) defined in BS_thread_pool.hpp to manage worker threads.
  • A singleton BackgroundPoolObject maintains the global thread pool instance, sized according to NelsonConfiguration::getMaxNumCompThreads().
  • Future objects (FevalFutureObject) encapsulate asynchronous computations, tracking states from QUEUED through FINISHED or CANCELLED.
  • Tasks are submitted via lambda functions to the thread pool through BackgroundPoolObject::feval, which captures the future and function arguments for background execution.
  • The FevalQueueObject maintains a global registry of all futures, supporting MATLAB-compatible operations like wait, cancel, and fetchOutputs.
  • The module supports pausing and resetting the thread pool to handle interpreter critical sections safely.

Frequently Asked Questions

What thread pool library does Nelson use for parallel computations?

Nelson uses a header-only C++ thread pool implementation located in modules/parallel/src/include/BS_thread_pool.hpp. This is based on Barak Shoshany's modern C++17/20 thread pool and provides the BS::pause_thread_pool class with support for pausing, unpausing, and dynamic resizing of worker threads.

How does Nelson handle thread safety during interpreter critical sections?

The parallel module implements a pause mechanism through BS::pause_thread_pool. When the interpreter enters a critical section or requires temporary thread safety, it calls threadPool->pause() to halt background task processing. Once the critical section completes, threadPool->unpause() resumes normal operation. This approach avoids complex locking by temporarily stopping worker threads rather than implementing fine-grained synchronization.

Can I cancel a computation that is already running in the background?

Yes, the parallel module supports cancellation through the cancel function. When invoked, it calls FutureObject::cancel which sets the future's state to CANCELLED. The FevalFutureObject::evaluateFunction method periodically checks the changeState flag and aborts execution if cancellation is detected, preventing wasted CPU cycles on obsolete computations.

How is the number of parallel threads configured in Nelson?

The thread pool size is determined by NelsonConfiguration::getMaxNumCompThreads(), which reads user configuration settings. The BackgroundPoolObject singleton initializes the pause_thread_pool with this size during construction. Users can also resize the pool dynamically through resetThreadPool, which invokes threadPool->reset(newSize) to adjust the number of worker threads without restarting the interpreter.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →