Skip to content

Fix sync server deadlock v1.2.0#439

Open
AndrushaUt wants to merge 1 commit intoplotly:masterfrom
AndrushaUt:fix-sync-server-deadlock-v1.2.0
Open

Fix sync server deadlock v1.2.0#439
AndrushaUt wants to merge 1 commit intoplotly:masterfrom
AndrushaUt:fix-sync-server-deadlock-v1.2.0

Conversation

@AndrushaUt
Copy link
Copy Markdown

…access

The shared _return_queue causes a race condition when multiple threads call to_image concurrently: task_queue.join() unblocks all waiters when the count hits zero, not when a specific task completes. Two threads race to return_queue.get(), one wins, the other blocks forever.

Fix: give each caller its own Queue embedded in the Task. The server routes the result directly into it. No sharing, no race, no deadlock.

Reproduces ~1/1000 calls with 64 threads.
See plotly/plotly.py#5549

@emilykl
Copy link
Copy Markdown
Collaborator

emilykl commented Apr 1, 2026

Hi @AndrushaUt, thanks for the PR.

Were you able to come up with a way to reliably reproduce plotly/plotly.py#5549 ? That would be helpful in reviewing this fix.

Could you also restore the deleted .github/workflows/ files and make sure your branch is up to date with master? Thanks!

The shared `_return_queue` causes a race condition when multiple threads
call `to_image` concurrently: `task_queue.join()` unblocks all waiters
when the count hits zero, not when a specific task completes. Two threads
race to `return_queue.get()`, one wins, the other blocks forever.

Fix: give each caller its own `Queue` embedded in the `Task`. The server
routes the result directly into it. No sharing, no race, no deadlock.

Additionally, `_sync` functions now auto-start the singleton server
instead of falling back to `oneshot_async_run`, which creates a new
thread and event loop per call and can also hang under load.

The `atexit` handler that called `close()` → `thread.join()` is removed
since the server thread is already `daemon=True` and will exit with the
process. The `join()` could hang if Chromium shutdown blocks.

Reproduces ~1/1000 calls with 64 threads.
See plotly/plotly.py#5549
@AndrushaUt AndrushaUt force-pushed the fix-sync-server-deadlock-v1.2.0 branch from d6058a1 to f5426b1 Compare April 3, 2026 13:10
@AndrushaUt
Copy link
Copy Markdown
Author

AndrushaUt commented Apr 3, 2026

Hi @emilykl ! I've force-pushed the branch - it's now rebased on master with workflows restored and all changes in a single clean commit.
I used the reproduction script from the issue and it consistently deadlocked within 3-4 runs - one task would hang indefinitely while the other 999 completed fine.

With the fix applied, we ran 6000+ calls with zero hangs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants