1 #pragma once 2 3 // JS_RUNTIME_HERMES is only set on Android so we have to check __has_include 4 // on iOS. 5 #if __APPLE__ && \ 6 (__has_include( \ 7 <reacthermes/HermesExecutorFactory.h>) || __has_include(<hermes/hermes.h>)) 8 #define JS_RUNTIME_HERMES 1 9 #endif 10 11 // Only include this file in Hermes-enabled builds as some platforms (like tvOS) 12 // don't support hermes and it causes the compilation to fail. 13 #if JS_RUNTIME_HERMES 14 15 #include <cxxreact/MessageQueueThread.h> 16 #include <jsi/decorator.h> 17 #include <jsi/jsi.h> 18 19 #include <memory> 20 #include <thread> 21 22 #if __has_include(<reacthermes/HermesExecutorFactory.h>) 23 #include <reacthermes/HermesExecutorFactory.h> 24 #else // __has_include(<hermes/hermes.h>) || ANDROID 25 #include <hermes/hermes.h> 26 #endif 27 28 #if REACT_NATIVE_MINOR_VERSION >= 71 29 #include <hermes/inspector/chrome/Registration.h> 30 #endif 31 32 namespace reanimated { 33 34 using namespace facebook; 35 using namespace react; 36 37 // ReentrancyCheck is copied from React Native 38 // from ReactCommon/hermes/executor/HermesExecutorFactory.cpp 39 // https://github.com/facebook/react-native/blob/main/ReactCommon/hermes/executor/HermesExecutorFactory.cpp 40 struct ReanimatedReentrancyCheck { 41 // This is effectively a very subtle and complex assert, so only 42 // include it in builds which would include asserts. 43 #ifndef NDEBUG 44 ReanimatedReentrancyCheck() : tid(std::thread::id()), depth(0) {} 45 46 void before() { 47 std::thread::id this_id = std::this_thread::get_id(); 48 std::thread::id expected = std::thread::id(); 49 50 // A note on memory ordering: the main purpose of these checks is 51 // to observe a before/before race, without an intervening after. 52 // This will be detected by the compare_exchange_strong atomicity 53 // properties, regardless of memory order. 54 // 55 // For everything else, it is easiest to think of 'depth' as a 56 // proxy for any access made inside the VM. If access to depth 57 // are reordered incorrectly, the same could be true of any other 58 // operation made by the VM. In fact, using acquire/release 59 // memory ordering could create barriers which mask a programmer 60 // error. So, we use relaxed memory order, to avoid masking 61 // actual ordering errors. Although, in practice, ordering errors 62 // of this sort would be surprising, because the decorator would 63 // need to call after() without before(). 64 65 if (tid.compare_exchange_strong( 66 expected, this_id, std::memory_order_relaxed)) { 67 // Returns true if tid and expected were the same. If they 68 // were, then the stored tid referred to no thread, and we 69 // atomically saved this thread's tid. Now increment depth. 70 assert(depth == 0 && "No thread id, but depth != 0"); 71 ++depth; 72 } else if (expected == this_id) { 73 // If the stored tid referred to a thread, expected was set to 74 // that value. If that value is this thread's tid, that's ok, 75 // just increment depth again. 76 assert(depth != 0 && "Thread id was set, but depth == 0"); 77 ++depth; 78 } else { 79 // The stored tid was some other thread. This indicates a bad 80 // programmer error, where VM methods were called on two 81 // different threads unsafely. Fail fast (and hard) so the 82 // crash can be analyzed. 83 __builtin_trap(); 84 } 85 } 86 87 void after() { 88 assert( 89 tid.load(std::memory_order_relaxed) == std::this_thread::get_id() && 90 "No thread id in after()"); 91 if (--depth == 0) { 92 // If we decremented depth to zero, store no-thread into tid. 93 std::thread::id expected = std::this_thread::get_id(); 94 bool didWrite = tid.compare_exchange_strong( 95 expected, std::thread::id(), std::memory_order_relaxed); 96 assert(didWrite && "Decremented to zero, but no tid write"); 97 } 98 } 99 100 std::atomic<std::thread::id> tid; 101 // This is not atomic, as it is only written or read from the owning 102 // thread. 103 unsigned int depth; 104 #endif // NDEBUG 105 }; 106 107 // This is in fact a subclass of jsi::Runtime! WithRuntimeDecorator is a 108 // template class that is a subclass of DecoratedRuntime which is also a 109 // template class that then inherits its template, which in this case is 110 // jsi::Runtime. So the inheritance is: ReanimatedHermesRuntime -> 111 // WithRuntimeDecorator -> DecoratedRuntime -> jsi::Runtime You can find out 112 // more about this in ReactCommon/jsi/jsi/Decorator.h or by following this link: 113 // https://github.com/facebook/react-native/blob/main/ReactCommon/jsi/jsi/decorator.h 114 class ReanimatedHermesRuntime 115 : public jsi::WithRuntimeDecorator<ReanimatedReentrancyCheck> { 116 public: 117 ReanimatedHermesRuntime( 118 std::unique_ptr<facebook::hermes::HermesRuntime> runtime, 119 std::shared_ptr<MessageQueueThread> jsQueue); 120 ~ReanimatedHermesRuntime(); 121 122 private: 123 std::unique_ptr<facebook::hermes::HermesRuntime> runtime_; 124 ReanimatedReentrancyCheck reentrancyCheck_; 125 #if HERMES_ENABLE_DEBUGGER 126 #if REACT_NATIVE_MINOR_VERSION >= 71 127 facebook::hermes::inspector::chrome::DebugSessionToken debugToken_; 128 #endif // REACT_NATIVE_MINOR_VERSION >= 71 129 #endif // HERMES_ENABLE_DEBUGGER 130 }; 131 132 } // namespace reanimated 133 134 #endif // JS_RUNTIME_HERMES 135