Thread Pool Scheduler

The scheduler module implements a minimalist thread-pool abstraction. It provides basic task queuing and execution across worker threads using crossbeam channels.

Structure

struct ThreadPool {
    workers: Vec<Worker>,
    sender: Option<channel::Sender<Job>>,
}
type Job = Box<dyn FnOnce() + Send + 'static>;

Uses crossbeam unbounded channel. Receivers are cloned per worker (MPMC). Sender dropped on shutdown → workers exit.

Architecture

╔════════════════════════════════════ ThreadPool ════════════════════════════════════╗
║ workers: Vec<Worker>       sender: Option<channel::Sender<Job>>                    ║
║                                                                                     ║
║      ╔═══════════════╗     ╔═══════════════╗           ╔═══════════════╗          ║
║      ║  Worker[0]    ║     ║  Worker[1]    ║    ...    ║ Worker[size-1]║          ║
║      ║  thread: Join ║     ║  thread: Join ║           ║  thread: Join ║          ║
║      ╚═══════╤═══════╝     ╚═══════╤═══════╝           ╚═══════╤═══════╝          ║
║              │                     │                           │                  ║
║              ▼                     ▼                           ▼                  ║
║         Receiver      Receiver.clone()       Receiver.clone()                     ║
║              └─────────────────┬──────────────────┘                               ║
║                                │                                                  ║
║              Jobs queued via ThreadPool::execute(job)                             ║
╚═══════════════════════════════════════════════════════════════════════════════════╝

Construction (ThreadPool::new)

fn new(size)
      │
      ├─▶ create channel::unbounded<Job>() → (sender, receiver)
      │
      ├─▶ spawn `size` worker threads:
      │     let receiver = receiver.clone()  // crossbeam receiver is clonable
      │     loop {
      │         match receiver.recv() {
      │            Ok(job) => job(),
      │            Err(_) => break (channel closed)
      │         }
      │     }
      │
      └─▶ store JoinHandle in Worker.thread

Task Submission (ThreadPool::execute)

fn execute<F>(f: F)
   where F: FnOnce() + Send + 'static

   └─ sender.send(Box::new(f)).unwrap()

Flow

ThreadPool::execute(task A) ────────▶ sender
ThreadPool::execute(task B) ────────▶ sender
                                         │
                                         ▼
                                  crossbeam channel
                                   (MPMC, lock-free)
                                         │
                      ┌──────────────────┼──────────────────┐
                      │                  │                  │
                      ▼                  ▼                  ▼
           Worker 0 receiver    Worker 1 receiver    Worker 2 receiver
                 │                      │                    │
                 └──────── first recv() wins task ──────────┘
                               │
                               ├──▶ executes task A
                               └──▶ executes task B

Shutdown (Drop impl)

impl Drop for ThreadPool
      │
      ├─▶ sender.take() → drop sender end → closes channel
      │
      └─▶ iterate workers:
            if thread handle exists:
                thread.join().unwrap()

Benefits of Crossbeam

  • Lock-free MPMC — no Arc<Mutex<>> overhead, better performance
  • Clonable receivers — cleaner API, each worker owns a receiver clone
  • Better contention — optimized for multi-consumer scenarios

Usage

Used by PipelineExecutor to spawn main and reserve worker pools for query execution.