/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include #include #include namespace facebook::react { /* * Takes a function and calls it with a reference to a Runtime. The function * will be called when it is safe to do so (i.e. it ensures non-concurrent * access) and may be invoked asynchronously, depending on the implementation. * If you need to access a Runtime, it's encouraged to use a RuntimeExecutor * instead of storing a pointer to the Runtime itself, which makes it more * difficult to ensure that the Runtime is being accessed safely. */ 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( const RuntimeExecutor& runtimeExecutor, std::function&& callback) noexcept { std::thread([callback = std::move(callback), runtimeExecutor]() mutable { runtimeExecutor(std::move(callback)); }).detach(); } /* * 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 concerned about the particular thread on which the * `callback` will be executed. */ inline static void executeSynchronously_CAN_DEADLOCK( const RuntimeExecutor& 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( const RuntimeExecutor& 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). std::mutex mutex1; std::mutex mutex2; std::mutex mutex3; mutex1.lock(); mutex2.lock(); mutex3.lock(); jsi::Runtime* runtimePtr; auto threadId = std::this_thread::get_id(); runtimeExecutor([&](jsi::Runtime& runtime) { runtimePtr = &runtime; if (threadId == std::this_thread::get_id()) { // In case of a synchronous call, we should unlock mutexes and return. mutex1.unlock(); mutex3.unlock(); return; } mutex1.unlock(); // `callback` is called somewhere here. mutex2.lock(); mutex3.unlock(); }); mutex1.lock(); callback(*runtimePtr); mutex2.unlock(); mutex3.lock(); } template inline static DataT executeSynchronouslyOnSameThread_CAN_DEADLOCK( const RuntimeExecutor& runtimeExecutor, std::function&& callback) noexcept { DataT data; executeSynchronouslyOnSameThread_CAN_DEADLOCK( runtimeExecutor, [&](jsi::Runtime& runtime) { data = callback(runtime); }); return data; } } // namespace facebook::react