Skip to content

Commit

Permalink
FileSystem calls over Atomics.wait instead of service worker (#87)
Browse files Browse the repository at this point in the history
* File system calls over Atomics.wait if available

* Update to latest jupyterlite

* Lazy creation of the DriveContentsProcessor

This makes sure to have a working setup with jupyterlite<0.4.0 when
using service worker
  • Loading branch information
martinRenou authored Jun 5, 2024
1 parent ccfacce commit a80ff1b
Show file tree
Hide file tree
Showing 5 changed files with 1,074 additions and 1,056 deletions.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@
"dependencies": {
"@jupyterlab/coreutils": "^6",
"@jupyterlab/services": "^7",
"@jupyterlite/contents": "^0.2.0 || ^0.3.0 || ^0.4.0-alpha.0",
"@jupyterlite/kernel": "^0.2.0 || ^0.3.0 || ^0.4.0-alpha.0",
"@jupyterlite/server": "^0.2.0 || ^0.3.0 || ^0.4.0-alpha.0",
"@jupyterlite/contents": "^0.2.0 || ^0.3.0 || ^0.4.0-alpha.3",
"@jupyterlite/kernel": "^0.2.0 || ^0.3.0 || ^0.4.0-alpha.3",
"@jupyterlite/server": "^0.2.0 || ^0.3.0 || ^0.4.0-alpha.3",
"@lumino/coreutils": "^2",
"@lumino/signaling": "^2",
"comlink": "^4.3.1"
"coincident": "^1.2.3"
},
"devDependencies": {
"@jupyterlab/builder": "^4.1.0",
Expand Down
6 changes: 5 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,14 @@ const plugins = kernel_list.map((kernel): JupyterLiteServerPlugin<void> => {
);
}

const contentsManager = app.serviceManager.contents;

kernelspecs.register({
spec: kernelspec,
create: async (options: IKernel.IOptions): Promise<IKernel> => {
const mountDrive = !!(
serviceWorker?.enabled && broadcastChannel?.enabled
(serviceWorker?.enabled && broadcastChannel?.enabled) ||
crossOriginIsolated
);

if (mountDrive) {
Expand All @@ -71,6 +74,7 @@ const plugins = kernel_list.map((kernel): JupyterLiteServerPlugin<void> => {

return new WebWorkerKernel({
...options,
contentsManager,
mountDrive,
kernelspec
});
Expand Down
70 changes: 50 additions & 20 deletions src/web_worker_kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@
// Copyright (c) JupyterLite Contributors
// Distributed under the terms of the Modified BSD License.

import { wrap } from 'comlink';
import type { Remote } from 'comlink';
import coincident from 'coincident';

import { ISignal, Signal } from '@lumino/signaling';
import { PromiseDelegate } from '@lumino/coreutils';

import { PageConfig } from '@jupyterlab/coreutils';
import { KernelMessage } from '@jupyterlab/services';
import { Contents, KernelMessage } from '@jupyterlab/services';

import { IKernel } from '@jupyterlite/kernel';
import {
DriveContentsProcessor,
TDriveMethod,
TDriveRequest
} from '@jupyterlite/contents';

interface IXeusKernel {
initialize(kernel_spec: any, base_url: string): Promise<void>;
Expand All @@ -25,6 +29,8 @@ interface IXeusKernel {
isDir(path: string): Promise<boolean>;

processMessage(msg: any): Promise<void>;

processDriveRequest(data: any): void;
}

export class WebWorkerKernel implements IKernel {
Expand All @@ -34,21 +40,23 @@ export class WebWorkerKernel implements IKernel {
* @param options The instantiation options for a new WebWorkerKernel
*/
constructor(options: WebWorkerKernel.IOptions) {
const { id, name, sendMessage, location } = options;
const { id, name, sendMessage, location, kernelspec, contentsManager } =
options;
this._id = id;
this._name = name;
this._location = location;
this._kernelspec = options.kernelspec;
this._kernelspec = kernelspec;
this._contentsManager = contentsManager;
this._sendMessage = sendMessage;
this._worker = new Worker(new URL('./worker.js', import.meta.url), {
type: 'module'
});

this._worker.onmessage = e => {
this._processWorkerMessage(e.data);
};
this._worker.onmessage = this._processWorkerMessage.bind(this);

this._remote = wrap(this._worker);
this._remote = coincident(this._worker) as IXeusKernel;

this.setupFilesystemAPIs();

this._remote.initialize(this._kernelspec, PageConfig.getBaseUrl());

Expand Down Expand Up @@ -102,18 +110,18 @@ export class WebWorkerKernel implements IKernel {
* @param msg The worker message to process.
*/
private _processWorkerMessage(msg: any): void {
if (!msg.header) {
if (!msg.data.header) {
return;
}

msg.header.session = this._parentHeader?.session ?? '';
msg.session = this._parentHeader?.session ?? '';
this._sendMessage(msg);
msg.data.header.session = this._parentHeader?.session ?? '';
msg.data.session = this._parentHeader?.session ?? '';
this._sendMessage(msg.data);

// resolve promise
if (
msg.header.msg_type === 'status' &&
msg.content.execution_state === 'idle'
msg.data.header.msg_type === 'status' &&
msg.data.content.execution_state === 'idle'
) {
this._executeDelegate.resolve();
}
Expand Down Expand Up @@ -168,6 +176,27 @@ export class WebWorkerKernel implements IKernel {
return this._name;
}

private setupFilesystemAPIs() {
this._remote.processDriveRequest = async <T extends TDriveMethod>(
data: TDriveRequest<T>
) => {
if (!DriveContentsProcessor) {
console.error(
'File system calls over Atomics.wait is only supported with jupyterlite>=0.4.0a3'
);
return;
}

if (this._contentsProcessor === undefined) {
this._contentsProcessor = new DriveContentsProcessor({
contentsManager: this._contentsManager
});
}

return await this._contentsProcessor.processDriveRequest(data);
};
}

private async initFileSystem(options: WebWorkerKernel.IOptions) {
let driveName: string;
let localPath: string;
Expand All @@ -183,13 +212,11 @@ export class WebWorkerKernel implements IKernel {

await this._remote.ready();

if (options.mountDrive) {
await this._remote.mount(driveName, '/drive', PageConfig.getBaseUrl());
}
await this._remote.mount(driveName, '/drive', PageConfig.getBaseUrl());

if (await this._remote.isDir('/files')) {
await this._remote.cd('/files');
} else if (options.mountDrive) {
} else {
await this._remote.cd(localPath);
}
}
Expand All @@ -198,7 +225,9 @@ export class WebWorkerKernel implements IKernel {
private _id: string;
private _name: string;
private _location: string;
private _remote: Remote<IXeusKernel>;
private _contentsManager: Contents.IManager;
private _contentsProcessor: DriveContentsProcessor | undefined = undefined;
private _remote: IXeusKernel;
private _isDisposed = false;
private _disposed = new Signal<this, void>(this);
private _worker: Worker;
Expand All @@ -218,6 +247,7 @@ export namespace WebWorkerKernel {
* The instantiation options for a Pyodide kernel
*/
export interface IOptions extends IKernel.IOptions {
contentsManager: Contents.IManager;
mountDrive: boolean;
kernelspec: any;
}
Expand Down
Loading

0 comments on commit a80ff1b

Please sign in to comment.