pthread.js
v0.2.5
Published
A pthread-inspired JavaScript concurrency library with runtime downscaling for Node.js and browsers.
Maintainers
Readme
pthread.js
pthread.js is a pthread-inspired JavaScript library for Node.js and browsers. It exposes pthread-style names, selects the strongest runtime available, and downscales when shared memory is missing or blocked.
Runtime selection is automatic:
shared-memory:SharedArrayBufferplus blockingAtomics.message-worker: Web Workers or Node.jsworker_threadswithout shared memory.promise: same-event-loop scheduling for restricted runtimes.
Fallback runtimes cannot exactly reproduce native blocking semantics. In those modes, operations that would block in C can return a Promise<number>.
Table of Contents
- Install
- JavaScript Usage
- Worker Usage
- Web Usage
- C pthread vs pthread.js
- API Coverage
- Minimum Runtime Versions
- License
Install
npm install pthread.jsJavaScript Usage
import Thread from 'pthread.js';
const thread = new Thread();
await thread.pthread_create(async value => {
return value * 2;
}, 21);
console.log(await thread.pthread_join());Worker Usage
import Thread from 'pthread.js';
const thread = new Thread();
await thread.pthread_create(new URL('./worker.mjs', import.meta.url), { left: 2, right: 3 });
console.log(await thread.pthread_join());import { pthread_worker_main } from 'pthread.js/worker';
pthread_worker_main(async arg => {
return arg.left + arg.right;
});Web Usage
Use pthread.js directly from jsDelivr with one <script> tag:
<button id="run">Run</button>
<output id="result"></output>
<script type="module">
import Thread, { detectRuntime } from 'https://cdn.jsdelivr.net/npm/[email protected]/index.js';
const button = document.querySelector('#run');
const result = document.querySelector('#result');
button.addEventListener('click', async () => {
const thread = new Thread();
await thread.pthread_create(async name => `hello ${name}`, 'browser');
result.textContent = await thread.pthread_join();
console.table(detectRuntime());
});
</script>The browser automatically downscales. If SharedArrayBuffer and blocking Atomics are available, pthread.js uses the shared-memory backend. If not, it uses Web Workers. If workers are not available, it uses the promise backend.
To enable the strongest browser backend, serve your app with cross-origin isolation headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corpIn Express:
app.use((req, res, next) => {
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
next();
});Check the selected backend at runtime:
import Thread, { detectRuntime } from 'https://cdn.jsdelivr.net/npm/[email protected]/index.js';
console.table(detectRuntime());
const thread = new Thread();
console.log(thread.backend.kind);C pthread vs pthread.js
Native pthread in C:
#include <pthread.h>
#include <stdio.h>
void *run(void *arg) {
int *value = arg;
*value = *value * 2;
return arg;
}
int main(void) {
pthread_t thread;
int value = 21;
pthread_create(&thread, NULL, run, &value);
pthread_join(thread, NULL);
printf("%d\n", value);
return 0;
}pthread.js:
import Thread from 'pthread.js';
const thread = new Thread();
await thread.pthread_create(async value => value * 2, 21);
const value = await thread.pthread_join();
console.log(value);Native pthread synchronizes real OS threads and memory. pthread.js uses JavaScript workers when the platform allows it, and falls back to cooperative promises when it does not. The API shape is familiar, but JavaScript cannot expose every POSIX scheduling, signal, priority, or process-shared behavior exactly.
API Coverage
Implemented or simulated groups:
| Group | Functions |
| --- | --- |
| Thread lifecycle | pthread_create, pthread_join, pthread_exit, pthread_detach, pthread_self, pthread_equal |
| Cancellation | pthread_cancel, pthread_setcancelstate, pthread_setcanceltype, pthread_testcancel, pthread_cleanup_push, pthread_cleanup_pop |
| Mutexes | pthread_mutex_init, pthread_mutex_destroy, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_timedlock, pthread_mutex_unlock |
| Mutex attributes | pthread_mutexattr_init, pthread_mutexattr_destroy, pthread_mutexattr_gettype, pthread_mutexattr_settype, pthread_mutexattr_getpshared, pthread_mutexattr_setpshared, pthread_mutexattr_getprotocol, pthread_mutexattr_setprotocol, pthread_mutexattr_getprioceiling, pthread_mutexattr_setprioceiling |
| Conditions | pthread_cond_init, pthread_cond_destroy, pthread_cond_wait, pthread_cond_timedwait, pthread_cond_signal, pthread_cond_broadcast |
| Condition attributes | pthread_condattr_init, pthread_condattr_destroy, pthread_condattr_getpshared, pthread_condattr_setpshared, pthread_condattr_getclock, pthread_condattr_setclock |
| Read-write locks | pthread_rwlock_init, pthread_rwlock_destroy, pthread_rwlock_rdlock, pthread_rwlock_tryrdlock, pthread_rwlock_wrlock, pthread_rwlock_trywrlock, pthread_rwlock_timedrdlock, pthread_rwlock_timedwrlock, pthread_rwlock_unlock |
| Read-write attributes | pthread_rwlockattr_init, pthread_rwlockattr_destroy, pthread_rwlockattr_getpshared, pthread_rwlockattr_setpshared, pthread_rwlockattr_getkind_np, pthread_rwlockattr_setkind_np |
| Thread attributes | pthread_attr_init, pthread_attr_destroy, pthread_attr_getdetachstate, pthread_attr_setdetachstate, pthread_attr_getschedpolicy, pthread_attr_setschedpolicy, pthread_attr_getschedparam, pthread_attr_setschedparam, pthread_attr_getinheritsched, pthread_attr_setinheritsched, pthread_attr_getscope, pthread_attr_setscope, pthread_attr_getstacksize, pthread_attr_setstacksize, pthread_attr_getstack, pthread_attr_setstack, pthread_attr_getguardsize, pthread_attr_setguardsize, pthread_attr_getstackaddr, pthread_attr_setstackaddr |
| TLS | pthread_key_create, pthread_key_delete, pthread_setspecific, pthread_getspecific |
| One-time init | pthread_once |
| Barriers | pthread_barrier_init, pthread_barrier_destroy, pthread_barrier_wait, pthread_barrierattr_init, pthread_barrierattr_destroy, pthread_barrierattr_getpshared, pthread_barrierattr_setpshared |
| Spin locks | pthread_spin_init, pthread_spin_destroy, pthread_spin_lock, pthread_spin_trylock, pthread_spin_unlock |
| Semaphores | sem_init, sem_destroy, sem_wait, sem_trywait, sem_timedwait, sem_post, sem_getvalue |
| Scheduling and names | pthread_getconcurrency, pthread_setconcurrency, pthread_setschedparam, pthread_getschedparam, pthread_setschedprio, pthread_getcpuclockid, pthread_getname_np, pthread_setname_np |
| Signals and platform extensions | pthread_kill, pthread_sigmask, pthread_atfork, pthread_getaffinity_np, pthread_setaffinity_np, pthread_getattr_np, pthread_attr_getaffinity_np, pthread_attr_setaffinity_np, pthread_getattr_default_np, pthread_setattr_default_np, pthread_timedjoin_np, pthread_tryjoin_np, pthread_clockjoin_np, pthread_yield_np, pthread_is_threaded_np |
Functions that cannot be represented in JavaScript return ENOSYS: pthread_mutex_consistent, pthread_mutex_consistent_np, robust mutex attribute helpers, priority ceiling helpers on live mutexes, and POSIX clock-lock variants.
Minimum Runtime Versions
The shared-memory backend also requires cross-origin isolation in browsers: Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp or equivalent.
| Platform | Shared-memory backend | Message-worker backend | Promise backend | | --- | --- | --- | --- | | Chrome desktop | 92+ | 61+ as ESM, older with bundling | 61+ as ESM | | Edge desktop | 92+ | 79+ as ESM | 79+ as ESM | | Firefox desktop | 79+ | 60+ as ESM | 60+ as ESM | | Safari macOS | 15.2+ | 10.1+ as ESM | 10.1+ as ESM | | Chrome Android | 89+ | 61+ as ESM | 61+ as ESM | | Firefox Android | 79+ | 60+ as ESM | 60+ as ESM | | Safari iOS | 15.2+ | 10.3+ as ESM | 10.3+ as ESM | | Node.js | 12.17+ | 12.17+ | 12.17+ | | Deno | 1.0+ | 1.0+ | 1.0+ | | Bun | 1.0+ | 1.0+ | 1.0+ |
References: MDN SharedArrayBuffer, MDN Atomics, Chrome SharedArrayBuffer updates, MDN Web Workers, and Node.js worker_threads.
License
MIT. See LICENSE.md.
