Offload functions to worker threads with shared memory primitives for Node.js.
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: AtomicInt32 with full Atomics operations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Devin Ivy ff4c6308 8ed1ccb3

+147
+1
src/index.ts
··· 2 2 export { Task } from './task.ts'; 3 3 export { workerPool } from './worker-pool.ts'; 4 4 export type { Runner } from './runner.ts'; 5 + export { AtomicInt32 } from './sync/index.ts';
+46
src/sync/atomic-int32.ts
··· 1 + export class AtomicInt32 { 2 + static readonly byteSize = 4; 3 + private readonly view: Int32Array; 4 + 5 + constructor(buffer?: SharedArrayBuffer, byteOffset?: number) { 6 + const buf = buffer ?? new SharedArrayBuffer(4); 7 + const offset = byteOffset ?? 0; 8 + this.view = new Int32Array(buf, offset, 1); 9 + } 10 + 11 + load(): number { 12 + return Atomics.load(this.view, 0); 13 + } 14 + 15 + store(value: number): void { 16 + Atomics.store(this.view, 0, value); 17 + } 18 + 19 + add(value: number): number { 20 + return Atomics.add(this.view, 0, value); 21 + } 22 + 23 + sub(value: number): number { 24 + return Atomics.sub(this.view, 0, value); 25 + } 26 + 27 + and(value: number): number { 28 + return Atomics.and(this.view, 0, value); 29 + } 30 + 31 + or(value: number): number { 32 + return Atomics.or(this.view, 0, value); 33 + } 34 + 35 + xor(value: number): number { 36 + return Atomics.xor(this.view, 0, value); 37 + } 38 + 39 + exchange(value: number): number { 40 + return Atomics.exchange(this.view, 0, value); 41 + } 42 + 43 + compareExchange(expected: number, replacement: number): number { 44 + return Atomics.compareExchange(this.view, 0, expected, replacement); 45 + } 46 + }
+1
src/sync/index.ts
··· 1 + export { AtomicInt32 } from './atomic-int32.ts';
+99
test/sync/atomic-int32.test.ts
··· 1 + import { describe, it } from 'node:test'; 2 + import assert from 'node:assert/strict'; 3 + import { AtomicInt32 } from 'moroutine'; 4 + 5 + describe('AtomicInt32', () => { 6 + it('self-allocates and initializes to zero', () => { 7 + const a = new AtomicInt32(); 8 + assert.equal(a.load(), 0); 9 + }); 10 + 11 + it('stores and loads a value', () => { 12 + const a = new AtomicInt32(); 13 + a.store(42); 14 + assert.equal(a.load(), 42); 15 + }); 16 + 17 + it('accepts an external buffer and byteOffset', () => { 18 + const buffer = new SharedArrayBuffer(16); 19 + const a = new AtomicInt32(buffer, 4); 20 + a.store(99); 21 + const view = new Int32Array(buffer, 4, 1); 22 + assert.equal(view[0], 99); 23 + }); 24 + 25 + it('defaults byteOffset to 0', () => { 26 + const buffer = new SharedArrayBuffer(4); 27 + const a = new AtomicInt32(buffer); 28 + a.store(7); 29 + assert.equal(new Int32Array(buffer)[0], 7); 30 + }); 31 + 32 + it('exposes static byteSize of 4', () => { 33 + assert.equal(AtomicInt32.byteSize, 4); 34 + }); 35 + 36 + it('add returns previous value and updates', () => { 37 + const a = new AtomicInt32(); 38 + a.store(10); 39 + const prev = a.add(5); 40 + assert.equal(prev, 10); 41 + assert.equal(a.load(), 15); 42 + }); 43 + 44 + it('sub returns previous value and updates', () => { 45 + const a = new AtomicInt32(); 46 + a.store(10); 47 + const prev = a.sub(3); 48 + assert.equal(prev, 10); 49 + assert.equal(a.load(), 7); 50 + }); 51 + 52 + it('and returns previous value and updates', () => { 53 + const a = new AtomicInt32(); 54 + a.store(0b1100); 55 + const prev = a.and(0b1010); 56 + assert.equal(prev, 0b1100); 57 + assert.equal(a.load(), 0b1000); 58 + }); 59 + 60 + it('or returns previous value and updates', () => { 61 + const a = new AtomicInt32(); 62 + a.store(0b1100); 63 + const prev = a.or(0b0011); 64 + assert.equal(prev, 0b1100); 65 + assert.equal(a.load(), 0b1111); 66 + }); 67 + 68 + it('xor returns previous value and updates', () => { 69 + const a = new AtomicInt32(); 70 + a.store(0b1100); 71 + const prev = a.xor(0b1010); 72 + assert.equal(prev, 0b1100); 73 + assert.equal(a.load(), 0b0110); 74 + }); 75 + 76 + it('exchange returns previous value and sets new', () => { 77 + const a = new AtomicInt32(); 78 + a.store(42); 79 + const prev = a.exchange(99); 80 + assert.equal(prev, 42); 81 + assert.equal(a.load(), 99); 82 + }); 83 + 84 + it('compareExchange succeeds when expected matches', () => { 85 + const a = new AtomicInt32(); 86 + a.store(42); 87 + const actual = a.compareExchange(42, 99); 88 + assert.equal(actual, 42); 89 + assert.equal(a.load(), 99); 90 + }); 91 + 92 + it('compareExchange fails when expected does not match', () => { 93 + const a = new AtomicInt32(); 94 + a.store(42); 95 + const actual = a.compareExchange(0, 99); 96 + assert.equal(actual, 42); 97 + assert.equal(a.load(), 42); 98 + }); 99 + });