Skip to content

Fix deadlock in RuntimeScheduler clearQueues when a task destructor schedules work - #59298

Closed
hcbylmz wants to merge 1 commit into
react:mainfrom
hcbylmz:fix/runtime-scheduler-clear-queues-deadlock
Closed

hcbylmz wants to merge 1 commit into
react:mainfrom
hcbylmz:fix/runtime-scheduler-clear-queues-deadlock

Conversation

@hcbylmz

@hcbylmz hcbylmz commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Summary:

Fixes #59204.

RuntimeScheduler_Modern::clearQueues() destroys the queued tasks while it holds schedulingMutex_. A task owns its callback, so this also destroys everything the callback captured. If one of those captures schedules a task in its destructor, scheduleTask() tries to lock the same non-recursive mutex on the same thread, and the JS thread deadlocks. clearQueues() runs from handleTaskError(), so a single uncaught error in a task is enough to trigger it when another queued task has such a capture.

The fix is the one @fregster proposed in the issue: swap the queue out under the lock, and let the drained tasks be destroyed after the lock is released. A task scheduled from one of those destructors goes into the now-empty queue and still runs.

Changelog:

[GENERAL] [FIXED] - Fix deadlock in RuntimeScheduler when a task destructor schedules work while the queues are cleared after an error

Test Plan:

Added clearQueuesAllowsTaskDestructorsToScheduleTasks to RuntimeSchedulerTest. It queues a task whose callback captures an object that schedules a task when destroyed, then runs a task that throws.

  • Without the fix: the test hangs. The stuck thread is in std::shared_mutex::lock called from RuntimeScheduler_Modern::scheduleTask, called from Task::~Task, called from clearQueues → handleTaskError.
  • With the fix: all 88 RuntimeSchedulerTest tests pass.

I ran the tests on macOS (arm64) with a standalone CMake + googletest build of RuntimeSchedulerTest.cpp and its dependencies, linked against the Hermes macOS framework.

🤖 Generated with Claude Code

…chedules work

clearQueues() destroyed the queued tasks while holding schedulingMutex_.
If a task's callback captured an object whose destructor schedules a
task, scheduleTask() tried to lock the same non-recursive mutex on the
same thread and the JS thread deadlocked. This happens when a task
throws and handleTaskError() clears the queues.

Swap the queue out under the lock and destroy the drained tasks after
the lock is released.

Fixes react#59204. The fix was proposed by @fregster in the issue.

Co-authored-by: fregster <630468+fregster@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@meta-cla

meta-cla Bot commented Oct 21, 2026

Copy link
Copy Markdown

Hi @hcbylmz!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@meta-cla

meta-cla Bot commented Oct 21, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 21, 2026
@meta-cla

meta-cla Bot commented Oct 21, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Oct 21, 2026
@meta-codesync

meta-codesync Bot commented Oct 21, 2026

Copy link
Copy Markdown

@javache has imported this pull request. If you are a Meta employee, you can view this in D126662928.

@meta-codesync meta-codesync Bot closed this in 3181abc Oct 21, 2026
@react-native-bot

Copy link
Copy Markdown
Collaborator

This pull request was successfully merged by @hcbylmz in 3181abc

When will my fix make it into a release? | How to file a pick request?

@react-native-bot react-native-bot added the Merged This PR has been merged. label Oct 21, 2026
@meta-codesync

meta-codesync Bot commented Oct 21, 2026

Copy link
Copy Markdown

@javache merged this pull request in 3181abc.

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

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

2 participants