Skip to content

Commit 04f2b91

Browse files
committed
Cross-platform error stack trace injection
1 parent 9c12e51 commit 04f2b91

24 files changed

+605
-203
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ await Promise.all([
113113

114114
console.log(user.balance); // 15
115115
```
116-
In this example, the `add` function is used within the multithreaded `addBalance` function. The `yield` statement is used to declare external dependencies, ensuring that the required functions and data are available to the threaded function.
116+
In this example, the `add` function is used within the multithreaded `addBalance` function. The `yield` statement is used to declare external dependencies. You can think of it as a way to import external data, functions or even packages.
117117

118118
As with previous examples, the shared state is managed using `$claim` and `$unclaim` to guarantee proper synchronization and prevent data conflicts.
119119

package-lock.json

Lines changed: 24 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "multithreading",
3-
"version": "0.2.0",
3+
"version": "0.2.1",
44
"description": "⚡ Multithreading functions in JavaScript to speedup heavy workloads, designed to feel like writing vanilla functions.",
55
"author": "Walter van der Giessen <waltervdgiessen@gmail.com>",
66
"homepage": "https://multithreading.io",
@@ -16,11 +16,11 @@
1616
"worker-threads"
1717
],
1818
"types": "./dist/index.d.ts",
19-
"module": "./dist/index.mjs",
19+
"module": "./dist/index.js",
2020
"main": "./dist/index.js",
2121
"exports": {
2222
"types": "./dist/index.d.ts",
23-
"import": "./dist/index.mjs",
23+
"import": "./dist/index.js",
2424
"require": "./dist/index.js"
2525
},
2626
"files": [
@@ -44,6 +44,7 @@
4444
"@rollup/plugin-swc": "^0.3.0",
4545
"@rollup/plugin-terser": "^0.4.4",
4646
"@types/node": "^20.10.4",
47+
"cross-env": "^7.0.3",
4748
"jest": "^29.7.0",
4849
"nodemon": "^3.0.2",
4950
"rimraf": "^5.0.5",
@@ -58,16 +59,12 @@
5859
"build": "npm run build:worker && rollup -c rollup.config.js --configPlugin @rollup/plugin-swc && rimraf .temp",
5960
"dev": "nodemon",
6061
"prepublishOnly": "npm run build",
61-
"test": "jest"
62+
"test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest"
6263
},
6364
"bugs": {
6465
"url": "https://github.com/W4G1/multithreading/issues"
6566
},
66-
"peerDependencies": {
67-
"web-worker": "^1.2.0"
68-
},
6967
"optionalDependencies": {
7068
"@rollup/rollup-linux-x64-gnu": "^4.9.1"
71-
},
72-
"dependencies": {}
69+
}
7370
}

rollup.config.dev.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ export default ["cjs"].flatMap((type) => {
4646
plugins: [...(version === ".min" ? [terser()] : [])],
4747
},
4848
],
49-
external: ["web-worker"],
5049
})
5150
);
5251
});

rollup.config.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,14 @@ export default ["esm", "cjs"].flatMap((type) => {
5050
},
5151
},
5252
],
53+
5354
output: [
5455
{
5556
file: `dist/index${version}.${ext}`,
5657
format: type,
5758
sourcemap: false,
5859
name: "multithreading",
59-
dynamicImportInCjs: false,
60+
dynamicImportInCjs: true,
6061
globals: {
6162
"web-worker": "Worker",
6263
},
@@ -66,15 +67,15 @@ export default ["esm", "cjs"].flatMap((type) => {
6667
terser({
6768
compress: {
6869
toplevel: true,
69-
passes: 3,
70+
passes: 2,
7071
},
72+
mangle: {},
7173
}),
7274
]
7375
: []),
7476
],
7577
},
7678
],
77-
external: ["web-worker"],
7879
})
7980
);
8081
});

rollup.config.worker.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ export default [
5353
terser({
5454
compress: {
5555
toplevel: true,
56-
passes: 3,
56+
passes: 2,
5757
},
58+
mangle: {},
5859
}),
5960
],
6061
name: "multithreading",

src/constants.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/index.ts

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,21 @@
11
import "./lib/polyfills/Promise.withResolvers.ts";
2+
import "./lib/polyfills/import.meta.resolve.ts";
3+
import "./lib/polyfills/web-worker.ts";
4+
25
import { serialize } from "./lib/serialize.ts";
3-
import { GLOBAL_FUNCTION_NAME } from "./constants.ts";
46
import * as $ from "./lib/keys.ts";
5-
import { MainEvent } from "./lib/types";
7+
import { MainEvent, UserFunction } from "./lib/types";
68
import { setupWorkerListeners } from "./lib/setupWorkerListeners.ts";
79
import { parseTopLevelYieldStatements } from "./lib/parseTopLevelYieldStatements.ts";
810

9-
const INLINE_WORKER = `__INLINE_WORKER__`;
11+
const inlineWorker = `__INLINE_WORKER__`;
1012

1113
export async function $claim(value: Object) {}
1214
export function $unclaim(value: Object) {}
1315

14-
const workerPools = new WeakMap<Function, Worker[]>();
16+
const workerPools = new WeakMap<UserFunction, Worker[]>();
1517
const valueOwnershipQueue = new WeakMap<Object, Worker[]>();
1618

17-
// Either AsyncGenerator or Generator
18-
type CommonGenerator<T, TReturn, TNext> =
19-
| AsyncGenerator<T, TReturn, TNext>
20-
| Generator<T, TReturn, TNext>;
21-
22-
type UserFunction<T extends Array<unknown> = [], TReturn = void> = (
23-
...args: T
24-
) => CommonGenerator<
25-
any,
26-
TReturn,
27-
{
28-
$claim: typeof $claim;
29-
$unclaim: typeof $unclaim;
30-
}
31-
>;
32-
3319
interface ThreadedConfig {
3420
debug: boolean;
3521
maxThreads: number;
@@ -73,7 +59,7 @@ export function threaded<T extends Array<unknown>, TReturn>(
7359
const init = (async () => {
7460
const fnStr = fn.toString();
7561

76-
const yieldList = parseTopLevelYieldStatements(fnStr);
62+
const yieldList = await parseTopLevelYieldStatements(fnStr);
7763

7864
// @ts-ignore - Call function without arguments
7965
const gen = fn();
@@ -93,26 +79,24 @@ export function threaded<T extends Array<unknown>, TReturn>(
9379
}
9480

9581
const workerCode = [
96-
`globalThis.${GLOBAL_FUNCTION_NAME} = ${fnStr};`,
97-
INLINE_WORKER + "",
82+
inlineWorker,
83+
`__internal.${$.UserFunction} = ${fnStr};`,
9884
];
9985

10086
const serializedVariables = serialize(context);
10187

10288
for (const [key, value] of Object.entries(serializedVariables)) {
10389
if (value[$.WasType] !== $.Function) continue;
90+
// globalthis. is necessary to prevent duplicate variable names when the function is named
10491
workerCode.unshift(`globalThis.${key} = ${value.value};`);
10592

10693
delete serializedVariables[key];
10794
}
10895

109-
// Polyfill for Node.js
110-
globalThis.Worker ??= (await import("web-worker")).default;
111-
11296
const workerCodeString = workerCode.join("\r\n");
11397

11498
for (let i = 0; i < config.maxThreads; i++) {
115-
const worker = new Worker(
99+
const worker = new (await Worker)(
116100
encodeURI(
117101
"data:application/javascript;base64," + btoa(workerCodeString)
118102
),
@@ -126,7 +110,9 @@ export function threaded<T extends Array<unknown>, TReturn>(
126110
context,
127111
valueOwnershipQueue,
128112
invocationQueue,
129-
workerPool
113+
workerPool,
114+
workerCodeString,
115+
i
130116
);
131117

132118
workerPool.push(worker);

src/lib/colors.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const red = "\x1b[31m";
2+
export const cyan = "\x1b[36m";
3+
export const gray = "\x1b[90m";
4+
export const reset = "\x1b[39m";

src/lib/getErrorPreview.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const colorRed = "\x1b[31m";
33
const colorCyan = "\x1b[36m";
44
const colorReset = "\x1b[39m";
55

6-
export function getErrorPreview(error: Error, code: string) {
6+
export function getErrorPreview(error: Error, code: string, pid: number) {
77
const [message, ...serializedStackFrames] = error.stack!.split("\n");
88

99
// Check if error originates from inside the user function
@@ -51,11 +51,16 @@ export function getErrorPreview(error: Error, code: string) {
5151

5252
const index = serializedStackFrames.indexOf(stackFrame);
5353
serializedStackFrames[index] =
54-
` at ${colorCyan}<user function>${colorReset}\n` +
54+
` at ${colorCyan}<Thread_${pid}>${colorReset}\n` +
5555
colorGray +
5656
" " +
5757
previewLines.join("\n ") +
5858
colorReset;
5959

60-
return message + "\n" + serializedStackFrames.slice(0, index + 1).join("\n");
60+
// return message + "\n" + serializedStackFrames.slice(0, index + 1).join("\n");
61+
return (
62+
message.split(":").slice(1).join(":").trim() +
63+
"\n" +
64+
serializedStackFrames.slice(0, index + 1).join("\n")
65+
);
6166
}

src/lib/keys.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const ClaimAcceptance = 8;
88
export const ClaimRejection = 9;
99
export const Invocation = 12;
1010
export const Synchronization = 13;
11+
export const Error = 14;
1112

1213
export const Variables = "a";
1314
export const Args = "b";
@@ -23,6 +24,12 @@ export const DebugEnabled = "k";
2324
export const Type = "l";
2425
export const AbsolutePath = "m";
2526
export const Code = "n";
27+
export const Pid = "o";
28+
export const UserFunction = "p";
29+
export const Internal = "q";
30+
export const ShareableNameMap = "r";
31+
export const ValueClaimMap = "s";
32+
export const ValueInUseCount = "t";
2633

2734
export declare type Function = typeof Function;
2835
export declare type Other = typeof Other;
@@ -34,6 +41,7 @@ export declare type ClaimAcceptance = typeof ClaimAcceptance;
3441
export declare type ClaimRejection = typeof ClaimRejection;
3542
export declare type Invocation = typeof Invocation;
3643
export declare type Synchronization = typeof Synchronization;
44+
export declare type Error = typeof Error;
3745

3846
export declare type Variables = typeof Variables;
3947
export declare type Args = typeof Args;
@@ -49,3 +57,9 @@ export declare type DebugEnabled = typeof DebugEnabled;
4957
export declare type Type = typeof Type;
5058
export declare type AbsolutePath = typeof AbsolutePath;
5159
export declare type Code = typeof Code;
60+
export declare type Pid = typeof Pid;
61+
export declare type UserFunction = typeof UserFunction;
62+
export declare type Internal = typeof Internal;
63+
export declare type ShareableNameMap = typeof ShareableNameMap;
64+
export declare type ValueClaimMap = typeof ValueClaimMap;
65+
export declare type ValueInUseCount = typeof ValueInUseCount;

0 commit comments

Comments
 (0)