std1.0.0-beta-7 SafeC docs

Source sync/parallel.h

View in the repository

#pragma once
// SafeC Standard Library — data-parallel loops on a persistent thread pool.
//
// parallel_for(n, grain, body, ctx) splits [0, n) into chunks of at least
// 'grain' indices and runs body(ctx, lo, hi) for each, on the calling
// thread plus the pool's workers, returning when every chunk is done.
// Chunks are handed out dynamically, so uneven work balances out.
//
// The workers are started on first use and then kept, parked on a condition
// variable between calls, so a call costs a wake-up (a few microseconds),
// not a thread spawn. Pick 'grain' so one chunk is worth at least ~10-50us
// of work; with n <= grain the body simply runs inline.
//
// Runs serially (body(ctx, 0, n) on the caller) when:
//   - the pool has one thread (SAFEC_NUM_THREADS=1, parallel_set_threads(1),
//     or a single-core machine),
//   - another parallel_for is already running (a nested call from inside a
//     body, or a concurrent call from another thread),
//   - the target has no threads (freestanding / bare-metal, WebAssembly).
//
// Not for real-time code: a call blocks until the slowest chunk finishes
// and wakes threads through the OS scheduler. Audio callbacks and other
// hard-deadline paths should stay single-threaded.
//
// Thread count: SAFEC_NUM_THREADS if set, else the number of online CPUs
// (at most PARALLEL_MAX_THREADS), including the calling thread.

#include <std/erased.h>

namespace std {

#define PARALLEL_MAX_THREADS 64

// Runtime entry point (the body and its context erased, see std/erased.h);
// call parallel_for below instead.
void parallel_for_raw_(unsigned long n, unsigned long grain,
                       fn void(&Erased, unsigned long, unsigned long) body, &Erased ctx);

// struct Job { const &float x; &float y; };
// static void work(&Job j, unsigned long lo, unsigned long hi) { ... }
// std::parallel_for(n, 4096UL, work, &job);
generic<T>
void parallel_for(unsigned long n, unsigned long grain,
                  fn void(&T, unsigned long, unsigned long) body, &T ctx) {
    unsafe {
        parallel_for_raw_(n, grain, (fn void(&Erased, unsigned long, unsigned long))body, (&Erased)ctx);
    }
}

// Threads a parallel_for uses, counting the caller (>= 1).
unsigned long parallel_threads();

// Sets the thread count (clamped to 1 .. PARALLEL_MAX_THREADS). Lowering it
// takes effect on the next call; raising it starts more workers lazily.
void parallel_set_threads(unsigned long n);

} // namespace std