You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Summary:
# 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.
The main thread wakes up in "execute js now":
* Main thread sees that 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". 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 sees that a "pending ui block" is posted. It executes the "pending ui block" immediately. The block, also scheduled on the main thread, noops henceforth.
* Js thread wakes up and moves onto the next task.
**Result:** Main thread captures the runtime.
| {F1978010525} | {F1978010533} | {F1978010542} |

Differential Revision: D74769326
Copy file name to clipboardExpand all lines: packages/react-native/ReactCommon/runtimeexecutor/platform/ios/ReactCommon/RuntimeExecutorSyncUIThreadUtils.h
Copy file name to clipboardExpand all lines: packages/react-native/ReactCommon/runtimeexecutor/platform/ios/ReactCommon/RuntimeExecutorSyncUIThreadUtils.mm
+141-4
Original file line number
Diff line number
Diff line change
@@ -5,11 +5,119 @@
5
5
* LICENSE file in the root directory of this source tree.
0 commit comments