1010#include < react/cxxstableapi/UmbrellaGuard.h>
1111
1212#include < react/bridging/Base.h>
13- #include < react/bridging/CallbackWrapper.h>
13+ #include < react/bridging/CallbackWrapper.h> // Retain the legacy transitive include.
1414
1515#include < ReactCommon/CallInvoker.h>
1616#include < ReactCommon/SchedulerPriority.h>
@@ -61,16 +61,16 @@ class AsyncCallback {
6161
6262 void callWithArgs (std::optional<SchedulerPriority> priority, Args... args) const noexcept
6363 {
64- if (auto wrapper = callback_->wrapper_ .lock ()) {
65- auto fn = [callback = callback_,
64+ auto callback = callback_;
65+ if (auto jsInvoker = callback->function_ .lockInvoker ()) {
66+ auto fn = [callback = std::move (callback),
6667 argsPtr = std::make_shared<std::tuple<Args...>>(std::make_tuple (std::forward<Args>(args)...))](
6768 jsi::Runtime &) { callback->apply (std::move (*argsPtr)); };
6869
69- auto &jsInvoker = wrapper->jsInvoker ();
7070 if (priority) {
71- jsInvoker. invokeAsync (*priority, std::move (fn));
71+ jsInvoker-> invokeAsync (*priority, std::move (fn));
7272 } else {
73- jsInvoker. invokeAsync (std::move (fn));
73+ jsInvoker-> invokeAsync (std::move (fn));
7474 }
7575 }
7676 }
@@ -79,57 +79,38 @@ class AsyncCallback {
7979 std::optional<SchedulerPriority> priority,
8080 std::function<void (jsi::Runtime &, jsi::Function &)> &&callImpl) const noexcept
8181 {
82- if ( auto wrapper = callback_-> wrapper_ . lock ()) {
83- // Capture callback_ and not wrapper_. If callback_ is deallocated or the
84- // JSVM is shutdown before the async task is scheduled, the underlying
85- // function will have been deallocated .
86- auto fn = [callback = callback_ , callImpl = std::move (callImpl)](jsi::Runtime &rt) {
87- if (auto wrapper2 = callback->wrapper_ . lock ()) {
88- callImpl (rt, wrapper2-> callback ());
82+ auto callback = callback_;
83+ if ( auto jsInvoker = callback-> function_ . lockInvoker ()) {
84+ // Retain native ownership until this task is destroyed, without retaining
85+ // a JavaScript value while the task is queued or discarded .
86+ auto fn = [callback = std::move (callback) , callImpl = std::move (callImpl)](jsi::Runtime &rt) {
87+ if (auto function = callback->function_ . lockOnJS ()) {
88+ callImpl (rt, function. value ());
8989 }
9090 };
9191
92- auto &jsInvoker = wrapper->jsInvoker ();
9392 if (priority) {
94- jsInvoker. invokeAsync (*priority, std::move (fn));
93+ jsInvoker-> invokeAsync (*priority, std::move (fn));
9594 } else {
96- jsInvoker. invokeAsync (std::move (fn));
95+ jsInvoker-> invokeAsync (std::move (fn));
9796 }
9897 }
9998 }
10099};
101100
102- // You must ensure that when invoking this you're located on the JS thread, or
103- // have exclusive control of the JS VM context. If you cannot ensure this, use
104- // AsyncCallback instead.
101+ // Invoke only on the JavaScript thread. Use AsyncCallback from other threads.
105102template <typename R, typename ... Args>
106103class SyncCallback <R(Args...)> {
107104 public:
108105 SyncCallback (jsi::Runtime &rt, jsi::Function function, std::shared_ptr<CallInvoker> jsInvoker)
109- : wrapper_(CallbackWrapper::createWeak( std::move(function), rt, std::move(jsInvoker) ))
106+ : function_(rt, std::move(function), std::move(jsInvoker))
110107 {
111108 }
112109
113- // Disallow copying, as we can no longer safely destroy the callback
114- // from the destructor if there's multiple copies
115110 SyncCallback (const SyncCallback &) = delete ;
116111 SyncCallback &operator =(const SyncCallback &) = delete ;
117-
118- // Allow move
119- SyncCallback (SyncCallback &&other) noexcept : wrapper_(std::move(other.wrapper_)) {}
120-
121- SyncCallback &operator =(SyncCallback &&other) noexcept
122- {
123- wrapper_ = std::move (other.wrapper_ );
124- return *this ;
125- }
126-
127- ~SyncCallback ()
128- {
129- if (auto wrapper = wrapper_.lock ()) {
130- wrapper->destroy ();
131- }
132- }
112+ SyncCallback (SyncCallback &&) noexcept = default ;
113+ SyncCallback &operator =(SyncCallback &&) noexcept = default ;
133114
134115 R operator ()(Args... args) const
135116 {
@@ -138,21 +119,23 @@ class SyncCallback<R(Args...)> {
138119
139120 R call (Args... args) const
140121 {
141- auto wrapper = wrapper_. lock ();
122+ auto function = function_. lockOnJS ();
142123
143- // If the wrapper has been deallocated, we can no longer provide a return
144- // value consistently, so our only option is to throw
145- if (!wrapper ) {
124+ // If the function has been deallocated, we can no longer provide a return
125+ // value consistently, so our only option is to throw.
126+ if (!function ) {
146127 if constexpr (std::is_void_v<R>) {
147128 return ;
148129 } else {
149130 throw std::runtime_error (" Failed to call invalidated sync callback" );
150131 }
151132 }
152133
153- auto &callback = wrapper->callback ();
154- auto &rt = wrapper->runtime ();
155- auto jsInvoker = wrapper->jsInvokerPtr ();
134+ // JavaScript can destroy this native callback during the call; use only
135+ // the retained value and local runtime/invoker references afterward.
136+ auto &callback = function.value ();
137+ auto &rt = function.runtime ();
138+ auto jsInvoker = function.jsInvokerPtr ();
156139
157140 if constexpr (std::is_void_v<R>) {
158141 callback.call (rt, bridging::toJs (rt, std::forward<Args>(args), jsInvoker)...);
@@ -177,8 +160,7 @@ class SyncCallback<R(Args...)> {
177160 return call (std::move (std::get<Index>(args))...);
178161 }
179162
180- // Held weakly so lifetime is managed by LongLivedObjectCollection.
181- std::weak_ptr<CallbackWrapper> wrapper_;
163+ LongLivedObjectHandle<jsi::Function> function_;
182164};
183165
184166template <typename ... Args>
@@ -191,7 +173,7 @@ struct Bridging<AsyncCallback<Args...>> {
191173
192174 static jsi::Function toJs (jsi::Runtime &rt, const AsyncCallback<Args...> &value)
193175 {
194- return value. callback_ -> function_ . getFunction (rt);
176+ return Bridging<SyncCallback< void (Args...)>>:: toJs (rt, *value. callback_ );
195177 }
196178};
197179
@@ -205,7 +187,10 @@ struct Bridging<SyncCallback<R(Args...)>> {
205187
206188 static jsi::Function toJs (jsi::Runtime &rt, const SyncCallback<R(Args...)> &value)
207189 {
208- return value.function_ .getFunction (rt);
190+ if (auto function = value.function_ .lockOnJS ()) {
191+ return function.value ().getFunction (rt);
192+ }
193+ throw jsi::JSError (rt, " Failed to get invalidated callback" );
209194 }
210195};
211196
0 commit comments