Commit 5a03bdd6 authored by Trent Apted's avatar Trent Apted Committed by Chromium LUCI CQ

Modularize the Piex wasm used for loading RAW images on ChromeOS.

Currently Piex wasm loads into a global window.Module object as a side-
effect of importing the wasm JS glue. This approach may inhibit other
wasm modules loading into the same JS context.

To fix, configure emscripten to wrap the Piex module in a loader
function that returns a promise, and capture it into a unique identifier
on initialization.

This approach lends itself to on-demand loading of the wasm, so now the
wasm subsystem is only initialized when required, rather than whenever
the image loader extension starts up. That is, on the first request to
refresh the thumbnail for a RAW image, which should occur rarely.

Re-ran the Makefile at emscripten 1.38.33 (same as r815044), and ensured
that my local sdk created identical files before adding MODULARIZE flags.

Ran the separate test.sh. Test coverage also provided in browser_tests.

Bug: b/175505626
Cq-Include-Trybots: luci.chrome.try:linux-chromeos-chrome
Change-Id: I533a8d8028f6913efe0739c5e5cf80864addbe0f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2584634Reviewed-by: default avatarNoel Gordon <noel@chromium.org>
Commit-Queue: Trent Apted <tapted@chromium.org>
Cr-Commit-Position: refs/heads/master@{#836985}
parent df114abe
...@@ -22,13 +22,6 @@ function loadPiex() { ...@@ -22,13 +22,6 @@ function loadPiex() {
}); });
await loadJs('piex/piex.js.wasm'); await loadJs('piex/piex.js.wasm');
await loadJs('piex_module_scripts.js'); await loadJs('piex_module_scripts.js');
if (!PiexModule.calledRun) {
// In rare cases in tests (<1%), the runtime is already initialized.
// Waiting again for onRuntimeInitialized would hang indefinitely.
await new Promise(resolve => {
PiexModule['onRuntimeInitialized'] = resolve;
});
}
} }
if (!_piexLoadPromise) { if (!_piexLoadPromise) {
_piexLoadPromise = startLoad(); _piexLoadPromise = startLoad();
......
...@@ -19,6 +19,8 @@ PSRC = $(PIEX)/src/piex.cc \ ...@@ -19,6 +19,8 @@ PSRC = $(PIEX)/src/piex.cc \
INCS = -I $(PIEX) INCS = -I $(PIEX)
WASM = -s WASM=1 -fno-exceptions -Wall -fsanitize=cfi -flto -fvisibility=hidden WASM = -s WASM=1 -fno-exceptions -Wall -fsanitize=cfi -flto -fvisibility=hidden
WOPT = -Os --llvm-opts 3 \ WOPT = -Os --llvm-opts 3 \
-s MODULARIZE=1 \
-s EXPORT_NAME=createPiexModule \
-s STRICT=1 \ -s STRICT=1 \
-s ALLOW_MEMORY_GROWTH=1 \ -s ALLOW_MEMORY_GROWTH=1 \
-s ENVIRONMENT='web' \ -s ENVIRONMENT='web' \
......
...@@ -7,17 +7,18 @@ ...@@ -7,17 +7,18 @@
window.onload = () => { window.onload = () => {
console.log('[piexwasm] window.onload'); console.log('[piexwasm] window.onload');
let script = document.createElement('script'); const script = document.createElement('script');
document.head.appendChild(script); document.head.appendChild(script);
script.onerror = (error) => { script.onerror = (error) => {
console.log('[piexwasm] failed loading script:', script.src); console.log('[piexwasm] failed loading script:', script.src);
}; };
window.Module = { script.onload = () => {
onRuntimeInitialized: () => { console.log('[piexwasm] wrapper loaded');
console.log('[piexwasm] runtime loaded'); createPiexModule().then(module => {
}, console.log('[piexwasm] module initialized', module);
});
}; };
script.src = '/piex.js.wasm'; script.src = '/piex.js.wasm';
......
...@@ -245,14 +245,16 @@ found in the LICENSE file. ...@@ -245,14 +245,16 @@ found in the LICENSE file.
console.log('test:', name, 'details hash', hash, details); console.log('test:', name, 'details hash', hash, details);
} }
window.Module = {
onRuntimeInitialized: () => document.title = 'READY',
};
window.onload = function loadPiexModule() { window.onload = function loadPiexModule() {
let script = document.createElement('script'); let script = document.createElement('script');
document.head.appendChild(script); document.head.appendChild(script);
script.src = '/piex.js.wasm'; script.src = '/piex.js.wasm';
script.onload = () => {
createPiexModule().then(module => {
window.Module = module;
document.title = 'READY';
});
};
}; };
window.onerror = (error) => { window.onerror = (error) => {
......
...@@ -2,15 +2,12 @@ ...@@ -2,15 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
console.log('[PiexLoader] loaded');
/** /**
* Declares the piex-wasm Module interface. The Module has many interfaces * Declares the piex-wasm Module interface. The Module has many interfaces
* but only declare the parts required for PIEX work. * but only declare the parts required for PIEX work.
* *
* @typedef {{ * @typedef {{
* calledRun: boolean, * calledRun: boolean,
* onAbort: function((!Error|string)):undefined,
* HEAP8: !Uint8Array, * HEAP8: !Uint8Array,
* _malloc: function(number):number, * _malloc: function(number):number,
* _free: function(number):undefined, * _free: function(number):undefined,
...@@ -20,10 +17,29 @@ console.log('[PiexLoader] loaded'); ...@@ -20,10 +17,29 @@ console.log('[PiexLoader] loaded');
let PiexWasmModule; let PiexWasmModule;
/** /**
* Module defined by 'piex.js.wasm' script. * Subset of the Emscripten Module API required for initialization. See
* https://emscripten.org/docs/api_reference/module.html#module.
* @typedef {{
* onAbort: function((!Error|string)):undefined,
* }}
*/
let ModuleInitParams;
/**
* Module defined by 'piex.js.wasm' script upon initialization.
* @type {!PiexWasmModule} * @type {!PiexWasmModule}
*/ */
const PiexModule = /** @type {!PiexWasmModule} */ (globalThis['Module']) || {}; let PiexModule;
/**
* Module constructor defined by 'piex.js.wasm' script.
* @type {function(!ModuleInitParams): !Promise<!PiexWasmModule>}
*/
const initPiexModule =
/** @type {function(!ModuleInitParams): !Promise<!PiexWasmModule>} */ (
globalThis['createPiexModule']);
console.log(`[PiexLoader] available [init=${typeof initPiexModule}]`);
/** /**
* Set true if the Module.onAbort() handler is called. * Set true if the Module.onAbort() handler is called.
...@@ -31,17 +47,39 @@ const PiexModule = /** @type {!PiexWasmModule} */ (globalThis['Module']) || {}; ...@@ -31,17 +47,39 @@ const PiexModule = /** @type {!PiexWasmModule} */ (globalThis['Module']) || {};
*/ */
let piexFailed = false; let piexFailed = false;
const MODULE_SETTINGS = {
/**
* Installs an (Emscripten) Module.onAbort handler. Record that the
* Module has failed in piexFailed and re-throw the error.
*
* @param {!Error|string} error
* @throws {!Error|string}
*/
onAbort: (error) => {
piexFailed = true;
throw error;
}
};
/** @type {?Promise<undefined>} */
let initPiexModulePromise = null;
/** /**
* Installs an (Emscripten) Module.onAbort handler. Record that the Module * Returns a promise that resolves once initialization is complete. PiexModule
* has failed in piexFailed and re-throw the error. * may be undefined before this promise resolves.
* * @return {!Promise<undefined>}
* @param {!Error|string} error
* @throws {!Error|string}
*/ */
PiexModule.onAbort = (error) => { function piexModuleInitialized() {
piexFailed = true; if (!initPiexModulePromise) {
throw error; initPiexModulePromise = new Promise(resolve => {
}; initPiexModule(MODULE_SETTINGS).then(module => {
PiexModule = module;
console.log(`[PiexLoader] loaded [module=${typeof module}]`);
resolve();
});
});
}
return initPiexModulePromise;
}
/** /**
* Module failure recovery: if piexFailed is set via onAbort due to OOM in * Module failure recovery: if piexFailed is set via onAbort due to OOM in
...@@ -699,8 +737,9 @@ PiexLoader.load = function(source, onPiexModuleFailed) { ...@@ -699,8 +737,9 @@ PiexLoader.load = function(source, onPiexModuleFailed) {
/** @type {?ImageBuffer} */ /** @type {?ImageBuffer} */
let imageBuffer; let imageBuffer;
return readSourceData(source) return piexModuleInitialized()
.then((buffer) => { .then(() => readSourceData(source))
.then((/** !ArrayBuffer */ buffer) => {
if (piexModuleFailed()) { if (piexModuleFailed()) {
throw new Error('piex wasm module failed'); throw new Error('piex wasm module failed');
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment