diff --git a/README.md b/README.md index 52409e7..91bab02 100644 --- a/README.md +++ b/README.md @@ -688,6 +688,7 @@ Inside the `isTask` branch, `task.args` is typed as the moroutine's argument tup All examples require Node v24+ and can be run directly, e.g. `node examples/primes/main.ts`. - [`examples/primes`](examples/primes) - CPU-bound prime checking on the global runtime +- [`examples/custom-runtime`](examples/custom-runtime) - register a custom global runtime; bare, explicit, and pinned dispatch - [`examples/non-blocking`](examples/non-blocking) - main thread stays responsive during heavy computation - [`examples/parallel-batch`](examples/parallel-batch) - sequential vs parallel batch processing - [`examples/atomics`](examples/atomics) - shared atomic counter across workers diff --git a/examples/custom-runtime/app-runtime.ts b/examples/custom-runtime/app-runtime.ts new file mode 100644 index 0000000..f66449f --- /dev/null +++ b/examples/custom-runtime/app-runtime.ts @@ -0,0 +1,10 @@ +import { define, roundRobin } from '../../src/index.ts'; +import type { RuntimeDefinition } from '../../src/index.ts'; + +// The app's runtime definition: a define()'d config in its own side-effect-free +// module, so it has a stable module identity. Registered from main.ts before +// the first dispatch. +export default define(import.meta, { + size: 2, + balance: roundRobin(), +}) satisfies RuntimeDefinition; diff --git a/examples/custom-runtime/main.ts b/examples/custom-runtime/main.ts new file mode 100644 index 0000000..86500fb --- /dev/null +++ b/examples/custom-runtime/main.ts @@ -0,0 +1,31 @@ +// Register a custom global runtime, then dispatch to it three ways: +// bare await, runtime.run(), and pinned via assign(). +// Requires Node v24+. +// +// Run: node examples/custom-runtime/main.ts + +import { registerRuntime, runtime, assign } from '../../src/index.ts'; +import appRuntime from './app-runtime.ts'; +import { whoAmI, fib } from './work.ts'; + +// Must precede the first dispatch — the runtime boots lazily with this +// definition (2 workers, round-robin) instead of the default. +registerRuntime(appRuntime); + +// Bare await — boots the runtime on first use. +console.log(await whoAmI('bare await')); +console.log(`runtime booted with ${runtime.workers.length} workers`); + +// Explicit dispatch, including batches. +const [a, b] = await runtime.run([fib(30), fib(31)]); +console.log(`fib(30) = ${a}, fib(31) = ${b}`); + +// Round-robin placement: consecutive dispatches alternate workers. +console.log(await runtime.run(whoAmI('first'))); +console.log(await runtime.run(whoAmI('second'))); + +// Pinned dispatch: assign() targets a specific runtime worker. +console.log(await runtime.run(assign(runtime.workers[0], whoAmI('pinned')))); +console.log(await runtime.run(assign(runtime.workers[0], whoAmI('pinned again')))); + +// No shutdown() needed — idle runtime workers don't keep the process alive. diff --git a/examples/custom-runtime/work.ts b/examples/custom-runtime/work.ts new file mode 100644 index 0000000..b85eed8 --- /dev/null +++ b/examples/custom-runtime/work.ts @@ -0,0 +1,17 @@ +import { threadId } from 'node:worker_threads'; +import { mo } from '../../src/index.ts'; + +export const whoAmI = mo(import.meta, (label: string): string => { + return `${label} ran on thread ${threadId}`; +}); + +export const fib = mo(import.meta, (n: number): number => { + return n < 2 ? n : fibSync(n); +}); + +function fibSync(n: number): number { + let a = 0; + let b = 1; + for (let i = 0; i < n; i++) [a, b] = [b, a + b]; + return a; +}