Skip to content

Commit

Permalink
Reject init() promise if webR worker thread fails (#466)
Browse files Browse the repository at this point in the history
* Reject init promise if webR worker thread fails

* Inline worker error messages
  • Loading branch information
georgestagg authored Aug 5, 2024
1 parent bb5ab52 commit 5db0762
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 9 deletions.
17 changes: 17 additions & 0 deletions src/tests/webR/error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,23 @@ test('Caught WebRError has the expected error name property', async () => {
expect(error).toHaveProperty('message', expect.stringContaining('unexpected end of input'));
});

test('An error is thrown if starting the webR worker fails', async () => {
let error: Error | undefined;

const tempR = new WebR({
baseUrl: '../non/existent/path/',
});

await tempR.init().catch((e) => {
if (e instanceof WebRError) {
error = e;
}
});

expect(error).toHaveProperty('name', 'WebRWorkerError');
expect(error).toHaveProperty('message', expect.stringContaining('An error occurred'));
});

afterAll(() => {
return webR.close();
});
19 changes: 16 additions & 3 deletions src/webR/chan/channel-postmessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Endpoint } from './task-common';
import { ChannelType } from './channel-common';
import { WebROptions } from '../webr-main';
import { ChannelMain } from './channel';
import { WebRChannelError } from '../error';
import { WebRChannelError, WebRWorkerError } from '../error';

import { IN_NODE } from '../compat';
import type { Worker as NodeWorker } from 'worker_threads';
Expand All @@ -18,11 +18,14 @@ export class PostMessageChannelMain extends ChannelMain {

initialised: Promise<unknown>;
resolve: (_?: unknown) => void;
reject: (message: string | Error) => void;
close: () => void = () => { return; };
#worker?: Worker;

constructor(config: Required<WebROptions>) {
super();
({ resolve: this.resolve, reject: this.reject, promise: this.initialised } = promiseHandles());

const initWorker = (worker: Worker) => {
this.#worker = worker;
this.#handleEventsFromWorker(worker);
Expand All @@ -42,8 +45,6 @@ export class PostMessageChannelMain extends ChannelMain {
const worker = new Worker(`${config.baseUrl}webr-worker.js`);
initWorker(worker);
}

({ resolve: this.resolve, promise: this.initialised } = promiseHandles());
}

interrupt() {
Expand All @@ -55,9 +56,21 @@ export class PostMessageChannelMain extends ChannelMain {
(worker as unknown as NodeWorker).on('message', (message: Message) => {
void this.#onMessageFromWorker(worker, message);
});
(worker as unknown as NodeWorker).on('error', (ev: Event) => {
console.error(ev);
this.reject(new WebRWorkerError(
"An error occurred initialising the webR PostMessageChannel worker."
));
});
} else {
worker.onmessage = (ev: MessageEvent) =>
this.#onMessageFromWorker(worker, ev.data as Message);
worker.onerror = (ev) => {
console.error(ev);
this.reject(new WebRWorkerError(
"An error occurred initialising the webR PostMessageChannel worker."
));
};
}
}

Expand Down
19 changes: 16 additions & 3 deletions src/webR/chan/channel-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Endpoint } from './task-common';
import { ChannelMain, ChannelWorker } from './channel';
import { ChannelType } from './channel-common';
import { WebROptions } from '../webr-main';
import { WebRChannelError } from '../error';
import { WebRChannelError, WebRWorkerError } from '../error';

import { IN_NODE } from '../compat';
import type { Worker as NodeWorker } from 'worker_threads';
Expand All @@ -25,6 +25,7 @@ export class ServiceWorkerChannelMain extends ChannelMain {
initialised: Promise<unknown>;

resolve: (_?: unknown) => void;
reject: (message: string | Error) => void;
close = () => { return; };

#syncMessageCache = new Map<string, Message>();
Expand All @@ -33,6 +34,8 @@ export class ServiceWorkerChannelMain extends ChannelMain {

constructor(config: Required<WebROptions>) {
super();
({ resolve: this.resolve, reject: this.reject, promise: this.initialised } = promiseHandles());

console.warn(
"The ServiceWorker communication channel is deprecated and will be removed in a future version of webR. " +
"Consider using the PostMessage channel instead. If blocking input is required (for example, `browser()`) " +
Expand Down Expand Up @@ -69,8 +72,6 @@ export class ServiceWorkerChannelMain extends ChannelMain {
const worker = new Worker(`${config.serviceWorkerUrl}webr-worker.js`);
initWorker(worker);
}

({ resolve: this.resolve, promise: this.initialised } = promiseHandles());
}

activeRegistration(): ServiceWorker {
Expand Down Expand Up @@ -154,9 +155,21 @@ export class ServiceWorkerChannelMain extends ChannelMain {
(worker as unknown as NodeWorker).on('message', (message: Message) => {
this.#onMessageFromWorker(worker, message);
});
(worker as unknown as NodeWorker).on('error', (ev: Event) => {
console.error(ev);
this.reject(new WebRWorkerError(
"An error occurred initialising the webR ServiceWorkerChannel worker."
));
});
} else {
worker.onmessage = (ev: MessageEvent) =>
this.#onMessageFromWorker(worker, ev.data as Message);
worker.onerror = (ev) => {
console.error(ev);
this.reject(new WebRWorkerError(
"An error occurred initialising the webR ServiceWorkerChannel worker."
));
};
}
}

Expand Down
19 changes: 16 additions & 3 deletions src/webR/chan/channel-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { syncResponse } from './task-main';
import { ChannelMain, ChannelWorker } from './channel';
import { ChannelType } from './channel-common';
import { WebROptions } from '../webr-main';
import { WebRChannelError } from '../error';
import { WebRChannelError, WebRWorkerError } from '../error';

import { IN_NODE } from '../compat';
import type { Worker as NodeWorker } from 'worker_threads';
Expand All @@ -20,10 +20,13 @@ export class SharedBufferChannelMain extends ChannelMain {

initialised: Promise<unknown>;
resolve: (_?: unknown) => void;
reject: (message: string | Error) => void;
close = () => { return; };

constructor(config: Required<WebROptions>) {
super();
({ resolve: this.resolve, reject: this.reject, promise: this.initialised } = promiseHandles());

const initWorker = (worker: Worker) => {
this.#handleEventsFromWorker(worker);
this.close = () => {
Expand All @@ -45,8 +48,6 @@ export class SharedBufferChannelMain extends ChannelMain {
const worker = new Worker(`${config.baseUrl}webr-worker.js`);
initWorker(worker);
}

({ resolve: this.resolve, promise: this.initialised } = promiseHandles());
}

interrupt() {
Expand All @@ -62,9 +63,21 @@ export class SharedBufferChannelMain extends ChannelMain {
(worker as unknown as NodeWorker).on('message', (message: Message) => {
void this.#onMessageFromWorker(worker, message);
});
(worker as unknown as NodeWorker).on('error', (ev: Event) => {
console.error(ev);
this.reject(new WebRWorkerError(
"An error occurred initialising the webR SharedBufferChannel worker."
));
});
} else {
worker.onmessage = (ev: MessageEvent) =>
this.#onMessageFromWorker(worker, ev.data as Message);
worker.onerror = (ev) => {
console.error(ev);
this.reject(new WebRWorkerError(
"An error occurred initialising the webR SharedBufferChannel worker."
));
};
}
}

Expand Down

0 comments on commit 5db0762

Please sign in to comment.