Skip to content

Commit 62e7952

Browse files
MaxGraeydcodeIO
authored andcommitted
Implement JS-like Atomics (#835)
1 parent 46054db commit 62e7952

File tree

5 files changed

+406
-64
lines changed

5 files changed

+406
-64
lines changed

std/assembly/atomics.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { ArrayBufferView } from "./arraybuffer";
2+
import { E_INDEXOUTOFRANGE } from "./util/error";
3+
4+
export namespace Atomics {
5+
6+
// @ts-ignore: decorator
7+
@inline
8+
export function load<T extends ArrayBufferView>(array: T, index: i32): valueof<T> {
9+
if (index < 0 || (index << alignof<valueof<T>>()) >= array.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
10+
return atomic.load<valueof<T>>(
11+
changetype<usize>(array.buffer) + (index << alignof<valueof<T>>()) + array.byteOffset
12+
);
13+
}
14+
15+
// @ts-ignore: decorator
16+
@inline
17+
export function store<T extends ArrayBufferView>(array: T, index: i32, value: valueof<T>): void {
18+
if (index < 0 || (index << alignof<valueof<T>>()) >= array.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
19+
atomic.store<valueof<T>>(
20+
changetype<usize>(array.buffer) + (index << alignof<valueof<T>>()) + array.byteOffset,
21+
value
22+
);
23+
}
24+
25+
// @ts-ignore: decorator
26+
@inline
27+
export function add<T extends ArrayBufferView>(array: T, index: i32, value: valueof<T>): valueof<T> {
28+
if (index < 0 || (index << alignof<valueof<T>>()) >= array.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
29+
return atomic.add<valueof<T>>(
30+
changetype<usize>(array.buffer) + (index << alignof<valueof<T>>()) + array.byteOffset,
31+
value
32+
);
33+
}
34+
35+
// @ts-ignore: decorator
36+
@inline
37+
export function sub<T extends ArrayBufferView>(array: T, index: i32, value: valueof<T>): valueof<T> {
38+
if (index < 0 || (index << alignof<valueof<T>>()) >= array.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
39+
return atomic.sub<valueof<T>>(
40+
changetype<usize>(array.buffer) + (index << alignof<valueof<T>>()) + array.byteOffset,
41+
value
42+
);
43+
}
44+
45+
// @ts-ignore: decorator
46+
@inline
47+
export function and<T extends ArrayBufferView>(array: T, index: i32, value: valueof<T>): valueof<T> {
48+
if (index < 0 || (index << alignof<valueof<T>>()) >= array.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
49+
return atomic.and<valueof<T>>(
50+
changetype<usize>(array.buffer) + (index << alignof<valueof<T>>()) + array.byteOffset,
51+
value
52+
);
53+
}
54+
55+
// @ts-ignore: decorator
56+
@inline
57+
export function or<T extends ArrayBufferView>(array: T, index: i32, value: valueof<T>): valueof<T> {
58+
if (index < 0 || (index << alignof<valueof<T>>()) >= array.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
59+
return atomic.or<valueof<T>>(
60+
changetype<usize>(array.buffer) + (index << alignof<valueof<T>>()) + array.byteOffset,
61+
value
62+
);
63+
}
64+
65+
// @ts-ignore: decorator
66+
@inline
67+
export function xor<T extends ArrayBufferView>(array: T, index: i32, value: valueof<T>): valueof<T> {
68+
if (index < 0 || (index << alignof<valueof<T>>()) >= array.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
69+
return atomic.xor<valueof<T>>(
70+
changetype<usize>(array.buffer) + (index << alignof<valueof<T>>()) + array.byteOffset,
71+
value
72+
);
73+
}
74+
75+
// @ts-ignore: decorator
76+
@inline
77+
export function exchange<T extends ArrayBufferView>(array: T, index: i32, value: valueof<T>): valueof<T> {
78+
if (index < 0 || (index << alignof<valueof<T>>()) >= array.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
79+
return atomic.xchg<valueof<T>>(
80+
changetype<usize>(array.buffer) + (index << alignof<valueof<T>>()) + array.byteOffset,
81+
value
82+
);
83+
}
84+
85+
// @ts-ignore: decorator
86+
@inline
87+
export function compareExchange<T extends ArrayBufferView>(
88+
array: T,
89+
index: i32,
90+
expectedValue: valueof<T>,
91+
replacementValue: valueof<T>
92+
): valueof<T> {
93+
if (index < 0 || (index << alignof<valueof<T>>()) >= array.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
94+
return atomic.cmpxchg<valueof<T>>(
95+
changetype<usize>(array.buffer) + (index << alignof<valueof<T>>()) + array.byteOffset,
96+
expectedValue,
97+
replacementValue
98+
);
99+
}
100+
101+
// @ts-ignore: decorator
102+
@inline
103+
export function wait<T extends ArrayBufferView>(array: T, value: valueof<T>, timeout: i64 = -1): AtomicWaitResult {
104+
return atomic.wait<valueof<T>>(changetype<usize>(array.buffer) + array.byteOffset, value, timeout);
105+
}
106+
107+
// @ts-ignore: decorator
108+
@inline
109+
export function notify<T extends ArrayBufferView>(array: T, index: i32, count: i32 = -1): i32 {
110+
if (index < 0 || (index << alignof<valueof<T>>()) >= array.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
111+
return atomic.notify(changetype<usize>(array.buffer) + (index << alignof<valueof<T>>()) + array.byteOffset, count);
112+
}
113+
114+
export function isLockFree(size: usize): bool {
115+
return size == 1 || size == 2 || size == 4;
116+
}
117+
}

std/assembly/index.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,22 @@ declare namespace table {
11161116
export function copy(dest: u32, src: u32, n: u32): void;
11171117
}
11181118

1119+
declare namespace Atomics {
1120+
export function load<T = i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64>(array: TypedArray<T>, index: i32): T;
1121+
export function store<T = i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64>(array: TypedArray<T>, index: i32, value: T): void;
1122+
export function add<T = i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64>(array: TypedArray<T>, index: i32, value: T): T;
1123+
export function sub<T = i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64>(array: TypedArray<T>, index: i32, value: T): T;
1124+
export function and<T = i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64>(array: TypedArray<T>, index: i32, value: T): T;
1125+
export function or<T = i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64>(array: TypedArray<T>, index: i32, value: T): T;
1126+
export function xor<T = i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64>(array: TypedArray<T>, index: i32, value: T): T;
1127+
export function exchange<T = i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64>(array: TypedArray<T>, index: i32, value: T): T;
1128+
export function compareExchange<T = i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64>(array: TypedArray<T>, index: i32, expectedValue: T, replacementValue: T): T;
1129+
export function wait<T = i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64>(array: TypedArray<T>, value: T, timeout?: i64): AtomicWaitResult;
1130+
export function notify<T = i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64>(array: TypedArray<T>, index: i32, count?: i32): i32;
1131+
/** The static Atomics.isLockFree() method is used to determine whether to use locks or atomic operations. It returns true, if the given size is one of the BYTES_PER_ELEMENT */
1132+
export function isLockFree(size: usize): bool;
1133+
}
1134+
11191135
/** Class representing a generic, fixed-length raw binary data buffer. */
11201136
declare class ArrayBuffer {
11211137
/** The size, in bytes, of the array. */

0 commit comments

Comments
 (0)