From 13119ef53b1015144633fd40c0f2a4125f2c5c97 Mon Sep 17 00:00:00 2001 From: Devin Ivy Date: Wed, 8 Apr 2026 18:33:31 -0400 Subject: [PATCH] feat: Int32 non-atomic shared primitive --- src/index.ts | 1 + src/shared/index.ts | 1 + src/shared/int32.ts | 28 ++++++++++++++++++++++++ test/shared/int32.test.ts | 46 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 src/shared/int32.ts create mode 100644 test/shared/int32.test.ts diff --git a/src/index.ts b/src/index.ts index 672b7b5..8ba7926 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ export { transfer } from './transfer.ts'; export type { Runner } from './runner.ts'; export type { Loadable } from './shared/index.ts'; export { + Int32, AtomicBool, AtomicInt8, AtomicUint8, diff --git a/src/shared/index.ts b/src/shared/index.ts index 8ecf09f..c28b6ea 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -1,4 +1,5 @@ export type { Loadable } from './loadable.ts'; +export { Int32 } from './int32.ts'; export { AtomicBool } from './atomic-bool.ts'; export { AtomicInt8 } from './atomic-int8.ts'; export { AtomicUint8 } from './atomic-uint8.ts'; diff --git a/src/shared/int32.ts b/src/shared/int32.ts new file mode 100644 index 0000000..64292c7 --- /dev/null +++ b/src/shared/int32.ts @@ -0,0 +1,28 @@ +import { registerSync } from './reconstruct.ts'; +import type { Loadable } from './loadable.ts'; + +export class Int32 implements Loadable { + static readonly byteSize = 4; + static readonly byteAlignment = 4; + private readonly view: Int32Array; + + constructor(buffer?: SharedArrayBuffer, byteOffset?: number) { + const buf = buffer ?? new SharedArrayBuffer(4); + const offset = byteOffset ?? 0; + this.view = new Int32Array(buf, offset, 1); + } + + load(): number { + return this.view[0]; + } + + store(value: number): void { + this.view[0] = value; + } + + [Symbol.for('moroutine.shared')](): { tag: string; buffer: SharedArrayBuffer; byteOffset: number } { + return { tag: 'Int32', buffer: this.view.buffer as SharedArrayBuffer, byteOffset: this.view.byteOffset }; + } +} + +registerSync('Int32', Int32); diff --git a/test/shared/int32.test.ts b/test/shared/int32.test.ts new file mode 100644 index 0000000..744591a --- /dev/null +++ b/test/shared/int32.test.ts @@ -0,0 +1,46 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert/strict'; +import { Int32 } from 'moroutine'; + +describe('Int32', () => { + it('self-allocates and initializes to zero', () => { + const v = new Int32(); + assert.equal(v.load(), 0); + }); + + it('stores and loads a value', () => { + const v = new Int32(); + v.store(42); + assert.equal(v.load(), 42); + }); + + it('accepts an external buffer and byteOffset', () => { + const buffer = new SharedArrayBuffer(16); + const v = new Int32(buffer, 4); + v.store(99); + assert.equal(new Int32Array(buffer, 4, 1)[0], 99); + }); + + it('defaults byteOffset to 0', () => { + const buffer = new SharedArrayBuffer(4); + const v = new Int32(buffer); + v.store(7); + assert.equal(new Int32Array(buffer)[0], 7); + }); + + it('exposes static byteSize of 4', () => { + assert.equal(Int32.byteSize, 4); + }); + + it('exposes static byteAlignment of 4', () => { + assert.equal(Int32.byteAlignment, 4); + }); + + it('does not use Atomics (plain read/write)', () => { + const buffer = new SharedArrayBuffer(4); + const v = new Int32(buffer); + v.store(123); + const raw = new Int32Array(buffer); + assert.equal(raw[0], 123); + }); +}); -- 2.51.2