Skip to content

Commit 4974839

Browse files
RSNarafacebook-github-bot
authored andcommitted
RuntimeExecutor: Use promises in sync ui thread utils (#51429)
Summary: Pull Request resolved: #51429 If we use promises, I believe the code is just easier to understand. Changelog: [Internal] Reviewed By: javache, yungsters Differential Revision: D74941734 fbshipit-source-id: a9bc5ac715c84a5a92f8f1c6635ebef9fe538377
1 parent 8a04ce6 commit 4974839

1 file changed

Lines changed: 38 additions & 48 deletions

File tree

packages/react-native/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h

Lines changed: 38 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
#pragma once
99

10-
#include <mutex>
10+
#include <future>
1111
#include <thread>
1212

1313
#include <jsi/jsi.h>
@@ -26,71 +26,61 @@ using RuntimeExecutor =
2626
std::function<void(std::function<void(jsi::Runtime& runtime)>&& callback)>;
2727

2828
/*
29-
* Executes a `callback` in a *synchronous* manner on the same thread using
30-
* given `RuntimeExecutor`.
31-
* Use this method when the caller needs to *be blocked* by executing the
32-
* `callback` and requires that the callback will be executed on the same
33-
* thread.
34-
* Example order of events (when not a sync call in runtimeExecutor callback):
35-
* - [UI thread] Lock all mutexes at start
36-
* - [UI thread] runtimeCaptured.lock before callback
37-
* - [JS thread] Set runtimePtr in runtimeExecutor callback
38-
* - [JS thread] runtimeCaptured.unlock in runtimeExecutor callback
39-
* - [UI thread] Call callback
40-
* - [JS thread] callbackExecuted.lock in runtimeExecutor callback
41-
* - [UI thread] callbackExecuted.unlock after callback
42-
* - [UI thread] jsBlockExecuted.lock after callback
43-
* - [JS thread] jsBlockExecuted.unlock in runtimeExecutor callback
29+
* Schedules `runtimeWork` to be executed on the same thread using the
30+
* `RuntimeExecutor`, and blocks on its completion.
31+
*
32+
* Example:
33+
* - [UI thread] Schedule `runtimeCaptureBlock` on js thread
34+
* - [UI thread] Wait for runtime capture: await(runtime)
35+
* - [JS thread] Capture runtime for ui thread: resolve(runtime, &rt);
36+
* - [JS thread] Wait until runtimeWork done: await(runtimeWorkDone)
37+
* - [UI thread] Call runtimeWork: runtimeWork(*runtimePrt);
38+
* - [UI thread] Signal runtimeWork done: resolve(runtimeWorkDone)
39+
* - [UI thread] Wait until runtime capture block finished:
40+
* await(runtimeCaptureBlockDone);
41+
* - [JS thread] Signal runtime capture block is finished:
42+
* resolve(runtimeCaptureBlockDone);
4443
*/
4544
inline static void executeSynchronouslyOnSameThread_CAN_DEADLOCK(
4645
const RuntimeExecutor& runtimeExecutor,
47-
std::function<void(jsi::Runtime& runtime)>&& callback) noexcept {
48-
// Note: We need the third mutex to get back to the main thread before
49-
// the lambda is finished (because all mutexes are allocated on the stack).
50-
51-
std::mutex runtimeCaptured;
52-
std::mutex callbackExecuted;
53-
std::mutex jsBlockExecuted;
54-
55-
runtimeCaptured.lock();
56-
callbackExecuted.lock();
57-
jsBlockExecuted.lock();
46+
std::function<void(jsi::Runtime&)>&& runtimeWork) noexcept {
47+
std::promise<jsi::Runtime*> runtime;
48+
std::promise<void> runtimeCaptureBlockDone;
49+
std::promise<void> runtimeWorkDone;
5850

59-
jsi::Runtime* runtimePtr;
51+
auto callingThread = std::this_thread::get_id();
6052

61-
auto threadId = std::this_thread::get_id();
53+
auto runtimeCaptureBlock = [&](jsi::Runtime& rt) {
54+
runtime.set_value(&rt);
6255

63-
runtimeExecutor([&](jsi::Runtime& runtime) {
64-
runtimePtr = &runtime;
65-
66-
if (threadId == std::this_thread::get_id()) {
67-
// In case of a synchronous call, we should unlock mutexes and return.
68-
runtimeCaptured.unlock();
69-
jsBlockExecuted.unlock();
70-
return;
56+
auto runtimeThread = std::this_thread::get_id();
57+
if (callingThread != runtimeThread) {
58+
// Block `runtimeThread` on execution of `runtimeWork` on `callingThread`.
59+
runtimeWorkDone.get_future().wait();
7160
}
7261

73-
runtimeCaptured.unlock();
74-
// `callback` is called somewhere here.
75-
callbackExecuted.lock();
76-
jsBlockExecuted.unlock();
77-
});
62+
// TODO(T225331233): This is likely unnecessary. Remove it.
63+
runtimeCaptureBlockDone.set_value();
64+
};
65+
runtimeExecutor(std::move(runtimeCaptureBlock));
66+
67+
jsi::Runtime* runtimePtr = runtime.get_future().get();
68+
runtimeWork(*runtimePtr);
69+
runtimeWorkDone.set_value();
7870

79-
runtimeCaptured.lock();
80-
callback(*runtimePtr);
81-
callbackExecuted.unlock();
82-
jsBlockExecuted.lock();
71+
// TODO(T225331233): This is likely unnecessary. Remove it.
72+
runtimeCaptureBlockDone.get_future().wait();
8373
}
8474

8575
template <typename DataT>
8676
inline static DataT executeSynchronouslyOnSameThread_CAN_DEADLOCK(
8777
const RuntimeExecutor& runtimeExecutor,
88-
std::function<DataT(jsi::Runtime& runtime)>&& callback) noexcept {
78+
std::function<DataT(jsi::Runtime& runtime)>&& runtimeWork) noexcept {
8979
DataT data;
9080

9181
executeSynchronouslyOnSameThread_CAN_DEADLOCK(
9282
runtimeExecutor,
93-
[&](jsi::Runtime& runtime) { data = callback(runtime); });
83+
[&](jsi::Runtime& runtime) { data = runtimeWork(runtime); });
9484

9585
return data;
9686
}

0 commit comments

Comments
 (0)