wasm: clarify qtloader onExit behavior
onExit is called whenever the application exits, i.e. when the app canvas should no longer be displayed and the loader/embedder code should take some action. Emscripten provides two callbacks which can be used here: - onExit, called when the app exits (but see EXIT_RUNTIME) - onAbort, called on abort errors. These map to the two cases Qt's onExit supports. onExit is not called when EXIT_RUNTIME is disabled, which means we don't need the special case for exit code 0. In addition call onExit on any exception. The second call to showUi() in html_shell.html is then not needed any more and we avoid duplicating the UI state handling in user code. Update the qtloader_integration test to handle changes in behavior (we no longer set the error text on exit). Use emscripten_force_exit() to simulate application exit - using this function makes Emscripten call onExit even when EXIT_RUNTIME is disabled. Change-Id: I72b5463c1836e8d5054e594abbd304fbc67032b7 Reviewed-by: Piotr Wierciński <piotr.wiercinski@qt.io> Reviewed-by: Mikołaj Boc <Mikolaj.Boc@qt.io> (cherry picked from commit a4d1c30a1b52e797cce504f90bcf20d7943dd1f9) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
2b62922e2e
commit
e3b08a61eb
@ -9,9 +9,14 @@
|
|||||||
* - environment: { [name:string] : string }
|
* - environment: { [name:string] : string }
|
||||||
* environment variables set on the instance
|
* environment variables set on the instance
|
||||||
* - onExit: (exitStatus: { text: string, code?: number, crashed: bool }) => void
|
* - onExit: (exitStatus: { text: string, code?: number, crashed: bool }) => void
|
||||||
* called when the application has exited for any reason. exitStatus.code is defined in
|
* called when the application has exited for any reason. There are two cases:
|
||||||
* case of a normal application exit. This is not called on exit with return code 0, as
|
* aborted: crashed is true, text contains an error message.
|
||||||
* the program does not shutdown its runtime and technically keeps running async.
|
* exited: crashed is false, code contians the exit code.
|
||||||
|
*
|
||||||
|
* Note that by default Emscripten does not exit when main() returns. This behavior
|
||||||
|
* is controlled by the EXIT_RUNTIME linker flag; set "-s EXIT_RUNTIME=1" to make
|
||||||
|
* Emscripten tear down the runtime and exit when main() returns.
|
||||||
|
*
|
||||||
* - containerElements: HTMLDivElement[]
|
* - containerElements: HTMLDivElement[]
|
||||||
* Array of host elements for Qt screens. Each of these elements is mapped to a QScreen on
|
* Array of host elements for Qt screens. Each of these elements is mapped to a QScreen on
|
||||||
* launch.
|
* launch.
|
||||||
@ -161,26 +166,14 @@ async function qtLoad(config)
|
|||||||
return originalLocatedFilename;
|
return originalLocatedFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is needed for errors which occur right after resolving the instance promise but
|
const originalOnExit = config.onExit;
|
||||||
// before exiting the function (i.e. on call to main before stack unwinding).
|
config.onExit = code => {
|
||||||
let loadTimeException = undefined;
|
originalOnExit?.();
|
||||||
// We don't want to issue onExit when aborted
|
|
||||||
let aborted = false;
|
|
||||||
const originalQuit = config.quit;
|
|
||||||
config.quit = (code, exception) =>
|
|
||||||
{
|
|
||||||
originalQuit?.(code, exception);
|
|
||||||
|
|
||||||
if (exception)
|
|
||||||
loadTimeException = exception;
|
|
||||||
if (!aborted && code !== 0) {
|
|
||||||
config.qt.onExit?.({
|
config.qt.onExit?.({
|
||||||
text: exception.message,
|
|
||||||
code,
|
code,
|
||||||
crashed: false
|
crashed: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const originalOnAbort = config.onAbort;
|
const originalOnAbort = config.onAbort;
|
||||||
config.onAbort = text =>
|
config.onAbort = text =>
|
||||||
@ -212,10 +205,17 @@ async function qtLoad(config)
|
|||||||
|
|
||||||
// Call app/emscripten module entry function. It may either come from the emscripten
|
// Call app/emscripten module entry function. It may either come from the emscripten
|
||||||
// runtime script or be customized as needed.
|
// runtime script or be customized as needed.
|
||||||
const instance = await Promise.race(
|
let instance;
|
||||||
|
try {
|
||||||
|
instance = await Promise.race(
|
||||||
[circuitBreaker, config.qt.entryFunction(config)]);
|
[circuitBreaker, config.qt.entryFunction(config)]);
|
||||||
if (loadTimeException && loadTimeException.name !== 'ExitStatus')
|
} catch (e) {
|
||||||
throw loadTimeException;
|
config.qt.onExit?.({
|
||||||
|
text: e.message,
|
||||||
|
crashed: true
|
||||||
|
});
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
@ -65,8 +65,6 @@
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
console.error(e.stack);
|
console.error(e.stack);
|
||||||
status.innerHTML = e.message;
|
|
||||||
showUi(spinner);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -71,7 +71,7 @@ void crash()
|
|||||||
|
|
||||||
void exitApp()
|
void exitApp()
|
||||||
{
|
{
|
||||||
exit(ExitValueFromExitApp);
|
emscripten_force_exit(ExitValueFromExitApp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void produceOutput()
|
void produceOutput()
|
||||||
@ -135,7 +135,7 @@ int main(int argc, char **argv)
|
|||||||
std::find(arguments.begin(), arguments.end(), QStringLiteral("--exit-immediately"))
|
std::find(arguments.begin(), arguments.end(), QStringLiteral("--exit-immediately"))
|
||||||
!= arguments.end();
|
!= arguments.end();
|
||||||
if (exitImmediately)
|
if (exitImmediately)
|
||||||
return ExitValueImmediateReturn;
|
emscripten_force_exit(ExitValueImmediateReturn);
|
||||||
|
|
||||||
const bool crashImmediately =
|
const bool crashImmediately =
|
||||||
std::find(arguments.begin(), arguments.end(), QStringLiteral("--crash-immediately"))
|
std::find(arguments.begin(), arguments.end(), QStringLiteral("--crash-immediately"))
|
||||||
|
@ -323,9 +323,7 @@ export class QtLoaderIntegrationTests
|
|||||||
caughtException = e;
|
caughtException = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// An exception should have been thrown from load()
|
assert.isUndefined(caughtException);
|
||||||
assert.equal('RuntimeError', caughtException.name);
|
|
||||||
|
|
||||||
assert.equal(1, onExitMock.calls.length);
|
assert.equal(1, onExitMock.calls.length);
|
||||||
const exitStatus = onExitMock.calls[0][0];
|
const exitStatus = onExitMock.calls[0][0];
|
||||||
assert.isTrue(exitStatus.crashed);
|
assert.isTrue(exitStatus.crashed);
|
||||||
@ -373,7 +371,7 @@ export class QtLoaderIntegrationTests
|
|||||||
const exitStatus = onExitMock.calls[0][0];
|
const exitStatus = onExitMock.calls[0][0];
|
||||||
assert.isFalse(exitStatus.crashed);
|
assert.isFalse(exitStatus.crashed);
|
||||||
assert.equal(instance.EXIT_VALUE_FROM_EXIT_APP, exitStatus.code);
|
assert.equal(instance.EXIT_VALUE_FROM_EXIT_APP, exitStatus.code);
|
||||||
assert.isNotUndefined(exitStatus.text);
|
assert.isUndefined(exitStatus.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
async exitImmediately()
|
async exitImmediately()
|
||||||
@ -392,7 +390,7 @@ export class QtLoaderIntegrationTests
|
|||||||
|
|
||||||
assert.isFalse(exitStatusFromOnExit.crashed);
|
assert.isFalse(exitStatusFromOnExit.crashed);
|
||||||
assert.equal(instance.EXIT_VALUE_IMMEDIATE_RETURN, exitStatusFromOnExit.code);
|
assert.equal(instance.EXIT_VALUE_IMMEDIATE_RETURN, exitStatusFromOnExit.code);
|
||||||
assert.isNotUndefined(exitStatusFromOnExit.text);
|
assert.isUndefined(exitStatusFromOnExit.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
async userQuitCallbackCalled()
|
async userQuitCallbackCalled()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user