RuntimeExecutor.h 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #pragma once
  8. #include <mutex>
  9. #include <thread>
  10. #include <jsi/jsi.h>
  11. namespace facebook {
  12. namespace react {
  13. /*
  14. * Takes a function and calls it with a reference to a Runtime. The function
  15. * will be called when it is safe to do so (i.e. it ensures non-concurrent
  16. * access) and may be invoked asynchronously, depending on the implementation.
  17. * If you need to access a Runtime, it's encouraged to use a RuntimeExecutor
  18. * instead of storing a pointer to the Runtime itself, which makes it more
  19. * difficult to ensure that the Runtime is being accessed safely.
  20. */
  21. using RuntimeExecutor =
  22. std::function<void(std::function<void(jsi::Runtime &runtime)> &&callback)>;
  23. /*
  24. * The caller can expect that the callback will be executed sometime later on an
  25. * unspecified thread.
  26. * Use this method when the caller prefers to not be blocked by executing the
  27. * `callback`.
  28. * Note that this method does not provide any guarantees
  29. * about when the `callback` will be executed (before returning to the caller,
  30. * after that, or in parallel), the only thing that is guaranteed is that there
  31. * is no synchronization.
  32. */
  33. inline static void executeAsynchronously(
  34. RuntimeExecutor const &runtimeExecutor,
  35. std::function<void(jsi::Runtime &runtime)> &&callback) noexcept {
  36. std::thread{[callback = std::move(callback), runtimeExecutor]() mutable {
  37. runtimeExecutor(std::move(callback));
  38. }};
  39. }
  40. /*
  41. * Executes a `callback` in a *synchronous* manner using given
  42. * `RuntimeExecutor`.
  43. * Use this method when the caller needs to *be blocked* by executing the
  44. * callback but does not concerted about the particular thread on which the
  45. * `callback` will be executed.
  46. */
  47. inline static void executeSynchronously_CAN_DEADLOCK(
  48. RuntimeExecutor const &runtimeExecutor,
  49. std::function<void(jsi::Runtime &runtime)> &&callback) noexcept {
  50. std::mutex mutex;
  51. mutex.lock();
  52. runtimeExecutor(
  53. [callback = std::move(callback), &mutex](jsi::Runtime &runtime) {
  54. callback(runtime);
  55. mutex.unlock();
  56. });
  57. mutex.lock();
  58. }
  59. /*
  60. * Executes a `callback` in a *synchronous* manner on the same thread using
  61. * given `RuntimeExecutor`.
  62. * Use this method when the caller needs to *be blocked* by executing the
  63. * `callback` and requires that the callback will be executed on the same
  64. * thread.
  65. */
  66. inline static void executeSynchronouslyOnSameThread_CAN_DEADLOCK(
  67. RuntimeExecutor const &runtimeExecutor,
  68. std::function<void(jsi::Runtime &runtime)> &&callback) noexcept {
  69. // Note: We need the third mutex to get back to the main thread before
  70. // the lambda is finished (because all mutexes are allocated on the stack).
  71. // We use `recursive_mutex` here to not deadlock in case if a
  72. // `RuntimeExecutor` executes the callback synchronously.
  73. std::recursive_mutex mutex1;
  74. std::recursive_mutex mutex2;
  75. std::recursive_mutex mutex3;
  76. mutex1.lock();
  77. mutex2.lock();
  78. mutex3.lock();
  79. jsi::Runtime *runtimePtr;
  80. runtimeExecutor([&](jsi::Runtime &runtime) {
  81. runtimePtr = &runtime;
  82. mutex1.unlock();
  83. // `callback` is called somewhere here.
  84. mutex2.lock();
  85. mutex3.unlock();
  86. });
  87. mutex1.lock();
  88. callback(*runtimePtr);
  89. mutex2.unlock();
  90. mutex3.lock();
  91. }
  92. } // namespace react
  93. } // namespace facebook