2021-09-26 18:53:46 +00:00
|
|
|
(interrupting_execution)=
|
|
|
|
|
|
|
|
# Interrupting execution
|
|
|
|
|
|
|
|
The native Python interrupt system is based on preemptive multitasking but Web
|
|
|
|
Assembly has no support for preemptive multitasking. Because of this,
|
|
|
|
interrupting execution in Pyodide must be achieved via a different mechanism
|
|
|
|
which takes some effort to set up.
|
|
|
|
|
|
|
|
## Setting up interrupts
|
|
|
|
|
|
|
|
In order to use interrupts you must be using Pyodide in a webworker.
|
2023-01-20 02:21:44 +00:00
|
|
|
You also will need to use a {js:class}`SharedArrayBuffer`, which means that your
|
|
|
|
server
|
2021-09-26 18:53:46 +00:00
|
|
|
must set appropriate security headers. See [the MDN
|
|
|
|
docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements)
|
|
|
|
for more information.
|
|
|
|
|
2023-01-20 02:21:44 +00:00
|
|
|
To use the interrupt system, you should create a {js:class}`SharedArrayBuffer`
|
|
|
|
on either the main thread or the worker thread and share it with the other
|
|
|
|
thread. You should use {js:func}`pyodide.setInterruptBuffer` to set the
|
|
|
|
interrupt buffer on the Pyodide thread. When you want to indicate an interrupt,
|
|
|
|
write a `2` into the interrupt buffer. When the interrupt signal is processed,
|
|
|
|
Pyodide will set the value of the interrupt buffer back to `0`.
|
2021-09-26 18:53:46 +00:00
|
|
|
|
2023-01-20 02:21:44 +00:00
|
|
|
By default, when the interrupt fires, a {py:exc}`KeyboardInterrupt` is raised.
|
|
|
|
Using the {py:mod}`signal` module, it is possible to register a custom Python
|
|
|
|
function to handle {py:data}`~signal.SIGINT`. If you register a custom handler
|
|
|
|
function it will be called instead.
|
2021-09-26 18:53:46 +00:00
|
|
|
|
|
|
|
Here is a very basic example. Main thread code:
|
|
|
|
|
|
|
|
```js
|
|
|
|
let pyodideWorker = new Worker("pyodideWorker.js");
|
|
|
|
let interruptBuffer = new Uint8Array(new SharedArrayBuffer(1));
|
|
|
|
pyodideWorker.postMessage({ cmd: "setInterruptBuffer", interruptBuffer });
|
|
|
|
function interruptExecution() {
|
|
|
|
// 2 stands for SIGINT.
|
|
|
|
interruptBuffer[0] = 2;
|
|
|
|
}
|
|
|
|
// imagine that interruptButton is a button we want to trigger an interrupt.
|
|
|
|
interruptButton.addEventListener("click", interruptExecution);
|
|
|
|
async function runCode(code) {
|
|
|
|
// Clear interruptBuffer in case it was accidentally left set after previous code completed.
|
|
|
|
interruptBuffer[0] = 0;
|
|
|
|
pyodideWorker.postMessage({ cmd: "runCode", code });
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Worker code:
|
|
|
|
|
|
|
|
```js
|
|
|
|
self.addEventListener("message", (msg) => {
|
|
|
|
if (msg.data.cmd === "setInterruptBuffer") {
|
|
|
|
pyodide.setInterruptBuffer(msg.data.interruptBuffer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (msg.data.cmd === "runCode") {
|
|
|
|
pyodide.runPython(msg.data.code);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
2021-09-29 08:01:53 +00:00
|
|
|
## Allowing JavaScript code to be interrupted
|
2021-09-26 18:53:46 +00:00
|
|
|
|
|
|
|
The interrupt system above allows interruption of Python code and also of C code
|
2023-01-20 02:21:44 +00:00
|
|
|
that allows itself to be interrupted by periodically calling
|
|
|
|
{c:func}`PyErr_CheckSignals`. There is also a function
|
|
|
|
{js:func}`pyodide.checkInterrupt` that allows JavaScript functions called from
|
|
|
|
Python to check for an interrupt. As a simple example, we can implement an
|
|
|
|
interruptible sleep function using {js:func}`Atomics.wait`:
|
2021-09-26 18:53:46 +00:00
|
|
|
|
|
|
|
```js
|
|
|
|
let blockingSleepBuffer = new Int32Array(new SharedArrayBuffer(4));
|
|
|
|
function blockingSleep(t) {
|
|
|
|
for (let i = 0; i < t * 20; i++) {
|
2022-05-29 16:45:45 +00:00
|
|
|
// This Atomics.wait call blocks the thread until the buffer changes or a 50ms timeout elapses.
|
2021-09-26 18:53:46 +00:00
|
|
|
// Since we won't change the value in the buffer, this blocks for 50ms.
|
|
|
|
Atomics.wait(blockingSleepBuffer, 0, 0, 50);
|
|
|
|
// Periodically check for an interrupt to allow a KeyboardInterrupt.
|
|
|
|
pyodide.checkInterrupt();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|