From 15ea60e3a86fa1c7857f9eacbe5bca70bbdd01bf Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Mon, 30 Mar 2020 19:32:46 -0700 Subject: [PATCH] RuntimeExecutor helpers that modify the way of the callback is being executed. Summary: Here we implement a bunch of helper methods that allow customizing the behavior of a RuntimeExecutor "on-demand" on the caller side. We will use it in the next diff(s). Changelog: [Internal] Fabric-specific internal change. Reviewed By: PeteTheHeat Differential Revision: D20551411 fbshipit-source-id: 51d3cd02b69753110c0e1155347c6e52eb882c7d --- ReactCommon/utils/RuntimeExecutor.h | 82 +++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/ReactCommon/utils/RuntimeExecutor.h b/ReactCommon/utils/RuntimeExecutor.h index e06caaa65ec..bdde3603e61 100644 --- a/ReactCommon/utils/RuntimeExecutor.h +++ b/ReactCommon/utils/RuntimeExecutor.h @@ -7,6 +7,9 @@ #pragma once +#include +#include + #include namespace facebook { @@ -23,5 +26,84 @@ namespace react { using RuntimeExecutor = std::function &&callback)>; +/* + * The caller can expect that the callback will be executed sometime later on an + * unspecified thread. + * Use this method when the caller prefers to not be blocked by executing the + * `callback`. + * Note that this method does not provide any guarantees + * about when the `callback` will be executed (before returning to the caller, + * after that, or in parallel), the only thing that is guaranteed is that there + * is no synchronization. + */ +inline static void executeAsynchronously( + RuntimeExecutor const &runtimeExecutor, + std::function &&callback) noexcept { + std::thread{[callback = std::move(callback), runtimeExecutor]() mutable { + runtimeExecutor(std::move(callback)); + }}; +} + +/* + * Executes a `callback` in a *synchronous* manner using given + * `RuntimeExecutor`. + * Use this method when the caller needs to *be blocked* by executing the + * callback but does not concerted about the particular thread on which the + * `callback` will be executed. + */ +inline static void executeSynchronously_CAN_DEADLOCK( + RuntimeExecutor const &runtimeExecutor, + std::function &&callback) noexcept { + std::mutex mutex; + mutex.lock(); + + runtimeExecutor( + [callback = std::move(callback), &mutex](jsi::Runtime &runtime) { + callback(runtime); + mutex.unlock(); + }); + + mutex.lock(); +} + +/* + * Executes a `callback` in a *synchronous* manner on the same thread using + * given `RuntimeExecutor`. + * Use this method when the caller needs to *be blocked* by executing the + * `callback` and requires that the callback will be executed on the same + * thread. + */ +inline static void executeSynchronouslyOnSameThread_CAN_DEADLOCK( + RuntimeExecutor const &runtimeExecutor, + std::function &&callback) noexcept { + // Note: We need the third mutex to get back to the main thread before + // the lambda is finished (because all mutexes are allocated on the stack). + // We use `recursive_mutex` here to not deadlock in case if a + // `RuntimeExecutor` executes the callback synchronously. + + std::recursive_mutex mutex1; + std::recursive_mutex mutex2; + std::recursive_mutex mutex3; + + mutex1.lock(); + mutex2.lock(); + mutex3.lock(); + + jsi::Runtime *runtimePtr; + + runtimeExecutor([&](jsi::Runtime &runtime) { + runtimePtr = &runtime; + mutex1.unlock(); + // `callback` is called somewhere here. + mutex2.lock(); + mutex3.unlock(); + }); + + mutex1.lock(); + callback(*runtimePtr); + mutex2.unlock(); + mutex3.lock(); +} + } // namespace react } // namespace facebook