Skip to content

Commit 02b2506

Browse files
committed
Fix #97 - Explain the deadlock case
1 parent 46b0b9b commit 02b2506

File tree

1 file changed

+62
-0
lines changed

1 file changed

+62
-0
lines changed

docs/faq.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,68 @@ obsolete standards.
376376
While such legacy code exists, be aware that JavaScript code may require
377377
special care.
378378

379+
### Possible deadlock
380+
381+
There are cases where users might encounter an error similar to the following one:
382+
383+
> 💀🔒 - Possible deadlock if proxy.xyz(...args) is awaited
384+
385+
#### When
386+
387+
Let's assume a worker script contains the following *Python* code:
388+
389+
```python title="worker: a deadlock example"
390+
from pyscript import sync
391+
392+
sync.worker_task = lambda: print('🔥 this is fine 🔥')
393+
394+
# deadlock 💀🔒
395+
sync.main_task()
396+
```
397+
398+
On the *main* thread, let's instead assume this code:
399+
```html title="main: a deadlock example"
400+
<script type="mpy">
401+
from pyscript import PyWorker
402+
403+
def async main_task():
404+
# deadlock 💀🔒
405+
await pw.sync.worker_task()
406+
407+
pw = PyWorker("./worker.py", {"type": "pyodide"})
408+
pw.sync.main_task = main_task
409+
</script>
410+
```
411+
412+
When the worker bootstraps and locks itself until `main_task()` is completed, it cannot respond to anything at all: it's literally locked.
413+
414+
If the *awaited* task is then asking for *worker* tasks to execute, we are in a clear [deadlock](https://en.wikipedia.org/wiki/Deadlock) situation.
415+
416+
#### Workaround
417+
418+
Beside trying to avoid deadlocks by all mean is the obvious suggestion and solution, the *blocking* part of the equation is what makes the presented exchange not possible.
419+
420+
However, if the *main* task does not need to block the *worker* while executing, we can rewrite that code as such:
421+
422+
On the *main* thread, let's instead assume this code:
423+
424+
```html title="main: avoiding deadlocks"
425+
<script type="mpy">
426+
from pyscript import window, PyWorker
427+
428+
def async main_task():
429+
# do not await the worker,
430+
# just schedule it for later (as resolved)
431+
window.Promise.resolve(pw.sync.worker_task())
432+
433+
pw = PyWorker("./worker.py", {"type": "pyodide"})
434+
pw.sync.main_task = main_task
435+
</script>
436+
```
437+
438+
This way it's still possible to consume *worker* exposed utilities within *main* exposed tasks and the worker can happily unlock itself and react to any scheduled task in the meantime.
439+
440+
379441
## Helpful hints
380442

381443
This section contains common hacks or hints to make using PyScript easier.

0 commit comments

Comments
 (0)