Skip to content

Commit 067aa99

Browse files
committed
Reduce passing around objects for increased performance
1 parent afa4171 commit 067aa99

File tree

14 files changed

+473
-267
lines changed

14 files changed

+473
-267
lines changed

src/lib/check_move_args.ts

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,48 @@ import {
99
} from "./lib.ts";
1010
import { toSerialized } from "./shared.ts";
1111

12+
const WARNING_MSG =
13+
"Warning: You are passing a SharedArrayBuffer to a worker without locking. Please wrap this data in a Mutex() or RwLock() to prevent race conditions.";
14+
15+
function warnUnsafe() {
16+
console.warn(WARNING_MSG);
17+
}
18+
1219
export function checkMoveArgs(args: any[]) {
13-
for (const arg of args) {
14-
const isRawSAB = arg instanceof SharedArrayBuffer;
15-
// Check if it's a TypedArray (e.g. Uint8Array) viewing a Shared buffer
16-
const isViewSAB = ArrayBuffer.isView(arg) &&
17-
arg.buffer instanceof SharedArrayBuffer;
18-
const isThreadSafe = arg instanceof Mutex || arg instanceof Condvar ||
19-
arg instanceof RwLock || arg instanceof Sender ||
20-
arg instanceof Receiver || arg instanceof Semaphore ||
21-
arg instanceof Barrier;
22-
const isLibrarySAB = !isThreadSafe &&
23-
typeof arg[toSerialized] !== "undefined";
24-
25-
if (isRawSAB || isViewSAB || isLibrarySAB) {
26-
console.warn(
27-
"Warning: You are passing a SharedArrayBuffer to a worker without locking. Please wrap this data in a Mutex() or RwLock() to prevent race conditions.",
28-
);
20+
const len = args.length;
21+
22+
for (let i = 0; i < len; i++) {
23+
const arg = args[i];
24+
25+
// If it's null or not an object, it cannot be a SAB or Class Instance.
26+
if (!arg || typeof arg !== "object") continue;
27+
28+
if (arg instanceof SharedArrayBuffer) {
29+
warnUnsafe();
30+
continue;
31+
}
32+
33+
// We strictly use isView first (native slot check) to avoid accessing .buffer on random objects
34+
if (ArrayBuffer.isView(arg) && arg.buffer instanceof SharedArrayBuffer) {
35+
warnUnsafe();
36+
continue;
37+
}
38+
39+
if (arg[toSerialized] !== undefined) {
40+
if (
41+
arg instanceof Mutex ||
42+
arg instanceof Condvar ||
43+
arg instanceof RwLock ||
44+
arg instanceof Sender ||
45+
arg instanceof Receiver ||
46+
arg instanceof Semaphore ||
47+
arg instanceof Barrier
48+
) {
49+
continue;
50+
}
51+
52+
// If it had the symbol but wasn't a lock, it's unsafe.
53+
warnUnsafe();
2954
}
3055
}
3156
}

src/lib/json_buffer.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,17 +1469,13 @@ class SharedJsonBufferImpl<T extends Proxyable> extends Serializable {
14691469
}
14701470

14711471
[toSerialized]() {
1472-
return {
1473-
value: this.buffer,
1474-
transfer: [],
1475-
typeId: 7,
1476-
};
1472+
return [this.buffer, [] as any, 7] as const;
14771473
}
14781474

14791475
static override [toDeserialized](
14801476
data: ReturnType<
14811477
SharedJsonBufferImpl<any>[typeof toSerialized]
1482-
>["value"],
1478+
>[0],
14831479
) {
14841480
return new SharedJsonBufferImpl(null as any, data);
14851481
}

src/lib/lib.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,19 @@ export type { JoinHandle, Result, SharedMemoryView } from "./types.ts";
1111
let globalPool: WorkerPool | null = null;
1212
let globalConfig = { maxWorkers: navigator.hardwareConcurrency || 4 };
1313

14+
let functionIdCounter = 0;
1415
const globalFunctionRegistry = new WeakMap<
1516
UserFunction,
16-
{ id: string; code: string }
17+
[
18+
/**
19+
* fnId
20+
*/
21+
number,
22+
/**
23+
* code
24+
*/
25+
string,
26+
]
1727
>();
1828

1929
export function initRuntime(config: { maxWorkers: number }) {
@@ -82,7 +92,8 @@ export function spawn(arg1: any, arg2?: any): JoinHandle<any> {
8292

8393
if (!meta) {
8494
// Cache miss: Generate ID and patch code
85-
const id = Math.random().toString(36).slice(2);
95+
// const id = Math.random().toString(36).slice(2);
96+
const id = functionIdCounter++;
8697
const callerLocation = getCallerLocation();
8798

8899
// We wrap this in a try-catch block inside the cache logic
@@ -92,7 +103,7 @@ export function spawn(arg1: any, arg2?: any): JoinHandle<any> {
92103
"export default " + fn.toString(),
93104
callerLocation.filePath,
94105
);
95-
meta = { id, code };
106+
meta = [id, code];
96107
globalFunctionRegistry.set(fn, meta);
97108
} catch (err) {
98109
console.error(err);
@@ -112,11 +123,11 @@ export function spawn(arg1: any, arg2?: any): JoinHandle<any> {
112123
// Task submission
113124
(async () => {
114125
try {
115-
const task: ThreadTask = {
116-
fnId: meta!.id,
117-
code: meta!.code,
126+
const task: ThreadTask = [
127+
meta![0],
128+
meta![1],
118129
args,
119-
};
130+
];
120131

121132
const val = await pool.submit(task);
122133
resolve({ ok: true, value: val });

0 commit comments

Comments
 (0)