Skip to content

Commit

Permalink
Instantiate Emscripten Runtime for python workers earlier.
Browse files Browse the repository at this point in the history
Move ownership of metrics and limitEnforcer to the api type.

Currently ownership is shared even though the Isolate class encapsulates
the api class.
Moving complete ownership to the underlying api class allows the isolate
class to be constructed in a different scope to the api class.
This is useful for preinitialization of the api class before a request
has come in.

Add updateConfiguration function to jsg Isolates

This can be used to update the given configuration at runtime. Note that
while some jsg structs are lazily using the configuration, others can
use it at construction and will have the original configuration value.
  • Loading branch information
danlapid committed Oct 30, 2024
1 parent e347c69 commit 05e7568
Show file tree
Hide file tree
Showing 19 changed files with 458 additions and 53 deletions.
76 changes: 65 additions & 11 deletions src/pyodide/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,6 @@ INTERNAL_MODULES = glob(
[
"internal/*.ts",
"internal/topLevelEntropy/*.ts",
# The pool directory is only needed by typescript, it shouldn't be used at runtime.
"internal/pool/*.ts",
"types/*.ts",
"types/*/*.ts",
],
Expand All @@ -217,21 +215,14 @@ wd_ts_bundle(
name = "pyodide",
eslintrc_json = "eslint.config.mjs",
import_name = "pyodide",
internal_data_modules = ["generated/python_stdlib.zip"] + INTERNAL_DATA_MODULES,
internal_data_modules = INTERNAL_DATA_MODULES,
internal_json_modules = [
"generated/pyodide-lock.json",
"generated/pyodide-bucket.json",
],
internal_modules = [
"generated/emscriptenSetup.js",
] + INTERNAL_MODULES,
internal_wasm_modules = ["generated/pyodide.asm.wasm"],
internal_modules = INTERNAL_MODULES,
js_deps = [
"generated/emscriptenSetup",
"pyodide.asm.js@rule",
"pyodide.asm.wasm@rule",
"pyodide-lock.js@rule",
"python_stdlib.zip@rule",
"pyodide-bucket.json@rule",
],
lint = False,
Expand Down Expand Up @@ -286,3 +277,66 @@ genrule(
tools = ["@capnp-cpp//src/capnp:capnp_tool"],
visibility = ["//visibility:public"],
)

copy_file(
name = "emscripten_setup_capnp_file",
src = "emscripten_setup.capnp",
out = "generated/emscripten_setup.capnp",
)

genrule(
name = "emscripten_setup.capnp.bin@rule",
srcs = [
":generated/emscripten_setup.capnp",
":generated/emscriptenSetup.js",
":generated/python_stdlib.zip",
":generated/pyodide.asm.wasm",
],
outs = ["emscripten_setup.capnp.bin"],
cmd = " ".join([
"$(execpath @capnp-cpp//src/capnp:capnp_tool)",
"eval",
"$(location generated/emscripten_setup.capnp)",
"emscriptenSetup",
"-o binary",
"> $@",
]),
tools = ["@capnp-cpp//src/capnp:capnp_tool"],
visibility = ["//visibility:public"],
)

capnp_embed(
name = "emscripten_setup_capnp_file_embed",
src = "generated/emscripten_setup.capnp",
deps = ["emscripten_setup_capnp_file"],
)

capnp_embed(
name = "emscripten_setup_js_file_embed",
src = "generated/emscriptenSetup.js",
deps = ["generated/emscriptenSetup"],
)

capnp_embed(
name = "python_stdlib_zip_file_embed",
src = "generated/python_stdlib.zip",
deps = ["python_stdlib.zip@rule"],
)

capnp_embed(
name = "pyodide_asm_wasm_file_embed",
src = "generated/pyodide.asm.wasm",
deps = ["pyodide.asm.wasm@rule"],
)

cc_capnp_library(
name = "emscripten_setup_capnp",
srcs = ["generated/emscripten_setup.capnp"],
visibility = ["//visibility:public"],
deps = [
":emscripten_setup_capnp_file_embed",
":emscripten_setup_js_file_embed",
":pyodide_asm_wasm_file_embed",
":python_stdlib_zip_file_embed",
],
)
13 changes: 13 additions & 0 deletions src/pyodide/emscripten_setup.capnp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@0xc00ad00cc650fb45;

struct EmscriptenSetup {
code @0 :Text;
pyodideAsmWasm @1 :Data;
pythonStdlibZip @2 :Data;
}

const emscriptenSetup :EmscriptenSetup = (
code = embed "emscriptenSetup.js",
pyodideAsmWasm = embed "pyodide.asm.wasm",
pythonStdlibZip = embed "python_stdlib.zip",
);
10 changes: 6 additions & 4 deletions src/pyodide/internal/pool/emscriptenSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { reportError } from 'pyodide-internal:util';
*/
import { _createPyodideModule } from 'pyodide-internal:generated/pyodide.asm';

export {
import {
setUnsafeEval,
setGetRandomValues,
} from 'pyodide-internal:pool/builtin_wrappers';
Expand Down Expand Up @@ -56,7 +56,7 @@ function getWaitForDynlibs(resolveReadyPromise: PreRunHook): PreRunHook {
* This is a simplified version of the `prepareFileSystem` function here:
* https://github.com/pyodide/pyodide/blob/main/src/js/module.ts
*/
function getPrepareFileSystem(pythonStdlib: Uint8Array): PreRunHook {
function getPrepareFileSystem(pythonStdlib: ArrayBuffer): PreRunHook {
return function prepareFileSystem(Module: Module): void {
try {
const pymajor = Module._py_version_major();
Expand Down Expand Up @@ -118,7 +118,7 @@ function getInstantiateWasm(
*/
function getEmscriptenSettings(
isWorkerd: boolean,
pythonStdlib: Uint8Array,
pythonStdlib: ArrayBuffer,
pyodideWasmModule: WebAssembly.Module
): EmscriptenSettings {
const config: PyodideConfig = {
Expand Down Expand Up @@ -193,7 +193,7 @@ function* featureDetectionMonkeyPatchesContextManager() {
*/
export async function instantiateEmscriptenModule(
isWorkerd: boolean,
pythonStdlib: Uint8Array,
pythonStdlib: ArrayBuffer,
wasmModule: WebAssembly.Module
): Promise<Module> {
const emscriptenSettings = getEmscriptenSettings(
Expand All @@ -210,6 +210,8 @@ export async function instantiateEmscriptenModule(

// Wait until we've executed all the preRun hooks before proceeding
const emscriptenModule = await emscriptenSettings.readyPromise;
emscriptenModule.setUnsafeEval = setUnsafeEval;
emscriptenModule.setGetRandomValues = setGetRandomValues;
return emscriptenModule;
} catch (e) {
console.warn('Error in instantiateEmscriptenModule');
Expand Down
39 changes: 6 additions & 33 deletions src/pyodide/internal/python.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,39 +18,11 @@ import {
entropyBeforeTopLevel,
getRandomValues,
} from 'pyodide-internal:topLevelEntropy/lib';
import { default as SetupEmscripten } from 'internal:setup-emscripten';

import { default as UnsafeEval } from 'internal:unsafe-eval';
import { simpleRunPython } from 'pyodide-internal:util';

/**
* This file is a simplified version of the Pyodide loader:
* https://github.com/pyodide/pyodide/blob/main/src/js/pyodide.ts
*
* In particular, it drops the package lock, which disables
* `pyodide.loadPackage`. In trade we add memory snapshots here.
*/

/**
* _createPyodideModule and pyodideWasmModule together are produced by the
* Emscripten linker
*/
import pyodideWasmModule from 'pyodide-internal:generated/pyodide.asm.wasm';

/**
* The Python and Pyodide stdlib zipped together. The zip format is convenient
* because Python has a "ziploader" that allows one to import directly from a
* zip file.
*
* The ziploader solves bootstrapping problems around unpacking: Python comes
* with a bunch of C libs to unpack various archive formats, but they need stuff
* in this zip file to initialize their runtime state.
*/
import pythonStdlib from 'pyodide-internal:generated/python_stdlib.zip';
import {
instantiateEmscriptenModule,
setUnsafeEval,
setGetRandomValues,
} from 'pyodide-internal:generated/emscriptenSetup';

/**
* After running `instantiateEmscriptenModule` but before calling into any C
* APIs, we call this function. If `MEMORY` is defined, then we will have passed
Expand Down Expand Up @@ -90,14 +62,15 @@ export async function loadPyodide(
indexURL: string
): Promise<Pyodide> {
const Module = await enterJaegerSpan('instantiate_emscripten', () =>
instantiateEmscriptenModule(isWorkerd, pythonStdlib, pyodideWasmModule)
SetupEmscripten.getModule()
);
Module.API.config.jsglobals = globalThis;
if (isWorkerd) {
Module.API.config.indexURL = indexURL;
Module.API.config.resolveLockFilePromise!(lockfile);
}
setUnsafeEval(UnsafeEval);
setGetRandomValues(getRandomValues);
Module.setUnsafeEval(UnsafeEval);
Module.setGetRandomValues(getRandomValues);
await enterJaegerSpan('prepare_wasm_linear_memory', () =>
prepareWasmLinearMemory(Module)
);
Expand Down
4 changes: 4 additions & 0 deletions src/pyodide/types/emscripten.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,8 @@ interface Module {
addRunDependency(x: string): void;
removeRunDependency(x: string): void;
noInitialRun: boolean;
setUnsafeEval(mod: typeof import('internal:unsafe-eval').default): void;
setGetRandomValues(
func: typeof import('pyodide-internal:topLevelEntropy/lib').getRandomValues
): void;
}
5 changes: 5 additions & 0 deletions src/pyodide/types/setup-emscripten.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare namespace SetupEmscripten {
const getModule: () => Module;
}

export default SetupEmscripten;
5 changes: 5 additions & 0 deletions src/workerd/api/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ filegroup(
"pyodide/pyodide.c++",
"memory-cache.c++",
"r2*.c++",
"pyodide/setup-emscripten.c++",
"rtti.c++",
"url.c++",
"util.c++",
Expand Down Expand Up @@ -126,15 +127,19 @@ wd_cc_library(
name = "pyodide",
srcs = [
"pyodide/pyodide.c++",
"pyodide/setup-emscripten.c++",
],
hdrs = [
"pyodide/pyodide.h",
"pyodide/setup-emscripten.h",
"//src/pyodide:generated/emscripten_setup.capnp.h",
"//src/pyodide:generated/pyodide_extra.capnp.h",
],
implementation_deps = ["//src/workerd/util:string-buffer"],
visibility = ["//visibility:public"],
deps = [
"//src/pyodide",
"//src/pyodide:emscripten_setup_capnp",
"//src/pyodide:pyodide_extra_capnp",
"//src/workerd/io",
"//src/workerd/jsg",
Expand Down
3 changes: 3 additions & 0 deletions src/workerd/api/modules.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <workerd/api/node/node.h>
#include <workerd/api/pyodide/pyodide.h>
#include <workerd/api/pyodide/setup-emscripten.h>
#include <workerd/api/rtti.h>
#include <workerd/api/sockets.h>
#include <workerd/api/unsafe.h>
Expand All @@ -21,6 +22,7 @@ template <class Registry>
void registerModules(Registry& registry, auto featureFlags) {
node::registerNodeJsCompatModules(registry, featureFlags);
if (featureFlags.getPythonWorkers()) {
pyodide::registerSetupEmscriptenModule(registry, featureFlags);
pyodide::registerPyodideModules(registry, featureFlags);
}
registerUnsafeModules(registry, featureFlags);
Expand Down Expand Up @@ -48,6 +50,7 @@ void registerBuiltinModules(jsg::modules::ModuleRegistry::Builder& builder, auto
}

if (featureFlags.getPythonWorkers()) {
builder.add(pyodide::getInternalSetupEmscriptenModuleBundle<TypeWrapper>(featureFlags));
builder.add(pyodide::getExternalPyodideModuleBundle(featureFlags));
builder.add(pyodide::getInternalPyodideModuleBundle(featureFlags));
}
Expand Down
Loading

0 comments on commit 05e7568

Please sign in to comment.