Skip to content

Commit

Permalink
Use coincident if crossOriginIsolated, comlink otherwise (#108)
Browse files Browse the repository at this point in the history
* Add comlink dependency

* Use either coincident or comlink

* typos

* cleanup

* fixes

* ready

* fixes

* Fix mounting logic

* Fix communication with comlink worker

* Linting

* Back to a working coincident setup

* Add comment as per review

---------

Co-authored-by: martinRenou <martin.renou@gmail.com>
  • Loading branch information
jtpio and martinRenou authored Sep 9, 2024
1 parent c5757c5 commit 44b9acb
Show file tree
Hide file tree
Showing 10 changed files with 468 additions and 220 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,6 @@ dmypy.json
experiment.sh
env.yml

# Jupyterlite cache
# JupyterLite
.jupyterlite.doit.db
_output
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@
"@jupyterlite/server": "^0.2.0 || ^0.3.0 || ^0.4.0",
"@lumino/coreutils": "^2",
"@lumino/signaling": "^2",
"coincident": "^1.2.3"
"coincident": "^1.2.3",
"comlink": "^4.4.1"
},
"devDependencies": {
"@jupyterlab/builder": "^4.1.0",
Expand Down
80 changes: 80 additions & 0 deletions src/coincident.worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) Thorsten Beier
// Copyright (c) JupyterLite Contributors
// Distributed under the terms of the Modified BSD License.

import coincident from 'coincident';

import {
ContentsAPI,
DriveFS,
TDriveRequest,
TDriveMethod,
TDriveResponse
} from '@jupyterlite/contents';

import { IXeusWorkerKernel } from './tokens';
import { XeusRemoteKernel } from './worker';

const workerAPI = coincident(self) as IXeusWorkerKernel;

/**
* An Emscripten-compatible synchronous Contents API using shared array buffers.
*/
export class SharedBufferContentsAPI extends ContentsAPI {
request<T extends TDriveMethod>(data: TDriveRequest<T>): TDriveResponse<T> {
return workerAPI.processDriveRequest(data);
}
}

/**
* A custom drive implementation which uses shared array buffers (via coincident) if available
*/
class XeusDriveFS extends DriveFS {
createAPI(options: DriveFS.IOptions): ContentsAPI {
return new SharedBufferContentsAPI(
options.driveName,
options.mountpoint,
options.FS,
options.ERRNO_CODES
);
}
}

export class XeusCoincidentKernel extends XeusRemoteKernel {
/**
* Setup custom Emscripten FileSystem
*/
async mount(
driveName: string,
mountpoint: string,
baseUrl: string
): Promise<void> {
const { FS, PATH, ERRNO_CODES } = globalThis.Module;

if (!FS) {
return;
}

const drive = new XeusDriveFS({
FS,
PATH,
ERRNO_CODES,
baseUrl,
driveName,
mountpoint
});

FS.mkdir(mountpoint);
FS.mount(drive, {}, mountpoint);
FS.chdir(mountpoint);
}
}

const worker = new XeusCoincidentKernel();

workerAPI.initialize = worker.initialize.bind(worker);
workerAPI.mount = worker.mount.bind(worker);
workerAPI.ready = worker.ready.bind(worker);
workerAPI.cd = worker.cd.bind(worker);
workerAPI.isDir = worker.isDir.bind(worker);
workerAPI.processMessage = worker.processMessage.bind(worker);
65 changes: 65 additions & 0 deletions src/comlink.worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

/**
* A WebWorker entrypoint that uses comlink to handle postMessage details
*/

import { expose } from 'comlink';

import {
ContentsAPI,
DriveFS,
ServiceWorkerContentsAPI
} from '@jupyterlite/contents';

import { XeusRemoteKernel } from './worker';

/**
* A custom drive implementation which uses the service worker
*/
class XeusDriveFS extends DriveFS {
createAPI(options: DriveFS.IOptions): ContentsAPI {
return new ServiceWorkerContentsAPI(
options.baseUrl,
options.driveName,
options.mountpoint,
options.FS,
options.ERRNO_CODES
);
}
}

export class XeusComlinkKernel extends XeusRemoteKernel {
/**
* Setup custom Emscripten FileSystem
*/
async mount(
driveName: string,
mountpoint: string,
baseUrl: string
): Promise<void> {
const { FS, PATH, ERRNO_CODES } = globalThis.Module;

if (!FS) {
return;
}

const drive = new XeusDriveFS({
FS,
PATH,
ERRNO_CODES,
baseUrl,
driveName,
mountpoint
});

FS.mkdir(mountpoint);
FS.mount(drive, {}, mountpoint);
FS.chdir(mountpoint);
}
}

const worker = new XeusComlinkKernel();

expose(worker);
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const plugins = kernel_list.map((kernel): JupyterLiteServerPlugin<void> => {
...options,
contentsManager,
mountDrive,
kernelspec
kernelSpec: kernelspec
});
}
});
Expand Down
89 changes: 89 additions & 0 deletions src/tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

/**
* Definitions for the Xeus kernel.
*/

import {
TDriveMethod,
TDriveRequest,
TDriveResponse
} from '@jupyterlite/contents';

import { IWorkerKernel } from '@jupyterlite/kernel';

/**
* An interface for Xeus workers.
*/
export interface IXeusWorkerKernel extends IWorkerKernel {
/**
* Handle any lazy initialization activities.
*/
initialize(options: IXeusWorkerKernel.IOptions): Promise<void>;

/**
* Process drive request
* @param data
*/
processDriveRequest<T extends TDriveMethod>(
data: TDriveRequest<T>
): TDriveResponse<T>;

/**
* Process a message sent from the main thread to the worker.
* @param msg
*/
processMessage(msg: any): void;

/**
* Process worker message
* @param msg
*/
processWorkerMessage(msg: any): void;

/**
* Register a callback for handling messages from the worker.
*/
registerCallback(callback: (msg: any) => void): void;

/**
* Whether the kernel is ready.
* @returns a promise that resolves when the kernel is ready.
*/
ready(): Promise<void>;

/**
* Mount a drive
* @param driveName The name of the drive
* @param mountpoint The mountpoint of the drive
* @param baseUrl The base URL of the server
*/
mount(driveName: string, mountpoint: string, baseUrl: string): Promise<void>;

/**
* Change the current working directory
* @param path The path to change to
*/
cd(path: string): Promise<void>;

/**
* Check if a path is a directory
* @param path The path to check
*/
isDir(path: string): Promise<boolean>;
}

/**
* An namespace for Xeus workers.
*/
export namespace IXeusWorkerKernel {
/**
* Initialization options for a worker.
*/
export interface IOptions extends IWorkerKernel.IOptions {
baseUrl: string;
kernelSpec: any;
mountDrive: boolean;
}
}
Loading

0 comments on commit 44b9acb

Please sign in to comment.