Skip to content
Development
Agent

threading

<!-- GENERATED from docs/api/threading.md by scripts/build-agent-skill.mjs. Do not edit. -->

BOOST
From plugin
crossbind
14832 skills32 agents
Install
$ npx -y skills add crossbind/crossbind --agent claude-code

How it fires

How this agent gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.

Context preview

The summary Claude sees to decide when to auto-load this agent.

<!-- GENERATED from docs/api/threading.md by scripts/build-agent-skill.mjs. Do not edit. -->

Agent definition

threading.md

<!-- GENERATED from docs/api/threading.md by scripts/build-agent-skill.mjs. Do not edit. -->

Threading — `runtime: 'st'` vs `'mt'`, `useWorker`, and edge limits

> Two orthogonal axes: **threading** (single vs multi-thread Wasm) and **`useWorker`** (whether the Wasm module runs in a Web Worker). Don't confuse them. > > **`useWorker` is on by default in a browser** (the runtime enables it wherever `globalThis.Worker` exists), so browser code awaits its calls, construction included. Pass `useWorker: false` to opt out.

The two axes

                    │ runtime: 'st'        │ runtime: 'mt'
────────────────────┼──────────────────────┼─────────────────────────
useWorker: false    │ Opt-out: main thread │ Wasm runs main-thread,
                    │ Wasm. Smallest setup.│ pthreads via SharedArray-
                    │                      │ Buffer. Needs COOP/COEP.
────────────────────┼──────────────────────┼─────────────────────────
useWorker: true     │ Wasm in 1 Web Worker.│ Wasm in 1 Web Worker;
                    │ Comlink bridge. Main │ pthreads spawn ADDITIONAL
                    │ thread free. Required│ workers from there. Needs
                    │ for OPFS persistence.│ COOP/COEP + Worker support.

When you need each

| You want | Pick | |----------|------| | Quickest path to "C++ in browser" | `runtime: 'st'`, no `useWorker` | | Persistent storage in browser | `runtime: 'st'`, `useWorker: true` | | CPU-bound parallelism (image / geo / crypto) | `runtime: 'mt'` | | Both: persistent storage AND parallelism | `runtime: 'mt'`, `useWorker: true` | | Cloudflare Worker / Deno Deploy / Vercel Edge | `runtime: 'st'` only — `mt` and `useWorker` not supported | | React Native | `runtime: 'mt'` if perf-sensitive (pthreads via JSI; no COOP/COEP needed, except on an Expo app's web build) |

Setting `runtime: 'mt'`

In `crossbind.config.js`:

export default {
  general: { name: 'myapp' },
  paths: { config: import.meta.url },
  target: { runtime: 'mt' },  // ← here
}

Two things happen at build time:

1. The Wasm is compiled with `-pthread` (Emscripten flag). 2. Any transitive dependency that's already `mt` keeps the project on `mt`. Conversely, if any dep is `mt`, this project auto-promotes to `mt` (you can't downgrade).

The COOP/COEP requirement

Multi-threaded Wasm uses `SharedArrayBuffer`, which browsers gate behind **cross-origin isolation**. Your hosting layer must send these response headers:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

Without them, `SharedArrayBuffer` is `undefined` and the Wasm init silently fails.

How to verify (in the browser console)

console.log(crossOriginIsolated)   // must be true for `mt`
console.log(typeof SharedArrayBuffer)  // must be 'function'

Per-host configuration

| Host | Config | |------|--------| | Vite dev / preview | Auto-injected by `@crossbind/plugin-vite` | | Webpack / Rspack dev server | Auto-injected by `@crossbind/plugin-webpack` | | Vercel | Add to `vercel.json`: `{ "headers": [{ "source": "/(.*)", "headers": [{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" }, { "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" }] }] }` | | Netlify | Add to `_headers`: `/*\n Cross-Origin-Opener-Policy: same-origin\n Cross-Origin-Embedder-Policy: require-corp` | | Cloudflare Pages | `_headers` file (same syntax as Netlify) | | nginx | `add_header Cross-Origin-Opener-Policy same-origin; add_header Cross-Origin-Embedder-Policy require-corp;` | | Express / Next.js custom server | Set headers via middleware on every response |

COEP gotcha

`require-corp` blocks cross-origin resources unless they explicitly opt in (`Cross-Origin-Resource-Policy: cross-origin` on the response, or `crossorigin` attribute on `<img>` / `<script>` tags). If your page loads third-party images / fonts / scripts, you'll either need to:

  • Switch to `Cross-Origin-Embedder-Policy: credentialless` (more permissive, supported in Chrome 96+, Firefox 110+).
  • Or proxy third-party assets through your own origin.

Known environment quirk: Playwright's WebKit ships a broken OPFS backend

In Playwright's bundled WebKit, `navigator.storage.getDirectory()` exists but always rejects with `UnknownError: The operation failed for an unknown transient reason`. Threads themselves are fine there — pthreads, SharedArrayBuffer, wasm exceptions and SIMD all work (verified 2026-07-12 with per-worker instrumentation; real Safari 26.5 works fully, OPFS included). Before the runtime preflight existed this broke mt boots: the WASMFS OPFS proxy thread swallowed the rejection and `init` deadlocked with no console or page error. The runtime now probes `getDirectory()` before mounting and falls back to `/memfs` with a logged error, so mt modules boot on all three Playwright engines and the mt playground e2e specs run WebKit again. If you ever see an mt init hang with zero signals, suspect a blocked storage backend first — presence of the OPFS API does not mean it works.

`useWorker: true` (independent of threading)

Wasm runs in a single dedicated Web Worker; main thread receives a Comlink-bridged proxy.

const m = await initNative({ useWorker: true })
// m looks identical, but every call is async.
const result = await m.add(2, 3)

You want this when:

  • You need OPFS persistent storage (mandatory: crossbind's OPFS backend runs on synchronous access handles, which only exist in Worker scope).
  • Your C++ is slow and you don't want to block the main thread paint loop.
  • You're using `runtime: 'mt'` and want pthread workers spawned from a non-main scope (cleaner architecture).

You don't need it when:

  • The C++ is fast (sub-frame) and main-thread blocking doesn't matter.
  • You're already using `mt` for parallelism (pthread workers are separate from `useWorker`).

What changes when `useWorker: true`

| Aspect | Without worker | With worker | |--------|------------

Read more
Ships withcrossbind

Import C++ and Rust like JavaScript modules (WebAssembly & React Native)

Get the whole plugin

Other agents on crossbind.