Skip to content

Commit d1ccb4b

Browse files
Merge pull request #1258 from sosy-lab/typescript-migration/migration/workers-folder
Convert workerDirector.js to workerDirector.ts
2 parents 32d3ffa + ef26dec commit d1ccb4b

3 files changed

Lines changed: 204 additions & 101 deletions

File tree

benchexec/tablegenerator/react-table/build/main.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

benchexec/tablegenerator/react-table/src/workers/workerDirector.js

Lines changed: 0 additions & 100 deletions
This file was deleted.
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// This file is part of BenchExec, a framework for reliable benchmarking:
2+
// https://github.com/sosy-lab/benchexec
3+
//
4+
// SPDX-FileCopyrightText: 2019-2020 Dirk Beyer <https://www.sosy-lab.org>
5+
//
6+
// SPDX-License-Identifier: Apache-2.0
7+
8+
// Content of ./scripts/stats.worker.js transformed into a data url to
9+
// deal with Chrome being unable to load WebWorkers when opened using
10+
// the file:// protocol https://stackoverflow.com/questions/21408510/chrome-cant-load-web-worker
11+
12+
import { stats as statsWorkerDataUrl } from "./dataUrls";
13+
require("setimmediate"); // provides setImmediate and clearImmediate
14+
15+
// ===============
16+
// Worker Director Types
17+
// ===============
18+
19+
/**
20+
* Data item sent to the stats worker.
21+
* (Derived from what stats.worker.js reads: categoryType, resultType, column, columnType, columnTitle)
22+
*/
23+
type StatsWorkerItem = {
24+
categoryType: string;
25+
resultType: string;
26+
column: number | string;
27+
columnType?: string;
28+
columnTitle: string;
29+
};
30+
31+
type StatsBucketMeta = {
32+
type: string | null;
33+
maxDecimals: number;
34+
};
35+
36+
/**
37+
* Note: stats.worker.js stringifies almost everything before posting back.
38+
* So the numeric stats arrive as strings ("NaN", "Infinity", "42.0", ...).
39+
*/
40+
type StatsBucket = Partial<{
41+
title: string;
42+
sum: string;
43+
avg: string;
44+
max: string;
45+
median: string;
46+
min: string;
47+
stdev: string;
48+
meta: StatsBucketMeta;
49+
}>;
50+
51+
type StatsWorkerResult =
52+
| {
53+
columnType: string;
54+
total: StatsBucket | undefined;
55+
[bucketKey: string]: unknown; // narrowed below via access patterns
56+
}
57+
| Record<string, unknown>;
58+
59+
/**
60+
* Map pool name -> (data shape, result shape).
61+
*/
62+
type WorkerJobMap = {
63+
stats: {
64+
data: StatsWorkerItem[];
65+
result: StatsWorkerResult &
66+
Record<string, StatsBucket | string | undefined>;
67+
};
68+
};
69+
70+
type WorkerPoolName = keyof WorkerJobMap;
71+
72+
type DataUrlString = string;
73+
74+
interface WorkerPoolConfig<N extends WorkerPoolName = WorkerPoolName> {
75+
template: DataUrlString;
76+
poolSize: number;
77+
name: N;
78+
}
79+
80+
interface WorkerWrapper {
81+
worker: Worker;
82+
busy: boolean;
83+
}
84+
85+
type WorkerPoolsByName = {
86+
[N in WorkerPoolName]: WorkerWrapper[];
87+
};
88+
89+
type WorkerIncomingMessage<N extends WorkerPoolName> = {
90+
transaction: number;
91+
result: WorkerJobMap[N]["result"];
92+
};
93+
94+
type WorkerOutgoingMessage<N extends WorkerPoolName> = {
95+
data: WorkerJobMap[N]["data"];
96+
transaction: number;
97+
};
98+
99+
type QueueItem<N extends WorkerPoolName> = {
100+
name: N;
101+
data: WorkerJobMap[N]["data"];
102+
callback: (result: WorkerJobMap[N]["result"]) => void;
103+
};
104+
105+
type EnqueueOptions<N extends WorkerPoolName> = {
106+
name: N;
107+
data: WorkerJobMap[N]["data"];
108+
};
109+
110+
const WORKER_POOLS: WorkerPoolConfig[] = [
111+
{
112+
template: statsWorkerDataUrl,
113+
poolSize: 8,
114+
name: "stats",
115+
},
116+
];
117+
118+
const queue: Array<QueueItem<WorkerPoolName>> = [];
119+
120+
// Store that maps callback functions to a worker transaction number
121+
const refTable = new Map<number, (result: unknown) => void>();
122+
123+
let transaction = 1;
124+
125+
const handleWorkerMessage = <N extends WorkerPoolName>(
126+
{ data: message }: MessageEvent<WorkerIncomingMessage<N>>,
127+
worker: WorkerWrapper,
128+
): void => {
129+
const { transaction: messageTransaction, result } = message;
130+
const callback = refTable.get(messageTransaction);
131+
worker.busy = false;
132+
133+
if (callback) {
134+
callback(result);
135+
// clear entry
136+
refTable.delete(messageTransaction);
137+
}
138+
};
139+
140+
// Pool population
141+
const workerPool: WorkerPoolsByName = Object.fromEntries(
142+
WORKER_POOLS.map(({ template, poolSize, name }) => {
143+
const pool: WorkerWrapper[] = [];
144+
for (let i = 0; i < poolSize; i += 1) {
145+
const worker = new Worker(template);
146+
const workerObj: WorkerWrapper = { worker, busy: false };
147+
worker.onmessage = (msg) => handleWorkerMessage(msg, workerObj);
148+
149+
pool.push(workerObj);
150+
}
151+
return [name, pool] as const;
152+
}),
153+
) as WorkerPoolsByName;
154+
155+
// gets the first idle worker and reserves it for job dispatch
156+
const reserveWorker = (name: WorkerPoolName): WorkerWrapper | null => {
157+
const worker = workerPool[name].find((w) => !w.busy);
158+
if (!worker) {
159+
return null;
160+
}
161+
worker.busy = true;
162+
return worker;
163+
};
164+
165+
const processQueue = (): void => {
166+
const item = queue.shift();
167+
if (item) {
168+
const reservedWorker = reserveWorker(item.name);
169+
if (!reservedWorker) {
170+
queue.unshift(item);
171+
// pushes the function back onto the stack for
172+
// execution on next cycle of the event loop
173+
setImmediate(processQueue);
174+
return;
175+
}
176+
const ourTransaction = transaction;
177+
transaction += 1;
178+
const meta: WorkerOutgoingMessage<typeof item.name> = {
179+
data: item.data,
180+
transaction: ourTransaction,
181+
};
182+
refTable.set(ourTransaction, item.callback as (result: unknown) => void);
183+
reservedWorker.worker.postMessage(meta);
184+
setImmediate(processQueue);
185+
}
186+
};
187+
188+
/**
189+
* Registers a new job request and wraps it in a promise that resolves
190+
* on completion of dispatched job.
191+
*
192+
* @param {object} options
193+
*/
194+
const enqueue = async <N extends WorkerPoolName>({
195+
name,
196+
data,
197+
}: EnqueueOptions<N>): Promise<WorkerJobMap[N]["result"]> =>
198+
new Promise((resolve) => {
199+
queue.push({ name, data, callback: resolve });
200+
setImmediate(processQueue);
201+
});
202+
203+
export { enqueue };

0 commit comments

Comments
 (0)