Make RCTUnsafeExecuteOnMainQueueSync safer
Problem
React native's new list component is doing synchronous render. That means it makes synchronous dispatches from main thread to the js thread. (To capture the runtime so that it can execute js on the main thread).
But, the js thread already as a bunch of synchronous calls to the main thread. So, if any of those js -> ui sync calls happen concurrently with a synchronous render, the application will deadlock.
This diff is an attempt to mitigate all those deadlocks.
Context
How js execution from the main thread works:
- Main thread puts a block on the js thread, to capture the js runtime.
- Main thread goes to sleep until that "runtime capture" block executes.
- Js thread executes "runtime capture block". The runtime is captured for the main thread. The js thread is put to sleep, until the runtime is released.
- Main thread wakes up, noticing that the runtime is captured. It executes its js code with the captured runtime. Then, it releases the runtime, and wakes up the js thread. Both the main and js thread move on to other tasks.
How synchronous js -> main thread calls work:
- Js thread puts a ui block on the main queue.
- Js thread goes to sleep until that ui block executes on the main thread.
Deadlock #1
Main thread: execute js now:
- Main thread puts a block on the js queue, to capture the runtime.
- Main thread then then goes to sleep, waiting for runtime to be captured
JS thread: execute ui code synchronously:
- Js thread schedules a block on the ui thread
- Js thread then goes to sleep, waiting for that block to execute.
Result: The application deadlocks
| {F1978009555} | {F1978009612} |
Deadlock #2
JS thread: execute ui code synchronously:
- Js thread schedules a block on the ui thread
- Js thread then goes to sleep waiting for that block to execute.
Main thread: execute js now:
- Main thread puts a block on the js queue, to capture the runtime.
- Main thread then then goes to sleep, waiting for runtime to be captured
Result: The application deadlocks
| {F1978009690} | {F1978009701} |
Changes
This diff attempts to fix those deadlocks. How:
- In "execute ui code synchronously" (js thread):
- Before going to sleep, the js thread schedules the ui work on the main queue, and it posts the ui work to "execute js now".
- In "execute js now" (main thread):
- This diff makes "execute js now" stateful: it keeps a "pending ui block."
- Before capturing the runtime, the "execute js now" executes "pending ui work", if it exists.
- While sleeping waiting for runtime capture, "execute js now" can wake up, and execute "pending ui work." It goes back to sleep afterwards, waiting for runtime capture.
Mitigation: Deadlock #1
Main thread: execute js now:
- Main thread puts a block on the js queue, to capture the runtime.
- Main thread then then goes to sleep, waiting for runtime capture
JS Thread: execute ui code synchronously:
- Js thread puts its ui block on the ui queue.
- New: Js thread also posts that ui block to "execute js now". Main thread was sleeping waiting for runtime to be captured. It now wakes up.
- Js thread goes to sleep.
Main thread: execute js now
- Main thread woken up immediately, because a "pending ui block" is posted. It executes the "pending ui block." The block, also scheduled on the main thread, noops henceforth.
- Main thread goes back to sleep, waiting for runtime capture.
- The js thread wakes up, moves on to the next task.
Result: The runtime is captured by the main thread.
| {F1978010383} | {F1978010363} | {F1978010371} | {F1978010379} |
Mitigation: Deadlock #2
JS Thread: execute ui code synchronously:
- Js thread puts its ui block on the ui queue.
- New: Js thread also posts that ui block to "execute js now".
- Js thread goes to sleep.
Main thread: execute js now
- Main thread puts a block on the js queue, to capture the runtime.
- Main thread then then goes to sleep, waiting for runtime capture.
Main thread: execute js now
- Main thread woken up immediately, because a "pending ui block" is posted. It executes the "pending ui block" immediately. The block, also scheduled on the main thread, noops henceforth.
- Main thread goes back to sleep, waiting for runtime capture.
- Js thread wakes up and moves onto the next task.
Result: Main thread captures the runtime.
| {F1978457293} | {F1978460398} | {F1978460407} | {F1978460440} |
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
| Fails | |
|---|---|
| :no_entry_sign: |
:clipboard: Missing Changelog - Please add a Changelog to your PR description. See Changelog format |
| Warnings | |
|---|---|
| :warning: | :clipboard: Missing Test Plan - Can you add a Test Plan? To do so, add a "## Test Plan" section to your PR description. A Test Plan lets us know how these changes were tested. |
Generated by :no_entry_sign: dangerJS against cc655b3bf6a6690a55b7a3a0fa71c62252386225
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326
This pull request was exported from Phabricator. Differential Revision: D74769326