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