Skip to content

Experiment: SharedArrayBuffer filesystem #2384

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 19 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions packages/php-wasm/universal/src/lib/php-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,14 @@ export class PHPWorker implements LimitedPHPApi {

/** @inheritDoc @php-wasm/universal!/PHP.run */
async run(request: PHPRunOptions): Promise<PHPResponse> {
const { php, reap } = await _private
.get(this)!
.requestHandler!.processManager.acquirePHPInstance();
const php = _private.get(this)!.php!;
// const { php, reap } = await _private
// .get(this)!
// .requestHandler!.processManager.acquirePHPInstance();
try {
return await php.run(request);
} finally {
reap();
// reap();
}
}

Expand Down
14 changes: 14 additions & 0 deletions packages/php-wasm/universal/src/lib/php.ts
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,20 @@ export class PHP implements Disposable {
return FSHelpers.mv(this[__private__dont__use].FS, fromPath, toPath);
}

/**
* Discuss: should we keep this? Or are the argument semantics too confusing? But it's
* the same as for mv, isn't it?
*
* Also, should it be copy(fromPath, toPath, { recursive: true }) like rmdir?
*/
copyRecursive(fromPath: string, toPath: string) {
return FSHelpers.copyRecursive(
this[__private__dont__use].FS,
fromPath,
toPath
);
}

/**
* Removes a directory from the PHP filesystem.
*
Expand Down
5 changes: 5 additions & 0 deletions packages/php-wasm/web-service-worker/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ export async function convertFetchEventToPHPRequest(event: FetchEvent) {
// X-frame-options gets in a way when PHP is
// being displayed in an iframe.
delete phpResponse.headers['x-frame-options'];

// When using SharedArrayBuffers, every response needs to have
// these headers.
phpResponse.headers['Cross-Origin-Opener-Policy'] = 'same-origin';
phpResponse.headers['Cross-Origin-Embedder-Policy'] = 'credentialless';
} catch (e) {
console.error(e, { url: url.toString() });
throw e;
Expand Down
6,992 changes: 6,920 additions & 72 deletions packages/php-wasm/web/public/php/jspi/php_8_0.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion packages/playground/remote/service-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,6 @@ self.addEventListener('fetch', (event) => {
}

const url = new URL(event.request.url);

// Don't handle requests to the service worker script itself.
if (url.pathname.startsWith(self.location.pathname)) {
return;
Expand Down
45 changes: 45 additions & 0 deletions packages/playground/remote/src/lib/boot-playground-remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,23 @@ import moduleWorkerUrl from './worker-thread?worker&url';

export const workerUrl: string = new URL(moduleWorkerUrl, origin) + '';

// @ts-ignore
import experimentalSABFSModuleWorkerUrl from './worker-thread-shared?worker&url';
export const experimentalSABFSWorkerUrl: string =
new URL(experimentalSABFSModuleWorkerUrl, origin) + '';

// @ts-ignore
import serviceWorkerPath from '../../service-worker.ts?worker&url';
import { FilesystemOperation } from '@php-wasm/fs-journal';
import { setupFetchNetworkTransport } from './setup-fetch-network-transport';
import { logger } from '@php-wasm/logger';
import { PhpWasmError } from '@php-wasm/util';
import { responseTo } from '@php-wasm/web-service-worker';
import {
SharedFSBuffers,
createSharedFSBuffers,
} from './shared-array-buffer-fs';
import type { ExperimentalWorkerEndpoint } from './worker-thread-shared';
export const serviceWorkerUrl = new URL(serviceWorkerPath, origin);

// Prevent Vite from hot-reloading this file – it would
Expand Down Expand Up @@ -422,3 +432,38 @@ function assertNotInfiniteLoadingLoop() {
}
(window as any).IS_WASM_WORDPRESS = true;
}

async function spawnSharedFSPhpWorker(sharedBuffers: SharedFSBuffers) {
const experimentalPhpWorkerApi = await spawnPHPWorkerThread(
experimentalSABFSWorkerUrl
);
const phpWorkerApi = consumeAPI<ExperimentalWorkerEndpoint>(
experimentalPhpWorkerApi
);
await phpWorkerApi.isConnected();
await phpWorkerApi.boot({
sharedBuffers,
});
await phpWorkerApi.isReady();
return phpWorkerApi;
}

const buffers = createSharedFSBuffers();
const worker1 = await spawnSharedFSPhpWorker(buffers);
const worker2 = await spawnSharedFSPhpWorker(buffers);

// We can actually write and read files!
console.log('writing file');
await worker1.writeFile('/experimental-sabfs/test.txt', 'Hello, world!');
console.log('reading file worker 1');
console.log(await worker1.readFileAsText('/experimental-sabfs/test.txt'));
await worker1.logHeader('worker1');
await worker1.logDirState('worker1');
await worker1.dumpDir('worker1');
await worker2.logHeader('worker2');
await worker2.logDirState('worker2');
await worker2.dumpDir('worker2');

console.log('reading file worker 2');
console.log(await worker2.readFileAsText('/experimental-sabfs/test.txt'));
// @TODO solution: We need to use a SharedArrayBuffer to store the top-level FS metadata
Loading