@loaders.gl/core 5.0.0-alpha.13 → 5.0.0-alpha.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dist.dev.js +190 -63
- package/dist/dist.min.js +3 -3
- package/dist/index.cjs +205 -72
- package/dist/index.cjs.map +2 -2
- package/dist/lib/api/parse-file.js +2 -0
- package/dist/lib/api/parse-file.js.map +1 -1
- package/dist/lib/api/parse-in-batches.d.ts.map +1 -1
- package/dist/lib/api/parse-in-batches.js +5 -4
- package/dist/lib/api/parse-in-batches.js.map +1 -1
- package/dist/lib/api/parse.js +4 -2
- package/dist/lib/api/parse.js.map +1 -1
- package/dist/lib/init.js +1 -1
- package/dist/lib/loader-utils/get-fetch-function.d.ts.map +1 -1
- package/dist/lib/loader-utils/get-fetch-function.js +3 -1
- package/dist/lib/loader-utils/get-fetch-function.js.map +1 -1
- package/dist/lib/loader-utils/loader-context.d.ts.map +1 -1
- package/dist/lib/loader-utils/loader-context.js +4 -0
- package/dist/lib/loader-utils/loader-context.js.map +1 -1
- package/dist/lib/loader-utils/option-defaults.d.ts.map +1 -1
- package/dist/lib/loader-utils/option-defaults.js +1 -0
- package/dist/lib/loader-utils/option-defaults.js.map +1 -1
- package/dist/lib/loader-utils/option-utils.js +1 -1
- package/dist/lib/loader-utils/option-utils.js.map +1 -1
- package/dist/loader-utils/index.cjs +231 -92
- package/dist/loader-utils/index.cjs.map +2 -2
- package/dist/loader-utils/index.d.ts +1 -1
- package/dist/loader-utils/index.d.ts.map +1 -1
- package/dist/loader-utils/index.js +1 -1
- package/dist/loader-utils/index.js.map +1 -1
- package/dist/loader-utils/json-loader.d.ts +1 -1
- package/dist/loader-utils/json-loader.js +2 -2
- package/dist/loader-utils/lib/log-utils/log.js +1 -1
- package/dist/loader-utils/lib/request-utils/request-credentials.d.ts +3 -1
- package/dist/loader-utils/lib/request-utils/request-credentials.d.ts.map +1 -1
- package/dist/loader-utils/lib/request-utils/request-credentials.js +10 -16
- package/dist/loader-utils/lib/request-utils/request-credentials.js.map +1 -1
- package/dist/loader-utils/lib/worker-loader-utils/create-loader-worker.d.ts +3 -0
- package/dist/loader-utils/lib/worker-loader-utils/create-loader-worker.d.ts.map +1 -1
- package/dist/loader-utils/lib/worker-loader-utils/create-loader-worker.js +13 -12
- package/dist/loader-utils/lib/worker-loader-utils/create-loader-worker.js.map +1 -1
- package/dist/loader-utils/lib/worker-loader-utils/parse-with-worker.d.ts +6 -0
- package/dist/loader-utils/lib/worker-loader-utils/parse-with-worker.d.ts.map +1 -1
- package/dist/loader-utils/lib/worker-loader-utils/parse-with-worker.js +38 -7
- package/dist/loader-utils/lib/worker-loader-utils/parse-with-worker.js.map +1 -1
- package/dist/loader-utils/loader-types.d.ts +12 -4
- package/dist/loader-utils/loader-types.d.ts.map +1 -1
- package/dist/loader-utils/loader-types.js.map +1 -1
- package/dist/null-loader.js +1 -1
- package/dist/null-worker-node.js +60 -15
- package/dist/null-worker.js +59 -15
- package/dist/schema-utils/lib/table/tables/convert-table.d.ts +1 -1
- package/dist/schema-utils/lib/table/tables/convert-table.js +1 -1
- package/dist/worker-utils/index.cjs +170 -49
- package/dist/worker-utils/index.cjs.map +2 -2
- package/dist/worker-utils/index.d.ts +2 -2
- package/dist/worker-utils/index.d.ts.map +1 -1
- package/dist/worker-utils/index.js +1 -1
- package/dist/worker-utils/index.js.map +1 -1
- package/dist/worker-utils/lib/env-utils/version.js +2 -2
- package/dist/worker-utils/lib/worker-api/create-worker.d.ts.map +1 -1
- package/dist/worker-utils/lib/worker-api/create-worker.js +40 -2
- package/dist/worker-utils/lib/worker-api/create-worker.js.map +1 -1
- package/dist/worker-utils/lib/worker-api/get-worker-url.d.ts +2 -0
- package/dist/worker-utils/lib/worker-api/get-worker-url.d.ts.map +1 -1
- package/dist/worker-utils/lib/worker-api/get-worker-url.js +7 -11
- package/dist/worker-utils/lib/worker-api/get-worker-url.js.map +1 -1
- package/dist/worker-utils/lib/worker-api/process-on-worker.d.ts +6 -0
- package/dist/worker-utils/lib/worker-api/process-on-worker.d.ts.map +1 -1
- package/dist/worker-utils/lib/worker-api/process-on-worker.js +64 -27
- package/dist/worker-utils/lib/worker-api/process-on-worker.js.map +1 -1
- package/dist/worker-utils/lib/worker-farm/worker-job.d.ts.map +1 -1
- package/dist/worker-utils/lib/worker-farm/worker-job.js +3 -2
- package/dist/worker-utils/lib/worker-farm/worker-job.js.map +1 -1
- package/dist/worker-utils/lib/worker-farm/worker-pool.d.ts +1 -1
- package/dist/worker-utils/lib/worker-farm/worker-pool.d.ts.map +1 -1
- package/dist/worker-utils/lib/worker-farm/worker-pool.js +65 -12
- package/dist/worker-utils/lib/worker-farm/worker-pool.js.map +1 -1
- package/dist/worker-utils/null-worker-node.js +36 -2
- package/dist/worker-utils/null-worker-node.js.map +2 -2
- package/dist/worker-utils/null-worker.js +36 -2
- package/dist/worker-utils/null-worker.js.map +2 -2
- package/dist/worker-utils/types.d.ts +25 -4
- package/dist/worker-utils/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/lib/api/parse-file.ts +2 -0
- package/src/lib/api/parse-in-batches.ts +5 -5
- package/src/lib/api/parse.ts +4 -2
- package/src/lib/loader-utils/get-fetch-function.ts +4 -2
- package/src/lib/loader-utils/loader-context.ts +4 -0
- package/src/lib/loader-utils/option-defaults.ts +1 -0
- package/src/lib/loader-utils/option-utils.ts +1 -1
- package/src/loader-utils/index.ts +1 -0
- package/src/loader-utils/json-loader.ts +1 -1
- package/src/loader-utils/lib/request-utils/request-credentials.ts +10 -12
- package/src/loader-utils/lib/worker-loader-utils/create-loader-worker.ts +38 -15
- package/src/loader-utils/lib/worker-loader-utils/parse-with-worker.ts +52 -7
- package/src/loader-utils/loader-types.ts +16 -4
- package/src/schema-utils/lib/table/tables/convert-table.ts +1 -1
- package/src/worker-utils/index.ts +3 -1
- package/src/worker-utils/lib/worker-api/create-worker.ts +41 -2
- package/src/worker-utils/lib/worker-api/get-worker-url.ts +13 -14
- package/src/worker-utils/lib/worker-api/process-on-worker.ts +128 -73
- package/src/worker-utils/lib/worker-farm/worker-job.ts +4 -2
- package/src/worker-utils/lib/worker-farm/worker-pool.ts +65 -12
- package/src/worker-utils/types.ts +26 -3
|
@@ -16,6 +16,7 @@ import WorkerFarm from '../worker-farm/worker-farm';
|
|
|
16
16
|
import type {WorkerPoolTarget} from '../worker-farm/worker-pool';
|
|
17
17
|
import {
|
|
18
18
|
getCustomWorkerURL,
|
|
19
|
+
getConfiguredWorkerURL,
|
|
19
20
|
getDefaultWorkerURL,
|
|
20
21
|
getWorkerURL,
|
|
21
22
|
getWorkerName
|
|
@@ -55,6 +56,22 @@ export function canProcessOnWorker(worker: WorkerObject, options?: WorkerOptions
|
|
|
55
56
|
);
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Checks whether a worker supports stateful batch processing.
|
|
61
|
+
* @param worker Worker descriptor declaring built-in batch support.
|
|
62
|
+
* @param options Worker options, including an explicit capability for custom URLs or source.
|
|
63
|
+
*/
|
|
64
|
+
export function canProcessOnWorkerInBatches(
|
|
65
|
+
worker: WorkerObject,
|
|
66
|
+
options: WorkerOptions = {}
|
|
67
|
+
): boolean {
|
|
68
|
+
if (!canProcessOnWorker(worker, options)) return false;
|
|
69
|
+
if (options.workerInBatches !== undefined) return options.workerInBatches;
|
|
70
|
+
return Boolean(
|
|
71
|
+
worker.workerInBatches && !options.source && !getConfiguredWorkerURL(worker, options)
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
58
75
|
/**
|
|
59
76
|
* This function expects that the worker thread sends certain messages,
|
|
60
77
|
* Creating such a worker can be automated if the worker is wrapper by a call to
|
|
@@ -77,18 +94,15 @@ export async function processOnWorker(
|
|
|
77
94
|
const job = await workerPool.startJob(
|
|
78
95
|
jobName,
|
|
79
96
|
// eslint-disable-next-line
|
|
80
|
-
onMessage.bind(null, context)
|
|
97
|
+
onMessage.bind(null, context, options),
|
|
98
|
+
undefined,
|
|
99
|
+
options.signal
|
|
81
100
|
);
|
|
82
101
|
|
|
83
|
-
const abortJob = (): void => job.abort(getAbortReason(options.signal));
|
|
84
|
-
options.signal?.addEventListener('abort', abortJob, {once: true});
|
|
85
|
-
if (options.signal?.aborted) {
|
|
86
|
-
abortJob();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
102
|
try {
|
|
103
|
+
if (options.signal?.aborted) job.abort(getAbortReason(options.signal));
|
|
90
104
|
// AbortSignal cannot be structured-cloned. It controls the job on this thread only.
|
|
91
|
-
const {signal: _signal, ...workerOptions} = options;
|
|
105
|
+
const {signal: _signal, onProgress: _onProgress, ...workerOptions} = options;
|
|
92
106
|
const transferableOptions = getTransferListForWriter(workerOptions);
|
|
93
107
|
const transferableContext = getTransferListForWriter(jobContext);
|
|
94
108
|
if (job.isRunning) {
|
|
@@ -101,8 +115,9 @@ export async function processOnWorker(
|
|
|
101
115
|
|
|
102
116
|
const result = await job.result;
|
|
103
117
|
return result.result;
|
|
104
|
-
}
|
|
105
|
-
|
|
118
|
+
} catch (error) {
|
|
119
|
+
job.abort(error);
|
|
120
|
+
throw error;
|
|
106
121
|
}
|
|
107
122
|
}
|
|
108
123
|
|
|
@@ -140,9 +155,18 @@ async function* processOnWorkerInBatchesIterator<InputBatch, OutputBatch>(
|
|
|
140
155
|
const inputIterator = getAsyncIterator(input);
|
|
141
156
|
let outputFinished = false;
|
|
142
157
|
let inputFinished = false;
|
|
158
|
+
let inputCleanupRequested = false;
|
|
143
159
|
let inputRequest = Promise.resolve();
|
|
144
160
|
const batchJobFailed = new Error('Worker batch job failed');
|
|
145
161
|
|
|
162
|
+
/** Requests cleanup even when the output consumer is paused after yielding a batch. */
|
|
163
|
+
const closeJobInput = (): void => {
|
|
164
|
+
if (!inputFinished && !inputCleanupRequested) {
|
|
165
|
+
inputCleanupRequested = true;
|
|
166
|
+
closeInputIterator(inputIterator);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
146
170
|
const finishOutput = (): void => {
|
|
147
171
|
if (!outputFinished) {
|
|
148
172
|
outputFinished = true;
|
|
@@ -157,66 +181,76 @@ async function* processOnWorkerInBatchesIterator<InputBatch, OutputBatch>(
|
|
|
157
181
|
}
|
|
158
182
|
};
|
|
159
183
|
|
|
160
|
-
const job = await workerPool
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
184
|
+
const job = await workerPool
|
|
185
|
+
.startJob(
|
|
186
|
+
options.jobName || worker.name,
|
|
187
|
+
(activeJob, type, payload) => {
|
|
188
|
+
switch (type) {
|
|
189
|
+
case 'progress':
|
|
190
|
+
reportProgress(activeJob, options, payload);
|
|
191
|
+
break;
|
|
192
|
+
|
|
193
|
+
case 'input-request':
|
|
194
|
+
inputRequest = inputRequest
|
|
195
|
+
.then(async () => {
|
|
196
|
+
if (!activeJob.isRunning) return;
|
|
197
|
+
const nextBatch = await inputIterator.next();
|
|
198
|
+
if (!activeJob.isRunning) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (nextBatch.done) {
|
|
202
|
+
inputFinished = true;
|
|
203
|
+
activeJob.postMessage('input-done', {});
|
|
204
|
+
} else {
|
|
205
|
+
activeJob.postMessage('input-batch', {input: nextBatch.value});
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
.catch(error => activeJob.abort(error));
|
|
209
|
+
break;
|
|
210
|
+
|
|
211
|
+
case 'output-batch':
|
|
212
|
+
outputBatches.push(payload.result as OutputBatch);
|
|
213
|
+
break;
|
|
214
|
+
|
|
215
|
+
case 'done':
|
|
216
|
+
activeJob.done(payload);
|
|
217
|
+
finishOutput();
|
|
218
|
+
break;
|
|
219
|
+
|
|
220
|
+
case 'error': {
|
|
221
|
+
const error = new Error(payload.error || 'Worker batch processing failed');
|
|
222
|
+
activeJob.error(error);
|
|
223
|
+
failOutput(error);
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
case 'process':
|
|
228
|
+
void onMessage(context, options, activeJob, type, payload);
|
|
229
|
+
break;
|
|
230
|
+
|
|
231
|
+
default: {
|
|
232
|
+
const error = new Error(`Unexpected worker batch message: ${type}`);
|
|
233
|
+
activeJob.error(error);
|
|
234
|
+
failOutput(error);
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
206
237
|
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
238
|
+
},
|
|
239
|
+
undefined,
|
|
240
|
+
options.signal
|
|
241
|
+
)
|
|
242
|
+
.catch(error => {
|
|
243
|
+
closeJobInput();
|
|
244
|
+
throw error;
|
|
245
|
+
});
|
|
210
246
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
void job.result.catch(failOutput);
|
|
247
|
+
void job.result.catch(error => {
|
|
248
|
+
failOutput(error);
|
|
249
|
+
closeJobInput();
|
|
250
|
+
});
|
|
217
251
|
|
|
218
252
|
try {
|
|
219
|
-
const {signal: _signal, ...workerOptions} = options;
|
|
253
|
+
const {signal: _signal, onProgress: _onProgress, ...workerOptions} = options;
|
|
220
254
|
if (job.isRunning) {
|
|
221
255
|
job.postMessage('process-in-batches', {
|
|
222
256
|
options: getTransferListForWriter(workerOptions),
|
|
@@ -239,13 +273,10 @@ async function* processOnWorkerInBatchesIterator<InputBatch, OutputBatch>(
|
|
|
239
273
|
}
|
|
240
274
|
await job.result;
|
|
241
275
|
} finally {
|
|
242
|
-
options.signal?.removeEventListener('abort', abortJob);
|
|
243
276
|
if (job.isRunning) {
|
|
244
277
|
job.abort(createAbortError('Worker batch iterator was closed'));
|
|
245
278
|
}
|
|
246
|
-
|
|
247
|
-
closeInputIterator(inputIterator);
|
|
248
|
-
}
|
|
279
|
+
closeJobInput();
|
|
249
280
|
}
|
|
250
281
|
}
|
|
251
282
|
|
|
@@ -259,7 +290,7 @@ function throwIfAborted(signal?: AbortSignal): void {
|
|
|
259
290
|
|
|
260
291
|
/** Returns a caller-provided abort reason or a cross-runtime `AbortError`. */
|
|
261
292
|
function getAbortReason(signal?: AbortSignal): unknown {
|
|
262
|
-
return signal
|
|
293
|
+
return signal ? signal.reason : createAbortError('Worker job was aborted');
|
|
263
294
|
}
|
|
264
295
|
|
|
265
296
|
/** Creates an abort error without requiring the DOMException global. */
|
|
@@ -353,11 +384,16 @@ function getWorkerPoolTarget(
|
|
|
353
384
|
*/
|
|
354
385
|
async function onMessage(
|
|
355
386
|
context: WorkerContext,
|
|
387
|
+
options: ProcessOnWorkerOptions,
|
|
356
388
|
job: WorkerJob,
|
|
357
389
|
type: WorkerMessageType,
|
|
358
390
|
payload: WorkerMessagePayload
|
|
359
391
|
) {
|
|
360
392
|
switch (type) {
|
|
393
|
+
case 'progress':
|
|
394
|
+
reportProgress(job, options, payload);
|
|
395
|
+
break;
|
|
396
|
+
|
|
361
397
|
case 'done':
|
|
362
398
|
// Worker is done
|
|
363
399
|
job.done(payload);
|
|
@@ -370,13 +406,18 @@ async function onMessage(
|
|
|
370
406
|
|
|
371
407
|
case 'process':
|
|
372
408
|
// Worker is asking for us (main thread) to process something
|
|
373
|
-
const {id, input, options} = payload;
|
|
409
|
+
const {id, input, options: processOptions} = payload;
|
|
374
410
|
try {
|
|
375
411
|
if (!context.process) {
|
|
376
412
|
job.postMessage('error', {id, error: 'Worker not set up to process on main thread'});
|
|
377
413
|
return;
|
|
378
414
|
}
|
|
379
|
-
const result = await context.process(
|
|
415
|
+
const result = await context.process(
|
|
416
|
+
input,
|
|
417
|
+
{...processOptions, signal: options.signal, onProgress: options.onProgress},
|
|
418
|
+
undefined,
|
|
419
|
+
payload.context || {}
|
|
420
|
+
);
|
|
380
421
|
job.postMessage('done', {id, result});
|
|
381
422
|
} catch (error) {
|
|
382
423
|
const message = error instanceof Error ? error.message : 'unknown error';
|
|
@@ -411,3 +452,17 @@ function onPreloadMessage(job: WorkerJob, type: WorkerMessageType, payload: Work
|
|
|
411
452
|
console.warn(`process-on-worker: unknown preload message ${type}`);
|
|
412
453
|
}
|
|
413
454
|
}
|
|
455
|
+
|
|
456
|
+
/** Delivers progress; callback failures terminate the job and settle its caller. */
|
|
457
|
+
function reportProgress(
|
|
458
|
+
job: WorkerJob,
|
|
459
|
+
options: ProcessOnWorkerOptions,
|
|
460
|
+
payload: WorkerMessagePayload
|
|
461
|
+
): void {
|
|
462
|
+
if (!job.isRunning || !payload.progress) return;
|
|
463
|
+
try {
|
|
464
|
+
options.onProgress?.(payload.progress);
|
|
465
|
+
} catch (error) {
|
|
466
|
+
job.abort(error);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
@@ -32,6 +32,7 @@ export default class WorkerJob {
|
|
|
32
32
|
* @param data any data structure, ideally consisting mostly of transferrable objects
|
|
33
33
|
*/
|
|
34
34
|
postMessage(type: WorkerMessageType, payload: WorkerMessagePayload): void {
|
|
35
|
+
if (!this.isRunning) return;
|
|
35
36
|
this.workerThread.postMessage({
|
|
36
37
|
source: 'loaders.gl', // Lets worker ignore unrelated messages
|
|
37
38
|
type,
|
|
@@ -69,9 +70,10 @@ export default class WorkerJob {
|
|
|
69
70
|
if (!this.isRunning) {
|
|
70
71
|
return;
|
|
71
72
|
}
|
|
72
|
-
const error = reason ?? createAbortError(`Worker job "${this.name}" was aborted`);
|
|
73
73
|
this.workerThread.destroy();
|
|
74
|
-
this.error(
|
|
74
|
+
this.error(
|
|
75
|
+
arguments.length ? reason : createAbortError(`Worker job "${this.name}" was aborted`)
|
|
76
|
+
);
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
|
|
@@ -50,6 +50,10 @@ type QueuedJob = {
|
|
|
50
50
|
onError: OnError;
|
|
51
51
|
onStart: (value: any) => void; // Resolve job start promise
|
|
52
52
|
onReject: (reason: unknown) => void; // Reject job start promise
|
|
53
|
+
/** Removes the queued cancellation listener when the job leaves the queue. */
|
|
54
|
+
removeAbortListener: () => void;
|
|
55
|
+
/** Cancels this queued or active job. */
|
|
56
|
+
signal?: AbortSignal;
|
|
53
57
|
};
|
|
54
58
|
|
|
55
59
|
/**
|
|
@@ -99,9 +103,13 @@ export default class WorkerPool {
|
|
|
99
103
|
*/
|
|
100
104
|
destroy(reason: unknown = new Error('Worker pool was destroyed')): void {
|
|
101
105
|
// Abort active jobs so callers receive an error when a development worker is invalidated.
|
|
106
|
+
this.isDestroyed = true;
|
|
102
107
|
this.activeJobs.forEach(job => job.abort(reason));
|
|
103
108
|
const queuedJobs = this.jobQueue.splice(0);
|
|
104
|
-
queuedJobs.forEach(queuedJob =>
|
|
109
|
+
queuedJobs.forEach(queuedJob => {
|
|
110
|
+
queuedJob.removeAbortListener();
|
|
111
|
+
queuedJob.onReject(reason);
|
|
112
|
+
});
|
|
105
113
|
this.idleQueue.forEach(worker => worker.destroy());
|
|
106
114
|
this.isDestroyed = true;
|
|
107
115
|
}
|
|
@@ -129,15 +137,37 @@ export default class WorkerPool {
|
|
|
129
137
|
async startJob(
|
|
130
138
|
name: string,
|
|
131
139
|
onMessage: OnMessage = (job, type, data) => job.done(data),
|
|
132
|
-
onError: OnError = (job, error) => job.error(error)
|
|
140
|
+
onError: OnError = (job, error) => job.error(error),
|
|
141
|
+
signal?: AbortSignal
|
|
133
142
|
): Promise<WorkerJob> {
|
|
143
|
+
if (signal?.aborted) {
|
|
144
|
+
throw signal.reason;
|
|
145
|
+
}
|
|
134
146
|
if (this.isDestroyed) {
|
|
135
147
|
return Promise.reject(new Error('Worker pool was destroyed'));
|
|
136
148
|
}
|
|
137
149
|
// Promise resolves when thread starts working on this job
|
|
138
150
|
const startPromise = new Promise<WorkerJob>((onStart, onReject) => {
|
|
139
151
|
// Promise resolves when thread completes or fails working on this job
|
|
140
|
-
|
|
152
|
+
const abortQueuedJob = (): void => {
|
|
153
|
+
const index = this.jobQueue.indexOf(queuedJob);
|
|
154
|
+
if (index >= 0) {
|
|
155
|
+
this.jobQueue.splice(index, 1);
|
|
156
|
+
queuedJob.removeAbortListener();
|
|
157
|
+
onReject(signal?.reason);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const queuedJob: QueuedJob = {
|
|
161
|
+
name,
|
|
162
|
+
onMessage,
|
|
163
|
+
onError,
|
|
164
|
+
onStart,
|
|
165
|
+
onReject,
|
|
166
|
+
signal,
|
|
167
|
+
removeAbortListener: () => signal?.removeEventListener('abort', abortQueuedJob)
|
|
168
|
+
};
|
|
169
|
+
this.jobQueue.push(queuedJob);
|
|
170
|
+
signal?.addEventListener('abort', abortQueuedJob, {once: true});
|
|
141
171
|
return this;
|
|
142
172
|
});
|
|
143
173
|
this._startQueuedJob(); // eslint-disable-line @typescript-eslint/no-floating-promises
|
|
@@ -155,7 +185,16 @@ export default class WorkerPool {
|
|
|
155
185
|
return;
|
|
156
186
|
}
|
|
157
187
|
|
|
158
|
-
|
|
188
|
+
let workerThread: WorkerThread | null;
|
|
189
|
+
try {
|
|
190
|
+
workerThread = this._getAvailableWorker();
|
|
191
|
+
} catch (error) {
|
|
192
|
+
const queuedJob = this.jobQueue.shift();
|
|
193
|
+
queuedJob?.removeAbortListener();
|
|
194
|
+
queuedJob?.onReject(error);
|
|
195
|
+
void this._startQueuedJob();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
159
198
|
if (!workerThread) {
|
|
160
199
|
return;
|
|
161
200
|
}
|
|
@@ -174,11 +213,17 @@ export default class WorkerPool {
|
|
|
174
213
|
|
|
175
214
|
// Create a worker job to let the app access thread and manage job completion
|
|
176
215
|
const job = new WorkerJob(queuedJob.name, workerThread);
|
|
216
|
+
queuedJob.removeAbortListener();
|
|
217
|
+
const abortJob = (): void => job.abort(queuedJob.signal?.reason);
|
|
218
|
+
queuedJob.signal?.addEventListener('abort', abortJob, {once: true});
|
|
219
|
+
if (queuedJob.signal?.aborted) abortJob();
|
|
177
220
|
this.activeJobs.add(job);
|
|
178
221
|
workerThread.ref();
|
|
179
222
|
|
|
180
223
|
// Set the worker thread's message handlers
|
|
181
|
-
workerThread.onMessage = data =>
|
|
224
|
+
workerThread.onMessage = data => {
|
|
225
|
+
if (job.isRunning) queuedJob.onMessage(job, data.type, data.payload);
|
|
226
|
+
};
|
|
182
227
|
workerThread.onError = error => queuedJob.onError(job, error);
|
|
183
228
|
|
|
184
229
|
// Resolve the start promise so that the app can start sending messages to worker
|
|
@@ -191,9 +236,12 @@ export default class WorkerPool {
|
|
|
191
236
|
// The job result promise carries worker errors back to the caller; do not duplicate-log
|
|
192
237
|
// handled rejections here.
|
|
193
238
|
} finally {
|
|
239
|
+
queuedJob.signal?.removeEventListener('abort', abortJob);
|
|
194
240
|
this.activeJobs.delete(job);
|
|
195
241
|
this.returnWorkerToQueue(workerThread);
|
|
196
242
|
}
|
|
243
|
+
} else {
|
|
244
|
+
this.returnWorkerToQueue(workerThread);
|
|
197
245
|
}
|
|
198
246
|
}
|
|
199
247
|
|
|
@@ -244,13 +292,18 @@ export default class WorkerPool {
|
|
|
244
292
|
if (this.count < this._getMaxConcurrency()) {
|
|
245
293
|
this.count++;
|
|
246
294
|
const name = `${this.name.toLowerCase()} (#${this.count} of ${this.maxConcurrency})`;
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
295
|
+
try {
|
|
296
|
+
return new WorkerThread({
|
|
297
|
+
name,
|
|
298
|
+
source: this.source,
|
|
299
|
+
url: this.url,
|
|
300
|
+
getUrl: this.getUrl,
|
|
301
|
+
loadWorker: this.loadWorker
|
|
302
|
+
});
|
|
303
|
+
} catch (error) {
|
|
304
|
+
this.count--;
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
254
307
|
}
|
|
255
308
|
|
|
256
309
|
// No worker available, have to wait
|
|
@@ -2,13 +2,29 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
*/
|
|
5
|
+
/** Progress measured independently within a named phase. */
|
|
6
|
+
export type WorkerProgress = {
|
|
7
|
+
/** Operation being measured. */
|
|
8
|
+
phase: string;
|
|
9
|
+
/** Work completed in this phase. */
|
|
10
|
+
completed: number;
|
|
11
|
+
/** Expected work, when known. */
|
|
12
|
+
total?: number;
|
|
13
|
+
/** Measurement unit, such as bytes, rows, or tasks. */
|
|
14
|
+
unit: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** Shared worker execution options. */
|
|
8
18
|
export type WorkerOptions = {
|
|
19
|
+
/** Cancels queued or running work. */
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
/** Receives phase progress. Throwing aborts the job with that error. */
|
|
22
|
+
onProgress?: (progress: WorkerProgress) => void;
|
|
9
23
|
// Worker farm options
|
|
10
24
|
CDN?: string | null;
|
|
11
25
|
worker?: boolean;
|
|
26
|
+
/** Declares batch-protocol support for a custom worker, or disables batch offloading. */
|
|
27
|
+
workerInBatches?: boolean;
|
|
12
28
|
maxConcurrency?: number;
|
|
13
29
|
maxMobileConcurrency?: number;
|
|
14
30
|
reuseWorkers?: boolean;
|
|
@@ -26,6 +42,8 @@ export type WorkerOptions = {
|
|
|
26
42
|
export type LoadWorker = () => Worker | null;
|
|
27
43
|
|
|
28
44
|
export type WorkerContext = {
|
|
45
|
+
/** Reports progress from the active worker job. */
|
|
46
|
+
reportProgress?: (progress: WorkerProgress) => void;
|
|
29
47
|
process?: Process;
|
|
30
48
|
processInBatches?: ProcessInBatches;
|
|
31
49
|
};
|
|
@@ -58,6 +76,8 @@ export type WorkerObject = {
|
|
|
58
76
|
module: string;
|
|
59
77
|
version: string;
|
|
60
78
|
worker?: string | boolean;
|
|
79
|
+
/** Whether the built-in worker accepts stateful batch requests. */
|
|
80
|
+
workerInBatches?: boolean;
|
|
61
81
|
/** Browser worker filename when it differs from the worker id. */
|
|
62
82
|
workerFile?: string;
|
|
63
83
|
/** Creates a built-in browser worker, typically using `type: 'module'`. */
|
|
@@ -93,6 +113,7 @@ export type WorkerObject = {
|
|
|
93
113
|
<= error
|
|
94
114
|
*/
|
|
95
115
|
export type WorkerMessageType =
|
|
116
|
+
| 'progress'
|
|
96
117
|
| 'preload'
|
|
97
118
|
| 'process'
|
|
98
119
|
| 'done'
|
|
@@ -105,6 +126,8 @@ export type WorkerMessageType =
|
|
|
105
126
|
| 'output-ack';
|
|
106
127
|
|
|
107
128
|
export type WorkerMessagePayload = {
|
|
129
|
+
/** Phase progress from the worker. */
|
|
130
|
+
progress?: WorkerProgress;
|
|
108
131
|
id?: number;
|
|
109
132
|
options?: {[key: string]: any};
|
|
110
133
|
context?: WorkerJobContext;
|