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.

refactor: remove class exports, update tests to use descriptor API

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+283 -632
+8 -32
src/index.ts
··· 3 3 export { workerPool } from './worker-pool.ts'; 4 4 export { transfer } from './transfer.ts'; 5 5 export type { Runner } from './runner.ts'; 6 - export type { Loadable } from './shared/index.ts'; 7 - export type { Bytes } from './shared/index.ts'; 8 - export type { SharedString } from './shared/index.ts'; 9 6 export { 10 - Int8, 11 - Uint8, 12 - Int16, 13 - Uint16, 14 - Int32, 15 - Uint32, 16 - Int64, 17 - Uint64, 18 - Bool, 19 - AtomicBool, 20 - AtomicInt8, 21 - AtomicUint8, 22 - AtomicInt16, 23 - AtomicUint16, 24 - AtomicInt32, 25 - AtomicUint32, 26 - AtomicInt64, 27 - AtomicUint64, 28 - Mutex, 29 - MutexGuard, 30 - RwLock, 31 - ReadGuard, 32 - WriteGuard, 33 - slab, 34 - SharedStruct, 35 7 shared, 36 - } from './shared/index.ts'; 37 - export { 38 8 int8, uint8, int16, uint16, int32, uint32, int64, uint64, bool, 39 9 int8atomic, uint8atomic, int16atomic, uint16atomic, int32atomic, uint32atomic, int64atomic, uint64atomic, boolatomic, 40 10 mutex, rwlock, 41 - bytes, 42 - string, 11 + bytes, string, 12 + } from './shared/index.ts'; 13 + export type { Loadable, Descriptor } from './shared/index.ts'; 14 + export type { 15 + Int8, Uint8, Int16, Uint16, Int32, Uint32, Int64, Uint64, Bool, 16 + AtomicInt8, AtomicUint8, AtomicInt16, AtomicUint16, AtomicInt32, AtomicUint32, AtomicInt64, AtomicUint64, AtomicBool, 17 + Mutex, MutexGuard, RwLock, ReadGuard, WriteGuard, 18 + SharedStruct, Bytes, SharedString, Tuple, 43 19 } from './shared/index.ts';
+26 -28
src/shared/index.ts
··· 1 1 export type { Loadable } from './loadable.ts'; 2 - export type { Bytes } from './bytes.ts'; 3 - export { Int8 } from './int8.ts'; 4 - export { Uint8 } from './uint8.ts'; 5 - export { Int16 } from './int16.ts'; 6 - export { Uint16 } from './uint16.ts'; 7 - export { Int32 } from './int32.ts'; 8 - export { Uint32 } from './uint32.ts'; 9 - export { Int64 } from './int64.ts'; 10 - export { Uint64 } from './uint64.ts'; 11 - export { Bool } from './bool.ts'; 12 - export { AtomicBool } from './atomic-bool.ts'; 13 - export { AtomicInt8 } from './atomic-int8.ts'; 14 - export { AtomicUint8 } from './atomic-uint8.ts'; 15 - export { AtomicInt16 } from './atomic-int16.ts'; 16 - export { AtomicUint16 } from './atomic-uint16.ts'; 17 - export { AtomicInt32 } from './atomic-int32.ts'; 18 - export { AtomicUint32 } from './atomic-uint32.ts'; 19 - export { AtomicInt64 } from './atomic-int64.ts'; 20 - export { AtomicUint64 } from './atomic-uint64.ts'; 21 - export { Mutex, MutexGuard } from './mutex.ts'; 22 - export { RwLock, ReadGuard, WriteGuard } from './rwlock.ts'; 23 - export { slab } from './slab.ts'; 24 - export { SharedStruct } from './shared-struct.ts'; 25 - export { Tuple } from './tuple.ts'; 26 - export { shared } from './shared.ts'; 27 - export { string } from './descriptors.ts'; 28 - export type { SharedString } from './string.ts'; 29 2 export { 30 3 int8, uint8, int16, uint16, int32, uint32, int64, uint64, bool, 31 4 int8atomic, uint8atomic, int16atomic, uint16atomic, int32atomic, uint32atomic, int64atomic, uint64atomic, boolatomic, 32 5 mutex, rwlock, 33 - bytes, 6 + bytes, string, 34 7 } from './descriptors.ts'; 35 8 export type { Descriptor, BytesDescriptor, StringDescriptor } from './descriptors.ts'; 9 + export { shared } from './shared.ts'; 10 + export type { Int8 } from './int8.ts'; 11 + export type { Uint8 } from './uint8.ts'; 12 + export type { Int16 } from './int16.ts'; 13 + export type { Uint16 } from './uint16.ts'; 14 + export type { Int32 } from './int32.ts'; 15 + export type { Uint32 } from './uint32.ts'; 16 + export type { Int64 } from './int64.ts'; 17 + export type { Uint64 } from './uint64.ts'; 18 + export type { Bool } from './bool.ts'; 19 + export type { AtomicInt8 } from './atomic-int8.ts'; 20 + export type { AtomicUint8 } from './atomic-uint8.ts'; 21 + export type { AtomicInt16 } from './atomic-int16.ts'; 22 + export type { AtomicUint16 } from './atomic-uint16.ts'; 23 + export type { AtomicInt32 } from './atomic-int32.ts'; 24 + export type { AtomicUint32 } from './atomic-uint32.ts'; 25 + export type { AtomicInt64 } from './atomic-int64.ts'; 26 + export type { AtomicUint64 } from './atomic-uint64.ts'; 27 + export type { AtomicBool } from './atomic-bool.ts'; 28 + export type { Mutex, MutexGuard } from './mutex.ts'; 29 + export type { RwLock, ReadGuard, WriteGuard } from './rwlock.ts'; 30 + export type { SharedStruct } from './shared-struct.ts'; 31 + export type { Bytes } from './bytes.ts'; 32 + export type { SharedString } from './string.ts'; 33 + export type { Tuple } from './tuple.ts';
+15 -22
test/shared/atomic-bool.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { AtomicBool } from 'moroutine'; 3 + import { boolatomic } from 'moroutine'; 4 4 5 5 describe('AtomicBool', () => { 6 6 it('self-allocates and initializes to false', () => { 7 - const a = new AtomicBool(); 7 + const a = boolatomic(); 8 8 assert.equal(a.load(), false); 9 9 }); 10 10 11 11 it('stores and loads true', () => { 12 - const a = new AtomicBool(); 12 + const a = boolatomic(); 13 13 a.store(true); 14 14 assert.equal(a.load(), true); 15 15 }); 16 16 17 17 it('stores and loads false', () => { 18 - const a = new AtomicBool(); 18 + const a = boolatomic(); 19 19 a.store(true); 20 20 a.store(false); 21 21 assert.equal(a.load(), false); 22 22 }); 23 23 24 - it('accepts an external buffer and byteOffset', () => { 25 - const buffer = new SharedArrayBuffer(4); 26 - const a = new AtomicBool(buffer, 2); 27 - a.store(true); 28 - assert.equal(new Uint8Array(buffer)[2], 1); 29 - }); 30 - 31 - it('exposes static byteSize of 1', () => { 32 - assert.equal(AtomicBool.byteSize, 1); 24 + it('exposes byteSize of 1', () => { 25 + assert.equal(boolatomic.byteSize, 1); 33 26 }); 34 27 35 28 it('and(false) clears, returns previous', () => { 36 - const a = new AtomicBool(); 29 + const a = boolatomic(); 37 30 a.store(true); 38 31 assert.equal(a.and(false), true); 39 32 assert.equal(a.load(), false); 40 33 }); 41 34 42 35 it('and(true) preserves, returns previous', () => { 43 - const a = new AtomicBool(); 36 + const a = boolatomic(); 44 37 a.store(true); 45 38 assert.equal(a.and(true), true); 46 39 assert.equal(a.load(), true); 47 40 }); 48 41 49 42 it('or(true) sets, returns previous', () => { 50 - const a = new AtomicBool(); 43 + const a = boolatomic(); 51 44 assert.equal(a.or(true), false); 52 45 assert.equal(a.load(), true); 53 46 }); 54 47 55 48 it('or(false) preserves, returns previous', () => { 56 - const a = new AtomicBool(); 49 + const a = boolatomic(); 57 50 a.store(true); 58 51 assert.equal(a.or(false), true); 59 52 assert.equal(a.load(), true); 60 53 }); 61 54 62 55 it('xor(true) toggles, returns previous', () => { 63 - const a = new AtomicBool(); 56 + const a = boolatomic(); 64 57 a.store(true); 65 58 assert.equal(a.xor(true), true); 66 59 assert.equal(a.load(), false); 67 60 }); 68 61 69 62 it('xor(false) preserves, returns previous', () => { 70 - const a = new AtomicBool(); 63 + const a = boolatomic(); 71 64 a.store(true); 72 65 assert.equal(a.xor(false), true); 73 66 assert.equal(a.load(), true); 74 67 }); 75 68 76 69 it('exchange returns previous value', () => { 77 - const a = new AtomicBool(); 70 + const a = boolatomic(); 78 71 a.store(true); 79 72 assert.equal(a.exchange(false), true); 80 73 assert.equal(a.load(), false); 81 74 }); 82 75 83 76 it('compareExchange succeeds when expected matches', () => { 84 - const a = new AtomicBool(); 77 + const a = boolatomic(); 85 78 a.store(true); 86 79 assert.equal(a.compareExchange(true, false), true); 87 80 assert.equal(a.load(), false); 88 81 }); 89 82 90 83 it('compareExchange fails when expected does not match', () => { 91 - const a = new AtomicBool(); 84 + const a = boolatomic(); 92 85 a.store(true); 93 86 assert.equal(a.compareExchange(false, false), true); 94 87 assert.equal(a.load(), true);
+13 -28
test/shared/atomic-int16.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { AtomicInt16 } from 'moroutine'; 3 + import { int16atomic } from 'moroutine'; 4 4 5 5 describe('AtomicInt16', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const a = new AtomicInt16(); 7 + const a = int16atomic(); 8 8 assert.equal(a.load(), 0); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const a = new AtomicInt16(); 12 + const a = int16atomic(); 13 13 a.store(1000); 14 14 assert.equal(a.load(), 1000); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(8); 19 - const a = new AtomicInt16(buffer, 4); 20 - a.store(1000); 21 - const view = new Int16Array(buffer, 4, 1); 22 - assert.equal(view[0], 1000); 23 - }); 24 - 25 - it('defaults byteOffset to 0', () => { 26 - const buffer = new SharedArrayBuffer(2); 27 - const a = new AtomicInt16(buffer); 28 - a.store(7); 29 - assert.equal(new Int16Array(buffer)[0], 7); 30 - }); 31 - 32 - it('exposes static byteSize of 2', () => { 33 - assert.equal(AtomicInt16.byteSize, 2); 17 + it('exposes byteSize of 2', () => { 18 + assert.equal(int16atomic.byteSize, 2); 34 19 }); 35 20 36 21 it('add returns previous value and updates', () => { 37 - const a = new AtomicInt16(); 22 + const a = int16atomic(); 38 23 a.store(10); 39 24 const prev = a.add(5); 40 25 assert.equal(prev, 10); ··· 42 27 }); 43 28 44 29 it('sub returns previous value and updates', () => { 45 - const a = new AtomicInt16(); 30 + const a = int16atomic(); 46 31 a.store(10); 47 32 const prev = a.sub(3); 48 33 assert.equal(prev, 10); ··· 50 35 }); 51 36 52 37 it('and returns previous value and updates', () => { 53 - const a = new AtomicInt16(); 38 + const a = int16atomic(); 54 39 a.store(0b1100); 55 40 const prev = a.and(0b1010); 56 41 assert.equal(prev, 0b1100); ··· 58 43 }); 59 44 60 45 it('or returns previous value and updates', () => { 61 - const a = new AtomicInt16(); 46 + const a = int16atomic(); 62 47 a.store(0b1100); 63 48 const prev = a.or(0b0011); 64 49 assert.equal(prev, 0b1100); ··· 66 51 }); 67 52 68 53 it('xor returns previous value and updates', () => { 69 - const a = new AtomicInt16(); 54 + const a = int16atomic(); 70 55 a.store(0b1100); 71 56 const prev = a.xor(0b1010); 72 57 assert.equal(prev, 0b1100); ··· 74 59 }); 75 60 76 61 it('exchange returns previous value and sets new', () => { 77 - const a = new AtomicInt16(); 62 + const a = int16atomic(); 78 63 a.store(1000); 79 64 const prev = a.exchange(2000); 80 65 assert.equal(prev, 1000); ··· 82 67 }); 83 68 84 69 it('compareExchange succeeds when expected matches', () => { 85 - const a = new AtomicInt16(); 70 + const a = int16atomic(); 86 71 a.store(1000); 87 72 const actual = a.compareExchange(1000, 2000); 88 73 assert.equal(actual, 1000); ··· 90 75 }); 91 76 92 77 it('compareExchange fails when expected does not match', () => { 93 - const a = new AtomicInt16(); 78 + const a = int16atomic(); 94 79 a.store(1000); 95 80 const actual = a.compareExchange(0, 2000); 96 81 assert.equal(actual, 1000);
+13 -28
test/shared/atomic-int32.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { AtomicInt32 } from 'moroutine'; 3 + import { int32atomic } from 'moroutine'; 4 4 5 5 describe('AtomicInt32', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const a = new AtomicInt32(); 7 + const a = int32atomic(); 8 8 assert.equal(a.load(), 0); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const a = new AtomicInt32(); 12 + const a = int32atomic(); 13 13 a.store(42); 14 14 assert.equal(a.load(), 42); 15 15 }); 16 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); 17 + it('exposes byteSize of 4', () => { 18 + assert.equal(int32atomic.byteSize, 4); 34 19 }); 35 20 36 21 it('add returns previous value and updates', () => { 37 - const a = new AtomicInt32(); 22 + const a = int32atomic(); 38 23 a.store(10); 39 24 const prev = a.add(5); 40 25 assert.equal(prev, 10); ··· 42 27 }); 43 28 44 29 it('sub returns previous value and updates', () => { 45 - const a = new AtomicInt32(); 30 + const a = int32atomic(); 46 31 a.store(10); 47 32 const prev = a.sub(3); 48 33 assert.equal(prev, 10); ··· 50 35 }); 51 36 52 37 it('and returns previous value and updates', () => { 53 - const a = new AtomicInt32(); 38 + const a = int32atomic(); 54 39 a.store(0b1100); 55 40 const prev = a.and(0b1010); 56 41 assert.equal(prev, 0b1100); ··· 58 43 }); 59 44 60 45 it('or returns previous value and updates', () => { 61 - const a = new AtomicInt32(); 46 + const a = int32atomic(); 62 47 a.store(0b1100); 63 48 const prev = a.or(0b0011); 64 49 assert.equal(prev, 0b1100); ··· 66 51 }); 67 52 68 53 it('xor returns previous value and updates', () => { 69 - const a = new AtomicInt32(); 54 + const a = int32atomic(); 70 55 a.store(0b1100); 71 56 const prev = a.xor(0b1010); 72 57 assert.equal(prev, 0b1100); ··· 74 59 }); 75 60 76 61 it('exchange returns previous value and sets new', () => { 77 - const a = new AtomicInt32(); 62 + const a = int32atomic(); 78 63 a.store(42); 79 64 const prev = a.exchange(99); 80 65 assert.equal(prev, 42); ··· 82 67 }); 83 68 84 69 it('compareExchange succeeds when expected matches', () => { 85 - const a = new AtomicInt32(); 70 + const a = int32atomic(); 86 71 a.store(42); 87 72 const actual = a.compareExchange(42, 99); 88 73 assert.equal(actual, 42); ··· 90 75 }); 91 76 92 77 it('compareExchange fails when expected does not match', () => { 93 - const a = new AtomicInt32(); 78 + const a = int32atomic(); 94 79 a.store(42); 95 80 const actual = a.compareExchange(0, 99); 96 81 assert.equal(actual, 42);
+13 -28
test/shared/atomic-int64.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { AtomicInt64 } from 'moroutine'; 3 + import { int64atomic } from 'moroutine'; 4 4 5 5 describe('AtomicInt64', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const a = new AtomicInt64(); 7 + const a = int64atomic(); 8 8 assert.equal(a.load(), 0n); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const a = new AtomicInt64(); 12 + const a = int64atomic(); 13 13 a.store(9000000000000n); 14 14 assert.equal(a.load(), 9000000000000n); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(16); 19 - const a = new AtomicInt64(buffer, 8); 20 - a.store(9000000000000n); 21 - const view = new BigInt64Array(buffer, 8, 1); 22 - assert.equal(view[0], 9000000000000n); 23 - }); 24 - 25 - it('defaults byteOffset to 0', () => { 26 - const buffer = new SharedArrayBuffer(8); 27 - const a = new AtomicInt64(buffer); 28 - a.store(7n); 29 - assert.equal(new BigInt64Array(buffer)[0], 7n); 30 - }); 31 - 32 - it('exposes static byteSize of 8', () => { 33 - assert.equal(AtomicInt64.byteSize, 8); 17 + it('exposes byteSize of 8', () => { 18 + assert.equal(int64atomic.byteSize, 8); 34 19 }); 35 20 36 21 it('add returns previous value and updates', () => { 37 - const a = new AtomicInt64(); 22 + const a = int64atomic(); 38 23 a.store(10n); 39 24 const prev = a.add(5n); 40 25 assert.equal(prev, 10n); ··· 42 27 }); 43 28 44 29 it('sub returns previous value and updates', () => { 45 - const a = new AtomicInt64(); 30 + const a = int64atomic(); 46 31 a.store(10n); 47 32 const prev = a.sub(3n); 48 33 assert.equal(prev, 10n); ··· 50 35 }); 51 36 52 37 it('and returns previous value and updates', () => { 53 - const a = new AtomicInt64(); 38 + const a = int64atomic(); 54 39 a.store(0b1100n); 55 40 const prev = a.and(0b1010n); 56 41 assert.equal(prev, 0b1100n); ··· 58 43 }); 59 44 60 45 it('or returns previous value and updates', () => { 61 - const a = new AtomicInt64(); 46 + const a = int64atomic(); 62 47 a.store(0b1100n); 63 48 const prev = a.or(0b0011n); 64 49 assert.equal(prev, 0b1100n); ··· 66 51 }); 67 52 68 53 it('xor returns previous value and updates', () => { 69 - const a = new AtomicInt64(); 54 + const a = int64atomic(); 70 55 a.store(0b1100n); 71 56 const prev = a.xor(0b1010n); 72 57 assert.equal(prev, 0b1100n); ··· 74 59 }); 75 60 76 61 it('exchange returns previous value and sets new', () => { 77 - const a = new AtomicInt64(); 62 + const a = int64atomic(); 78 63 a.store(9000000000000n); 79 64 const prev = a.exchange(99n); 80 65 assert.equal(prev, 9000000000000n); ··· 82 67 }); 83 68 84 69 it('compareExchange succeeds when expected matches', () => { 85 - const a = new AtomicInt64(); 70 + const a = int64atomic(); 86 71 a.store(9000000000000n); 87 72 const actual = a.compareExchange(9000000000000n, 99n); 88 73 assert.equal(actual, 9000000000000n); ··· 90 75 }); 91 76 92 77 it('compareExchange fails when expected does not match', () => { 93 - const a = new AtomicInt64(); 78 + const a = int64atomic(); 94 79 a.store(9000000000000n); 95 80 const actual = a.compareExchange(0n, 99n); 96 81 assert.equal(actual, 9000000000000n);
+13 -28
test/shared/atomic-int8.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { AtomicInt8 } from 'moroutine'; 3 + import { int8atomic } from 'moroutine'; 4 4 5 5 describe('AtomicInt8', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const a = new AtomicInt8(); 7 + const a = int8atomic(); 8 8 assert.equal(a.load(), 0); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const a = new AtomicInt8(); 12 + const a = int8atomic(); 13 13 a.store(42); 14 14 assert.equal(a.load(), 42); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(8); 19 - const a = new AtomicInt8(buffer, 4); 20 - a.store(42); 21 - const view = new Int8Array(buffer, 4, 1); 22 - assert.equal(view[0], 42); 23 - }); 24 - 25 - it('defaults byteOffset to 0', () => { 26 - const buffer = new SharedArrayBuffer(1); 27 - const a = new AtomicInt8(buffer); 28 - a.store(7); 29 - assert.equal(new Int8Array(buffer)[0], 7); 30 - }); 31 - 32 - it('exposes static byteSize of 1', () => { 33 - assert.equal(AtomicInt8.byteSize, 1); 17 + it('exposes byteSize of 1', () => { 18 + assert.equal(int8atomic.byteSize, 1); 34 19 }); 35 20 36 21 it('add returns previous value and updates', () => { 37 - const a = new AtomicInt8(); 22 + const a = int8atomic(); 38 23 a.store(10); 39 24 const prev = a.add(5); 40 25 assert.equal(prev, 10); ··· 42 27 }); 43 28 44 29 it('sub returns previous value and updates', () => { 45 - const a = new AtomicInt8(); 30 + const a = int8atomic(); 46 31 a.store(10); 47 32 const prev = a.sub(3); 48 33 assert.equal(prev, 10); ··· 50 35 }); 51 36 52 37 it('and returns previous value and updates', () => { 53 - const a = new AtomicInt8(); 38 + const a = int8atomic(); 54 39 a.store(0b1100); 55 40 const prev = a.and(0b1010); 56 41 assert.equal(prev, 0b1100); ··· 58 43 }); 59 44 60 45 it('or returns previous value and updates', () => { 61 - const a = new AtomicInt8(); 46 + const a = int8atomic(); 62 47 a.store(0b1100); 63 48 const prev = a.or(0b0011); 64 49 assert.equal(prev, 0b1100); ··· 66 51 }); 67 52 68 53 it('xor returns previous value and updates', () => { 69 - const a = new AtomicInt8(); 54 + const a = int8atomic(); 70 55 a.store(0b1100); 71 56 const prev = a.xor(0b1010); 72 57 assert.equal(prev, 0b1100); ··· 74 59 }); 75 60 76 61 it('exchange returns previous value and sets new', () => { 77 - const a = new AtomicInt8(); 62 + const a = int8atomic(); 78 63 a.store(42); 79 64 const prev = a.exchange(99); 80 65 assert.equal(prev, 42); ··· 82 67 }); 83 68 84 69 it('compareExchange succeeds when expected matches', () => { 85 - const a = new AtomicInt8(); 70 + const a = int8atomic(); 86 71 a.store(42); 87 72 const actual = a.compareExchange(42, 99); 88 73 assert.equal(actual, 42); ··· 90 75 }); 91 76 92 77 it('compareExchange fails when expected does not match', () => { 93 - const a = new AtomicInt8(); 78 + const a = int8atomic(); 94 79 a.store(42); 95 80 const actual = a.compareExchange(0, 99); 96 81 assert.equal(actual, 42);
+13 -28
test/shared/atomic-uint16.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { AtomicUint16 } from 'moroutine'; 3 + import { uint16atomic } from 'moroutine'; 4 4 5 5 describe('AtomicUint16', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const a = new AtomicUint16(); 7 + const a = uint16atomic(); 8 8 assert.equal(a.load(), 0); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const a = new AtomicUint16(); 12 + const a = uint16atomic(); 13 13 a.store(50000); 14 14 assert.equal(a.load(), 50000); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(8); 19 - const a = new AtomicUint16(buffer, 4); 20 - a.store(50000); 21 - const view = new Uint16Array(buffer, 4, 1); 22 - assert.equal(view[0], 50000); 23 - }); 24 - 25 - it('defaults byteOffset to 0', () => { 26 - const buffer = new SharedArrayBuffer(2); 27 - const a = new AtomicUint16(buffer); 28 - a.store(7); 29 - assert.equal(new Uint16Array(buffer)[0], 7); 30 - }); 31 - 32 - it('exposes static byteSize of 2', () => { 33 - assert.equal(AtomicUint16.byteSize, 2); 17 + it('exposes byteSize of 2', () => { 18 + assert.equal(uint16atomic.byteSize, 2); 34 19 }); 35 20 36 21 it('add returns previous value and updates', () => { 37 - const a = new AtomicUint16(); 22 + const a = uint16atomic(); 38 23 a.store(10); 39 24 const prev = a.add(5); 40 25 assert.equal(prev, 10); ··· 42 27 }); 43 28 44 29 it('sub returns previous value and updates', () => { 45 - const a = new AtomicUint16(); 30 + const a = uint16atomic(); 46 31 a.store(10); 47 32 const prev = a.sub(3); 48 33 assert.equal(prev, 10); ··· 50 35 }); 51 36 52 37 it('and returns previous value and updates', () => { 53 - const a = new AtomicUint16(); 38 + const a = uint16atomic(); 54 39 a.store(0b1100); 55 40 const prev = a.and(0b1010); 56 41 assert.equal(prev, 0b1100); ··· 58 43 }); 59 44 60 45 it('or returns previous value and updates', () => { 61 - const a = new AtomicUint16(); 46 + const a = uint16atomic(); 62 47 a.store(0b1100); 63 48 const prev = a.or(0b0011); 64 49 assert.equal(prev, 0b1100); ··· 66 51 }); 67 52 68 53 it('xor returns previous value and updates', () => { 69 - const a = new AtomicUint16(); 54 + const a = uint16atomic(); 70 55 a.store(0b1100); 71 56 const prev = a.xor(0b1010); 72 57 assert.equal(prev, 0b1100); ··· 74 59 }); 75 60 76 61 it('exchange returns previous value and sets new', () => { 77 - const a = new AtomicUint16(); 62 + const a = uint16atomic(); 78 63 a.store(50000); 79 64 const prev = a.exchange(100); 80 65 assert.equal(prev, 50000); ··· 82 67 }); 83 68 84 69 it('compareExchange succeeds when expected matches', () => { 85 - const a = new AtomicUint16(); 70 + const a = uint16atomic(); 86 71 a.store(50000); 87 72 const actual = a.compareExchange(50000, 100); 88 73 assert.equal(actual, 50000); ··· 90 75 }); 91 76 92 77 it('compareExchange fails when expected does not match', () => { 93 - const a = new AtomicUint16(); 78 + const a = uint16atomic(); 94 79 a.store(50000); 95 80 const actual = a.compareExchange(0, 100); 96 81 assert.equal(actual, 50000);
+13 -28
test/shared/atomic-uint32.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { AtomicUint32 } from 'moroutine'; 3 + import { uint32atomic } from 'moroutine'; 4 4 5 5 describe('AtomicUint32', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const a = new AtomicUint32(); 7 + const a = uint32atomic(); 8 8 assert.equal(a.load(), 0); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const a = new AtomicUint32(); 12 + const a = uint32atomic(); 13 13 a.store(3000000000); 14 14 assert.equal(a.load(), 3000000000); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(16); 19 - const a = new AtomicUint32(buffer, 4); 20 - a.store(3000000000); 21 - const view = new Uint32Array(buffer, 4, 1); 22 - assert.equal(view[0], 3000000000); 23 - }); 24 - 25 - it('defaults byteOffset to 0', () => { 26 - const buffer = new SharedArrayBuffer(4); 27 - const a = new AtomicUint32(buffer); 28 - a.store(7); 29 - assert.equal(new Uint32Array(buffer)[0], 7); 30 - }); 31 - 32 - it('exposes static byteSize of 4', () => { 33 - assert.equal(AtomicUint32.byteSize, 4); 17 + it('exposes byteSize of 4', () => { 18 + assert.equal(uint32atomic.byteSize, 4); 34 19 }); 35 20 36 21 it('add returns previous value and updates', () => { 37 - const a = new AtomicUint32(); 22 + const a = uint32atomic(); 38 23 a.store(10); 39 24 const prev = a.add(5); 40 25 assert.equal(prev, 10); ··· 42 27 }); 43 28 44 29 it('sub returns previous value and updates', () => { 45 - const a = new AtomicUint32(); 30 + const a = uint32atomic(); 46 31 a.store(10); 47 32 const prev = a.sub(3); 48 33 assert.equal(prev, 10); ··· 50 35 }); 51 36 52 37 it('and returns previous value and updates', () => { 53 - const a = new AtomicUint32(); 38 + const a = uint32atomic(); 54 39 a.store(0b1100); 55 40 const prev = a.and(0b1010); 56 41 assert.equal(prev, 0b1100); ··· 58 43 }); 59 44 60 45 it('or returns previous value and updates', () => { 61 - const a = new AtomicUint32(); 46 + const a = uint32atomic(); 62 47 a.store(0b1100); 63 48 const prev = a.or(0b0011); 64 49 assert.equal(prev, 0b1100); ··· 66 51 }); 67 52 68 53 it('xor returns previous value and updates', () => { 69 - const a = new AtomicUint32(); 54 + const a = uint32atomic(); 70 55 a.store(0b1100); 71 56 const prev = a.xor(0b1010); 72 57 assert.equal(prev, 0b1100); ··· 74 59 }); 75 60 76 61 it('exchange returns previous value and sets new', () => { 77 - const a = new AtomicUint32(); 62 + const a = uint32atomic(); 78 63 a.store(3000000000); 79 64 const prev = a.exchange(99); 80 65 assert.equal(prev, 3000000000); ··· 82 67 }); 83 68 84 69 it('compareExchange succeeds when expected matches', () => { 85 - const a = new AtomicUint32(); 70 + const a = uint32atomic(); 86 71 a.store(3000000000); 87 72 const actual = a.compareExchange(3000000000, 99); 88 73 assert.equal(actual, 3000000000); ··· 90 75 }); 91 76 92 77 it('compareExchange fails when expected does not match', () => { 93 - const a = new AtomicUint32(); 78 + const a = uint32atomic(); 94 79 a.store(3000000000); 95 80 const actual = a.compareExchange(0, 99); 96 81 assert.equal(actual, 3000000000);
+13 -28
test/shared/atomic-uint64.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { AtomicUint64 } from 'moroutine'; 3 + import { uint64atomic } from 'moroutine'; 4 4 5 5 describe('AtomicUint64', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const a = new AtomicUint64(); 7 + const a = uint64atomic(); 8 8 assert.equal(a.load(), 0n); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const a = new AtomicUint64(); 12 + const a = uint64atomic(); 13 13 a.store(18000000000000000000n); 14 14 assert.equal(a.load(), 18000000000000000000n); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(16); 19 - const a = new AtomicUint64(buffer, 8); 20 - a.store(18000000000000000000n); 21 - const view = new BigUint64Array(buffer, 8, 1); 22 - assert.equal(view[0], 18000000000000000000n); 23 - }); 24 - 25 - it('defaults byteOffset to 0', () => { 26 - const buffer = new SharedArrayBuffer(8); 27 - const a = new AtomicUint64(buffer); 28 - a.store(7n); 29 - assert.equal(new BigUint64Array(buffer)[0], 7n); 30 - }); 31 - 32 - it('exposes static byteSize of 8', () => { 33 - assert.equal(AtomicUint64.byteSize, 8); 17 + it('exposes byteSize of 8', () => { 18 + assert.equal(uint64atomic.byteSize, 8); 34 19 }); 35 20 36 21 it('add returns previous value and updates', () => { 37 - const a = new AtomicUint64(); 22 + const a = uint64atomic(); 38 23 a.store(10n); 39 24 const prev = a.add(5n); 40 25 assert.equal(prev, 10n); ··· 42 27 }); 43 28 44 29 it('sub returns previous value and updates', () => { 45 - const a = new AtomicUint64(); 30 + const a = uint64atomic(); 46 31 a.store(10n); 47 32 const prev = a.sub(3n); 48 33 assert.equal(prev, 10n); ··· 50 35 }); 51 36 52 37 it('and returns previous value and updates', () => { 53 - const a = new AtomicUint64(); 38 + const a = uint64atomic(); 54 39 a.store(0b1100n); 55 40 const prev = a.and(0b1010n); 56 41 assert.equal(prev, 0b1100n); ··· 58 43 }); 59 44 60 45 it('or returns previous value and updates', () => { 61 - const a = new AtomicUint64(); 46 + const a = uint64atomic(); 62 47 a.store(0b1100n); 63 48 const prev = a.or(0b0011n); 64 49 assert.equal(prev, 0b1100n); ··· 66 51 }); 67 52 68 53 it('xor returns previous value and updates', () => { 69 - const a = new AtomicUint64(); 54 + const a = uint64atomic(); 70 55 a.store(0b1100n); 71 56 const prev = a.xor(0b1010n); 72 57 assert.equal(prev, 0b1100n); ··· 74 59 }); 75 60 76 61 it('exchange returns previous value and sets new', () => { 77 - const a = new AtomicUint64(); 62 + const a = uint64atomic(); 78 63 a.store(18000000000000000000n); 79 64 const prev = a.exchange(99n); 80 65 assert.equal(prev, 18000000000000000000n); ··· 82 67 }); 83 68 84 69 it('compareExchange succeeds when expected matches', () => { 85 - const a = new AtomicUint64(); 70 + const a = uint64atomic(); 86 71 a.store(18000000000000000000n); 87 72 const actual = a.compareExchange(18000000000000000000n, 99n); 88 73 assert.equal(actual, 18000000000000000000n); ··· 90 75 }); 91 76 92 77 it('compareExchange fails when expected does not match', () => { 93 - const a = new AtomicUint64(); 78 + const a = uint64atomic(); 94 79 a.store(18000000000000000000n); 95 80 const actual = a.compareExchange(0n, 99n); 96 81 assert.equal(actual, 18000000000000000000n);
+13 -28
test/shared/atomic-uint8.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { AtomicUint8 } from 'moroutine'; 3 + import { uint8atomic } from 'moroutine'; 4 4 5 5 describe('AtomicUint8', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const a = new AtomicUint8(); 7 + const a = uint8atomic(); 8 8 assert.equal(a.load(), 0); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const a = new AtomicUint8(); 12 + const a = uint8atomic(); 13 13 a.store(200); 14 14 assert.equal(a.load(), 200); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(8); 19 - const a = new AtomicUint8(buffer, 4); 20 - a.store(200); 21 - const view = new Uint8Array(buffer, 4, 1); 22 - assert.equal(view[0], 200); 23 - }); 24 - 25 - it('defaults byteOffset to 0', () => { 26 - const buffer = new SharedArrayBuffer(1); 27 - const a = new AtomicUint8(buffer); 28 - a.store(7); 29 - assert.equal(new Uint8Array(buffer)[0], 7); 30 - }); 31 - 32 - it('exposes static byteSize of 1', () => { 33 - assert.equal(AtomicUint8.byteSize, 1); 17 + it('exposes byteSize of 1', () => { 18 + assert.equal(uint8atomic.byteSize, 1); 34 19 }); 35 20 36 21 it('add returns previous value and updates', () => { 37 - const a = new AtomicUint8(); 22 + const a = uint8atomic(); 38 23 a.store(10); 39 24 const prev = a.add(5); 40 25 assert.equal(prev, 10); ··· 42 27 }); 43 28 44 29 it('sub returns previous value and updates', () => { 45 - const a = new AtomicUint8(); 30 + const a = uint8atomic(); 46 31 a.store(10); 47 32 const prev = a.sub(3); 48 33 assert.equal(prev, 10); ··· 50 35 }); 51 36 52 37 it('and returns previous value and updates', () => { 53 - const a = new AtomicUint8(); 38 + const a = uint8atomic(); 54 39 a.store(0b1100); 55 40 const prev = a.and(0b1010); 56 41 assert.equal(prev, 0b1100); ··· 58 43 }); 59 44 60 45 it('or returns previous value and updates', () => { 61 - const a = new AtomicUint8(); 46 + const a = uint8atomic(); 62 47 a.store(0b1100); 63 48 const prev = a.or(0b0011); 64 49 assert.equal(prev, 0b1100); ··· 66 51 }); 67 52 68 53 it('xor returns previous value and updates', () => { 69 - const a = new AtomicUint8(); 54 + const a = uint8atomic(); 70 55 a.store(0b1100); 71 56 const prev = a.xor(0b1010); 72 57 assert.equal(prev, 0b1100); ··· 74 59 }); 75 60 76 61 it('exchange returns previous value and sets new', () => { 77 - const a = new AtomicUint8(); 62 + const a = uint8atomic(); 78 63 a.store(200); 79 64 const prev = a.exchange(100); 80 65 assert.equal(prev, 200); ··· 82 67 }); 83 68 84 69 it('compareExchange succeeds when expected matches', () => { 85 - const a = new AtomicUint8(); 70 + const a = uint8atomic(); 86 71 a.store(200); 87 72 const actual = a.compareExchange(200, 100); 88 73 assert.equal(actual, 200); ··· 90 75 }); 91 76 92 77 it('compareExchange fails when expected does not match', () => { 93 - const a = new AtomicUint8(); 78 + const a = uint8atomic(); 94 79 a.store(200); 95 80 const actual = a.compareExchange(0, 100); 96 81 assert.equal(actual, 200);
+9 -23
test/shared/bool.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { Bool } from 'moroutine'; 3 + import { bool } from 'moroutine'; 4 4 5 5 describe('Bool', () => { 6 6 it('self-allocates and initializes to false', () => { 7 - const v = new Bool(); 7 + const v = bool(); 8 8 assert.equal(v.load(), false); 9 9 }); 10 10 11 11 it('stores and loads true', () => { 12 - const v = new Bool(); 12 + const v = bool(); 13 13 v.store(true); 14 14 assert.equal(v.load(), true); 15 15 }); 16 16 17 17 it('stores and loads false', () => { 18 - const v = new Bool(); 18 + const v = bool(); 19 19 v.store(true); 20 20 v.store(false); 21 21 assert.equal(v.load(), false); 22 22 }); 23 23 24 24 it('round-trips true/false', () => { 25 - const v = new Bool(); 25 + const v = bool(); 26 26 v.store(true); 27 27 assert.equal(v.load(), true); 28 28 v.store(false); 29 29 assert.equal(v.load(), false); 30 30 }); 31 31 32 - it('accepts an external buffer and byteOffset', () => { 33 - const buffer = new SharedArrayBuffer(8); 34 - const v = new Bool(buffer, 1); 35 - v.store(true); 36 - assert.equal(new Uint8Array(buffer, 1, 1)[0], 1); 37 - }); 38 - 39 - it('defaults byteOffset to 0', () => { 40 - const buffer = new SharedArrayBuffer(1); 41 - const v = new Bool(buffer); 42 - v.store(true); 43 - assert.equal(new Uint8Array(buffer)[0], 1); 44 - }); 45 - 46 - it('exposes static byteSize of 1', () => { 47 - assert.equal(Bool.byteSize, 1); 32 + it('exposes byteSize of 1', () => { 33 + assert.equal(bool.byteSize, 1); 48 34 }); 49 35 50 - it('exposes static byteAlignment of 1', () => { 51 - assert.equal(Bool.byteAlignment, 1); 36 + it('exposes byteAlignment of 1', () => { 37 + assert.equal(bool.byteAlignment, 1); 52 38 }); 53 39 });
+12 -14
test/shared/cross-worker.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { workerPool, AtomicInt32, Mutex, RwLock, SharedStruct, Int32, slab } from 'moroutine'; 3 + import { workerPool, int32atomic, mutex, rwlock, shared, int32 } from 'moroutine'; 4 4 import { atomicAdd, mutexIncrement, rwlockRead } from '../fixtures/sync.ts'; 5 5 import { readPoint, writePoint } from '../fixtures/shared-struct.ts'; 6 6 7 7 describe('sync primitives across workers', () => { 8 8 it('AtomicInt32 is shared across worker threads', async () => { 9 - const counter = new AtomicInt32(); 9 + const counter = int32atomic(); 10 10 const pool = workerPool(2); 11 11 try { 12 12 await Promise.all([ ··· 20 20 }); 21 21 22 22 it('Mutex serializes access across workers', async () => { 23 - const mutex = new Mutex(); 24 - const counter = new AtomicInt32(); 23 + const m = mutex(); 24 + const counter = int32atomic(); 25 25 const pool = workerPool(2); 26 26 try { 27 27 await Promise.all([ 28 - pool(mutexIncrement(mutex, counter, 100)), 29 - pool(mutexIncrement(mutex, counter, 100)), 28 + pool(mutexIncrement(m, counter, 100)), 29 + pool(mutexIncrement(m, counter, 100)), 30 30 ]); 31 31 assert.equal(counter.load(), 200); 32 32 } finally { ··· 35 35 }); 36 36 37 37 it('RwLock allows concurrent reads from workers', async () => { 38 - const rwlock = new RwLock(); 39 - const counter = new AtomicInt32(); 38 + const rw = rwlock(); 39 + const counter = int32atomic(); 40 40 counter.store(42); 41 41 const pool = workerPool(2); 42 42 try { 43 43 const [a, b] = await Promise.all([ 44 - pool(rwlockRead(rwlock, counter)), 45 - pool(rwlockRead(rwlock, counter)), 44 + pool(rwlockRead(rw, counter)), 45 + pool(rwlockRead(rw, counter)), 46 46 ]); 47 47 assert.equal(a, 42); 48 48 assert.equal(b, 42); ··· 52 52 }); 53 53 54 54 it('SharedStruct is shared across worker threads', async () => { 55 - const [x, y] = slab(Int32, Int32); 56 - const point = new SharedStruct({ x, y }); 55 + const point = shared({ x: int32, y: int32 }); 57 56 point.store({ x: 1, y: 2 }); 58 57 59 58 const pool = workerPool(1); ··· 66 65 }); 67 66 68 67 it('worker can write to SharedStruct', async () => { 69 - const [x, y] = slab(Int32, Int32); 70 - const point = new SharedStruct({ x, y }); 68 + const point = shared({ x: int32, y: int32 }); 71 69 72 70 const pool = workerPool(1); 73 71 try {
+7 -21
test/shared/int16.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { Int16 } from 'moroutine'; 3 + import { int16 } from 'moroutine'; 4 4 5 5 describe('Int16', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const v = new Int16(); 7 + const v = int16(); 8 8 assert.equal(v.load(), 0); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const v = new Int16(); 12 + const v = int16(); 13 13 v.store(42); 14 14 assert.equal(v.load(), 42); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(16); 19 - const v = new Int16(buffer, 2); 20 - v.store(99); 21 - assert.equal(new Int16Array(buffer, 2, 1)[0], 99); 22 - }); 23 - 24 - it('defaults byteOffset to 0', () => { 25 - const buffer = new SharedArrayBuffer(2); 26 - const v = new Int16(buffer); 27 - v.store(7); 28 - assert.equal(new Int16Array(buffer)[0], 7); 29 - }); 30 - 31 - it('exposes static byteSize of 2', () => { 32 - assert.equal(Int16.byteSize, 2); 17 + it('exposes byteSize of 2', () => { 18 + assert.equal(int16.byteSize, 2); 33 19 }); 34 20 35 - it('exposes static byteAlignment of 2', () => { 36 - assert.equal(Int16.byteAlignment, 2); 21 + it('exposes byteAlignment of 2', () => { 22 + assert.equal(int16.byteAlignment, 2); 37 23 }); 38 24 });
+7 -29
test/shared/int32.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { Int32 } from 'moroutine'; 3 + import { int32 } from 'moroutine'; 4 4 5 5 describe('Int32', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const v = new Int32(); 7 + const v = int32(); 8 8 assert.equal(v.load(), 0); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const v = new Int32(); 12 + const v = int32(); 13 13 v.store(42); 14 14 assert.equal(v.load(), 42); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(16); 19 - const v = new Int32(buffer, 4); 20 - v.store(99); 21 - assert.equal(new Int32Array(buffer, 4, 1)[0], 99); 22 - }); 23 - 24 - it('defaults byteOffset to 0', () => { 25 - const buffer = new SharedArrayBuffer(4); 26 - const v = new Int32(buffer); 27 - v.store(7); 28 - assert.equal(new Int32Array(buffer)[0], 7); 29 - }); 30 - 31 - it('exposes static byteSize of 4', () => { 32 - assert.equal(Int32.byteSize, 4); 33 - }); 34 - 35 - it('exposes static byteAlignment of 4', () => { 36 - assert.equal(Int32.byteAlignment, 4); 17 + it('exposes byteSize of 4', () => { 18 + assert.equal(int32.byteSize, 4); 37 19 }); 38 20 39 - it('does not use Atomics (plain read/write)', () => { 40 - const buffer = new SharedArrayBuffer(4); 41 - const v = new Int32(buffer); 42 - v.store(123); 43 - const raw = new Int32Array(buffer); 44 - assert.equal(raw[0], 123); 21 + it('exposes byteAlignment of 4', () => { 22 + assert.equal(int32.byteAlignment, 4); 45 23 }); 46 24 });
+7 -21
test/shared/int64.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { Int64 } from 'moroutine'; 3 + import { int64 } from 'moroutine'; 4 4 5 5 describe('Int64', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const v = new Int64(); 7 + const v = int64(); 8 8 assert.equal(v.load(), 0n); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const v = new Int64(); 12 + const v = int64(); 13 13 v.store(99n); 14 14 assert.equal(v.load(), 99n); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(16); 19 - const v = new Int64(buffer, 8); 20 - v.store(42n); 21 - assert.equal(new BigInt64Array(buffer, 8, 1)[0], 42n); 22 - }); 23 - 24 - it('defaults byteOffset to 0', () => { 25 - const buffer = new SharedArrayBuffer(8); 26 - const v = new Int64(buffer); 27 - v.store(7n); 28 - assert.equal(new BigInt64Array(buffer)[0], 7n); 29 - }); 30 - 31 - it('exposes static byteSize of 8', () => { 32 - assert.equal(Int64.byteSize, 8); 17 + it('exposes byteSize of 8', () => { 18 + assert.equal(int64.byteSize, 8); 33 19 }); 34 20 35 - it('exposes static byteAlignment of 8', () => { 36 - assert.equal(Int64.byteAlignment, 8); 21 + it('exposes byteAlignment of 8', () => { 22 + assert.equal(int64.byteAlignment, 8); 37 23 }); 38 24 });
+7 -21
test/shared/int8.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { Int8 } from 'moroutine'; 3 + import { int8 } from 'moroutine'; 4 4 5 5 describe('Int8', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const v = new Int8(); 7 + const v = int8(); 8 8 assert.equal(v.load(), 0); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const v = new Int8(); 12 + const v = int8(); 13 13 v.store(42); 14 14 assert.equal(v.load(), 42); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(8); 19 - const v = new Int8(buffer, 1); 20 - v.store(99); 21 - assert.equal(new Int8Array(buffer, 1, 1)[0], 99); 22 - }); 23 - 24 - it('defaults byteOffset to 0', () => { 25 - const buffer = new SharedArrayBuffer(1); 26 - const v = new Int8(buffer); 27 - v.store(7); 28 - assert.equal(new Int8Array(buffer)[0], 7); 29 - }); 30 - 31 - it('exposes static byteSize of 1', () => { 32 - assert.equal(Int8.byteSize, 1); 17 + it('exposes byteSize of 1', () => { 18 + assert.equal(int8.byteSize, 1); 33 19 }); 34 20 35 - it('exposes static byteAlignment of 1', () => { 36 - assert.equal(Int8.byteAlignment, 1); 21 + it('exposes byteAlignment of 1', () => { 22 + assert.equal(int8.byteAlignment, 1); 37 23 }); 38 24 });
+18 -24
test/shared/mutex.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { Mutex } from 'moroutine'; 3 + import { mutex } from 'moroutine'; 4 4 5 5 describe('Mutex', () => { 6 6 it('lock and unlock', async () => { 7 - const mutex = new Mutex(); 8 - const guard = await mutex.lock(); 9 - mutex.unlock(); 7 + const m = mutex(); 8 + const guard = await m.lock(); 9 + m.unlock(); 10 10 }); 11 11 12 12 it('lock returns a MutexGuard', async () => { 13 - const mutex = new Mutex(); 14 - const guard = await mutex.lock(); 13 + const m = mutex(); 14 + const guard = await m.lock(); 15 15 assert.equal(typeof guard[Symbol.dispose], 'function'); 16 - mutex.unlock(); 16 + m.unlock(); 17 17 }); 18 18 19 19 it('MutexGuard dispose calls unlock', async () => { 20 - const mutex = new Mutex(); 21 - const guard = await mutex.lock(); 20 + const m = mutex(); 21 + const guard = await m.lock(); 22 22 guard[Symbol.dispose](); 23 23 // Should be able to lock again immediately 24 - const guard2 = await mutex.lock(); 25 - mutex.unlock(); 24 + const guard2 = await m.lock(); 25 + m.unlock(); 26 26 }); 27 27 28 28 it('serializes concurrent access', async () => { 29 - const mutex = new Mutex(); 29 + const m = mutex(); 30 30 const order: number[] = []; 31 31 32 32 const task = async (id: number) => { 33 - const guard = await mutex.lock(); 33 + const guard = await m.lock(); 34 34 order.push(id); 35 35 await new Promise((r) => setTimeout(r, 10)); 36 36 order.push(id); 37 - mutex.unlock(); 37 + m.unlock(); 38 38 }; 39 39 40 40 await Promise.all([task(1), task(2)]); ··· 46 46 }); 47 47 48 48 it('self-allocates', () => { 49 - const mutex = new Mutex(); 50 - assert.ok(mutex); 49 + const m = mutex(); 50 + assert.ok(m); 51 51 }); 52 52 53 - it('accepts an external buffer and byteOffset', () => { 54 - const buffer = new SharedArrayBuffer(16); 55 - const mutex = new Mutex(buffer, 4); 56 - assert.ok(mutex); 57 - }); 58 - 59 - it('exposes static byteSize of 4', () => { 60 - assert.equal(Mutex.byteSize, 4); 53 + it('exposes byteSize of 4', () => { 54 + assert.equal(mutex.byteSize, 4); 61 55 }); 62 56 });
+12 -18
test/shared/rwlock.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { RwLock } from 'moroutine'; 3 + import { rwlock } from 'moroutine'; 4 4 5 5 describe('RwLock', () => { 6 6 it('read lock and unlock', async () => { 7 - const rw = new RwLock(); 7 + const rw = rwlock(); 8 8 const guard = await rw.readLock(); 9 9 assert.equal(typeof guard[Symbol.dispose], 'function'); 10 10 rw.readUnlock(); 11 11 }); 12 12 13 13 it('write lock and unlock', async () => { 14 - const rw = new RwLock(); 14 + const rw = rwlock(); 15 15 const guard = await rw.writeLock(); 16 16 assert.equal(typeof guard[Symbol.dispose], 'function'); 17 17 rw.writeUnlock(); 18 18 }); 19 19 20 20 it('multiple readers can hold the lock concurrently', async () => { 21 - const rw = new RwLock(); 21 + const rw = rwlock(); 22 22 const g1 = await rw.readLock(); 23 23 const g2 = await rw.readLock(); 24 24 // Both acquired — success ··· 27 27 }); 28 28 29 29 it('writer excludes readers', async () => { 30 - const rw = new RwLock(); 30 + const rw = rwlock(); 31 31 const order: string[] = []; 32 32 33 33 await rw.writeLock(); ··· 48 48 }); 49 49 50 50 it('writer excludes other writers', async () => { 51 - const rw = new RwLock(); 51 + const rw = rwlock(); 52 52 const order: string[] = []; 53 53 54 54 await rw.writeLock(); ··· 69 69 }); 70 70 71 71 it('ReadGuard dispose calls readUnlock', async () => { 72 - const rw = new RwLock(); 72 + const rw = rwlock(); 73 73 const guard = await rw.readLock(); 74 74 guard[Symbol.dispose](); 75 75 const wg = await rw.writeLock(); ··· 77 77 }); 78 78 79 79 it('WriteGuard dispose calls writeUnlock', async () => { 80 - const rw = new RwLock(); 80 + const rw = rwlock(); 81 81 const guard = await rw.writeLock(); 82 82 guard[Symbol.dispose](); 83 83 const rg = await rw.readLock(); ··· 85 85 }); 86 86 87 87 it('self-allocates', () => { 88 - const rw = new RwLock(); 88 + const rw = rwlock(); 89 89 assert.ok(rw); 90 90 }); 91 91 92 - it('accepts an external buffer and byteOffset', () => { 93 - const buffer = new SharedArrayBuffer(16); 94 - const rw = new RwLock(buffer, 4); 95 - assert.ok(rw); 96 - }); 97 - 98 - it('exposes static byteSize of 4', () => { 99 - assert.equal(RwLock.byteSize, 4); 92 + it('exposes byteSize of 4', () => { 93 + assert.equal(rwlock.byteSize, 4); 100 94 }); 101 95 102 96 it('wakes all waiting readers when writer unlocks', async () => { 103 - const rw = new RwLock(); 97 + const rw = rwlock(); 104 98 105 99 await rw.writeLock(); 106 100
+23 -28
test/shared/shared-struct.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { SharedStruct, Int32, Uint8, Bool, slab } from 'moroutine'; 3 + import { shared, int32, uint8, bool } from 'moroutine'; 4 4 5 5 describe('SharedStruct', () => { 6 6 it('load returns all field values', () => { 7 - const [x, y] = slab(Int32, Int32); 8 - const point = new SharedStruct({ x, y }); 7 + const point = shared({ x: int32, y: int32 }); 9 8 assert.deepEqual(point.load(), { x: 0, y: 0 }); 10 9 }); 11 10 12 11 it('store writes all field values', () => { 13 - const [x, y] = slab(Int32, Int32); 14 - const point = new SharedStruct({ x, y }); 12 + const point = shared({ x: int32, y: int32 }); 15 13 point.store({ x: 10, y: 20 }); 16 14 assert.deepEqual(point.load(), { x: 10, y: 20 }); 17 15 }); 18 16 19 17 it('works with mixed types', () => { 20 - const [x, y, health, alive] = slab(Int32, Int32, Uint8, Bool); 21 - const state = new SharedStruct({ x, y, health, alive }); 18 + const state = shared({ x: int32, y: int32, health: uint8, alive: bool }); 22 19 state.store({ x: 1, y: 2, health: 100, alive: true }); 23 20 assert.deepEqual(state.load(), { x: 1, y: 2, health: 100, alive: true }); 24 21 }); 25 22 26 - it('works with independently created fields', () => { 27 - const pos = new SharedStruct({ x: new Int32(), y: new Int32() }); 28 - pos.store({ x: 42, y: 99 }); 29 - assert.deepEqual(pos.load(), { x: 42, y: 99 }); 30 - }); 31 - 32 23 it('supports nested structs', () => { 33 - const [sx, sy, sw, sh] = slab(Int32, Int32, Int32, Int32); 34 - const position = new SharedStruct({ x: sx, y: sy }); 35 - const size = new SharedStruct({ w: sw, h: sh }); 36 - const rect = new SharedStruct({ position, size }); 24 + const rect = shared({ 25 + position: { x: int32, y: int32 }, 26 + size: { w: int32, h: int32 }, 27 + }); 37 28 38 29 rect.store({ position: { x: 1, y: 2 }, size: { w: 100, h: 50 } }); 39 30 assert.deepEqual(rect.load(), { position: { x: 1, y: 2 }, size: { w: 100, h: 50 } }); 40 31 }); 41 32 42 33 it('inner struct mutation is visible through outer struct', () => { 43 - const [x, y] = slab(Int32, Int32); 44 - const pos = new SharedStruct({ x, y }); 45 - const entity = new SharedStruct({ pos, alive: new Bool() }); 34 + const entity = shared({ 35 + pos: { x: int32, y: int32 }, 36 + alive: bool, 37 + }); 46 38 47 - pos.store({ x: 10, y: 20 }); 39 + entity.fields.pos.store({ x: 10, y: 20 }); 48 40 assert.deepEqual(entity.load(), { pos: { x: 10, y: 20 }, alive: false }); 49 41 }); 50 42 51 43 it('exposes fields for direct access', () => { 52 - const [x, y] = slab(Int32, Int32); 53 - const point = new SharedStruct({ x, y }); 44 + const point = shared({ x: int32, y: int32 }); 54 45 point.fields.x.store(42); 55 46 assert.equal(point.fields.y.load(), 0); 56 47 assert.deepEqual(point.load(), { x: 42, y: 0 }); 57 48 }); 58 49 59 50 it('nested fields accessible via fields property', () => { 60 - const [x, y, w, h] = slab(Int32, Int32, Int32, Int32); 61 - const pos = new SharedStruct({ x, y }); 62 - const rect = new SharedStruct({ pos, w, h }); 51 + const rect = shared({ 52 + pos: { x: int32, y: int32 }, 53 + w: int32, 54 + h: int32, 55 + }); 63 56 64 57 rect.fields.pos.fields.x.store(99); 65 58 rect.fields.w.store(200); ··· 67 60 }); 68 61 69 62 it('is itself Loadable (can be nested)', () => { 70 - const inner = new SharedStruct({ v: new Int32() }); 71 - const outer = new SharedStruct({ inner, flag: new Bool() }); 63 + const outer = shared({ 64 + inner: { v: int32 }, 65 + flag: bool, 66 + }); 72 67 73 68 outer.store({ inner: { v: 42 }, flag: true }); 74 69 assert.deepEqual(outer.load(), { inner: { v: 42 }, flag: true });
-43
test/shared/slab.test.ts
··· 1 - import { describe, it } from 'node:test'; 2 - import assert from 'node:assert/strict'; 3 - import { slab, AtomicInt32, AtomicBool, AtomicInt64, Mutex, RwLock } from 'moroutine'; 4 - 5 - describe('slab', () => { 6 - it('allocates a single SharedArrayBuffer for multiple types', () => { 7 - const [a, b] = slab(AtomicInt32, AtomicInt32); 8 - a.store(1); 9 - b.store(2); 10 - assert.equal(a.load(), 1); 11 - assert.equal(b.load(), 2); 12 - }); 13 - 14 - it('works with mixed types', () => { 15 - const [counter, flag, lock] = slab(AtomicInt32, AtomicBool, Mutex); 16 - counter.store(42); 17 - flag.store(true); 18 - assert.equal(counter.load(), 42); 19 - assert.equal(flag.load(), true); 20 - }); 21 - 22 - it('handles alignment for 64-bit types', () => { 23 - const [flag, big] = slab(AtomicBool, AtomicInt64); 24 - flag.store(true); 25 - big.store(999n); 26 - assert.equal(flag.load(), true); 27 - assert.equal(big.load(), 999n); 28 - }); 29 - 30 - it('returns correct types', () => { 31 - const [i32, bool, mutex, rwlock] = slab(AtomicInt32, AtomicBool, Mutex, RwLock); 32 - i32.add(1); 33 - bool.xor(true); 34 - mutex.lock(); 35 - rwlock.readLock(); 36 - }); 37 - 38 - it('single type works', () => { 39 - const [a] = slab(AtomicInt32); 40 - a.store(7); 41 - assert.equal(a.load(), 7); 42 - }); 43 - });
+7 -21
test/shared/uint16.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { Uint16 } from 'moroutine'; 3 + import { uint16 } from 'moroutine'; 4 4 5 5 describe('Uint16', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const v = new Uint16(); 7 + const v = uint16(); 8 8 assert.equal(v.load(), 0); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const v = new Uint16(); 12 + const v = uint16(); 13 13 v.store(42); 14 14 assert.equal(v.load(), 42); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(16); 19 - const v = new Uint16(buffer, 2); 20 - v.store(99); 21 - assert.equal(new Uint16Array(buffer, 2, 1)[0], 99); 22 - }); 23 - 24 - it('defaults byteOffset to 0', () => { 25 - const buffer = new SharedArrayBuffer(2); 26 - const v = new Uint16(buffer); 27 - v.store(7); 28 - assert.equal(new Uint16Array(buffer)[0], 7); 29 - }); 30 - 31 - it('exposes static byteSize of 2', () => { 32 - assert.equal(Uint16.byteSize, 2); 17 + it('exposes byteSize of 2', () => { 18 + assert.equal(uint16.byteSize, 2); 33 19 }); 34 20 35 - it('exposes static byteAlignment of 2', () => { 36 - assert.equal(Uint16.byteAlignment, 2); 21 + it('exposes byteAlignment of 2', () => { 22 + assert.equal(uint16.byteAlignment, 2); 37 23 }); 38 24 });
+7 -21
test/shared/uint32.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { Uint32 } from 'moroutine'; 3 + import { uint32 } from 'moroutine'; 4 4 5 5 describe('Uint32', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const v = new Uint32(); 7 + const v = uint32(); 8 8 assert.equal(v.load(), 0); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const v = new Uint32(); 12 + const v = uint32(); 13 13 v.store(42); 14 14 assert.equal(v.load(), 42); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(16); 19 - const v = new Uint32(buffer, 4); 20 - v.store(99); 21 - assert.equal(new Uint32Array(buffer, 4, 1)[0], 99); 22 - }); 23 - 24 - it('defaults byteOffset to 0', () => { 25 - const buffer = new SharedArrayBuffer(4); 26 - const v = new Uint32(buffer); 27 - v.store(7); 28 - assert.equal(new Uint32Array(buffer)[0], 7); 29 - }); 30 - 31 - it('exposes static byteSize of 4', () => { 32 - assert.equal(Uint32.byteSize, 4); 17 + it('exposes byteSize of 4', () => { 18 + assert.equal(uint32.byteSize, 4); 33 19 }); 34 20 35 - it('exposes static byteAlignment of 4', () => { 36 - assert.equal(Uint32.byteAlignment, 4); 21 + it('exposes byteAlignment of 4', () => { 22 + assert.equal(uint32.byteAlignment, 4); 37 23 }); 38 24 });
+7 -21
test/shared/uint64.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { Uint64 } from 'moroutine'; 3 + import { uint64 } from 'moroutine'; 4 4 5 5 describe('Uint64', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const v = new Uint64(); 7 + const v = uint64(); 8 8 assert.equal(v.load(), 0n); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const v = new Uint64(); 12 + const v = uint64(); 13 13 v.store(99n); 14 14 assert.equal(v.load(), 99n); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(16); 19 - const v = new Uint64(buffer, 8); 20 - v.store(42n); 21 - assert.equal(new BigUint64Array(buffer, 8, 1)[0], 42n); 22 - }); 23 - 24 - it('defaults byteOffset to 0', () => { 25 - const buffer = new SharedArrayBuffer(8); 26 - const v = new Uint64(buffer); 27 - v.store(7n); 28 - assert.equal(new BigUint64Array(buffer)[0], 7n); 29 - }); 30 - 31 - it('exposes static byteSize of 8', () => { 32 - assert.equal(Uint64.byteSize, 8); 17 + it('exposes byteSize of 8', () => { 18 + assert.equal(uint64.byteSize, 8); 33 19 }); 34 20 35 - it('exposes static byteAlignment of 8', () => { 36 - assert.equal(Uint64.byteAlignment, 8); 21 + it('exposes byteAlignment of 8', () => { 22 + assert.equal(uint64.byteAlignment, 8); 37 23 }); 38 24 });
+7 -21
test/shared/uint8.test.ts
··· 1 1 import { describe, it } from 'node:test'; 2 2 import assert from 'node:assert/strict'; 3 - import { Uint8 } from 'moroutine'; 3 + import { uint8 } from 'moroutine'; 4 4 5 5 describe('Uint8', () => { 6 6 it('self-allocates and initializes to zero', () => { 7 - const v = new Uint8(); 7 + const v = uint8(); 8 8 assert.equal(v.load(), 0); 9 9 }); 10 10 11 11 it('stores and loads a value', () => { 12 - const v = new Uint8(); 12 + const v = uint8(); 13 13 v.store(42); 14 14 assert.equal(v.load(), 42); 15 15 }); 16 16 17 - it('accepts an external buffer and byteOffset', () => { 18 - const buffer = new SharedArrayBuffer(8); 19 - const v = new Uint8(buffer, 1); 20 - v.store(99); 21 - assert.equal(new Uint8Array(buffer, 1, 1)[0], 99); 22 - }); 23 - 24 - it('defaults byteOffset to 0', () => { 25 - const buffer = new SharedArrayBuffer(1); 26 - const v = new Uint8(buffer); 27 - v.store(7); 28 - assert.equal(new Uint8Array(buffer)[0], 7); 29 - }); 30 - 31 - it('exposes static byteSize of 1', () => { 32 - assert.equal(Uint8.byteSize, 1); 17 + it('exposes byteSize of 1', () => { 18 + assert.equal(uint8.byteSize, 1); 33 19 }); 34 20 35 - it('exposes static byteAlignment of 1', () => { 36 - assert.equal(Uint8.byteAlignment, 1); 21 + it('exposes byteAlignment of 1', () => { 22 + assert.equal(uint8.byteAlignment, 1); 37 23 }); 38 24 });