Parallel Primes

A concurrent prime counter — the flagship klain:sync showcase.

This is a real fan-out / fan-in pipeline that counts the primes below N across a pool of goroutines running in parallel on every core, exactly the way you'd write it in Go:

- `go(fn)` spawns each worker onto the M:N work-stealing scheduler. - a buffered `Channel<number>` streams every prime a worker finds back to a single collector, and an unbuffered `Channel` signals completion. - `select(...)` lets the collector wait on both channels at once — counting primes as they arrive while tallying which workers have finished — and a `defaultCase` drains the last buffered results without blocking.

Because the scheduler is preemptive, no worker can starve the others, and the whole thing is a single native binary with no runtime or GC required.

import { go, Channel, select, defaultCase } from 'klain:sync';

const N = 500000;
const WORKERS = 8;

const primes = new Channel<number>(1024); // every prime found, streamed to the collector
const done = new Channel<number>(0);      // a worker signals when its slice is exhausted

function isPrime(n: number): boolean {
  if (n < 2) return false;
  if (n % 2 === 0) return n === 2;
  for (let d = 3; d * d <= n; d += 2) {
    if (n % d === 0) return false;
  }
  return true;
}

// Fan out: worker `id` tests the interleaved slice id, id+WORKERS, id+2·WORKERS…
// so the work is evenly spread. Each captures only top-level channels.
function spawnWorker(id: number): void {
  go(() => {
    for (let n = 2 + id; n <= N; n += WORKERS) {
      if (isPrime(n)) {
        primes.send(n);
      }
    }
    done.send(id);
  });
}

for (let w = 0; w < WORKERS; w++) {
  spawnWorker(w);
}

// Fan in: count primes as they stream in and tally worker completions. select
// takes whichever channel is ready; when every worker has reported done, drain
// any still-buffered primes with a non-blocking default and stop.
let count = 0;
let finished = 0;

while (finished < WORKERS) {
  select(
    primes.recvCase((p: number) => { count += 1; }),
    done.recvCase((id: number) => { finished += 1; }),
  );
}

let draining = true;
while (draining) {
  select(
    primes.recvCase((p: number) => { count += 1; }),
    defaultCase(() => { draining = false; }),
  );
}

console.log(`primes below ${N}: ${count}`);
console.log(`(found by ${WORKERS} goroutines running in parallel)`);
View source on GitHubexamples/goroutines/parallel_primes.ts