diff --git a/packages/react-native/ReactCommon/react/bridging/Function.h b/packages/react-native/ReactCommon/react/bridging/Function.h index f479c509dc4..d934ddbca35 100644 --- a/packages/react-native/ReactCommon/react/bridging/Function.h +++ b/packages/react-native/ReactCommon/react/bridging/Function.h @@ -29,19 +29,27 @@ class AsyncCallback { std::move(function), std::move(jsInvoker))) {} - AsyncCallback(const AsyncCallback&) = default; - AsyncCallback& operator=(const AsyncCallback&) = default; - void operator()(Args... args) const { call(std::forward(args)...); } void call(Args... args) const { - callInternal(std::nullopt, std::forward(args)...); + callWithArgs(std::nullopt, std::forward(args)...); } void callWithPriority(SchedulerPriority priority, Args... args) const { - callInternal(priority, std::forward(args)...); + callWithArgs(priority, std::forward(args)...); + } + + void call( + std::function&& callImpl) const { + callWithFunction(std::nullopt, std::move(callImpl)); + } + + void callWithPriority( + SchedulerPriority priority, + std::function&& callImpl) const { + callWithFunction(priority, std::move(callImpl)); } private: @@ -49,22 +57,39 @@ class AsyncCallback { std::shared_ptr> callback_; - void callInternal(std::optional priority, Args... args) + void callWithArgs(std::optional priority, Args... args) const { auto wrapper = callback_->wrapper_.lock(); - if (!wrapper) { - throw std::runtime_error("Failed to call invalidated async callback"); - } - auto fn = [callback = callback_, - argsPtr = std::make_shared>( - std::make_tuple(std::forward(args)...))] { - callback->apply(std::move(*argsPtr)); - }; + if (wrapper) { + auto fn = [callback = callback_, + argsPtr = std::make_shared>( + std::make_tuple(std::forward(args)...))] { + callback->apply(std::move(*argsPtr)); + }; - if (priority) { - wrapper->jsInvoker().invokeAsync(*priority, std::move(fn)); - } else { - wrapper->jsInvoker().invokeAsync(std::move(fn)); + if (priority) { + wrapper->jsInvoker().invokeAsync(*priority, std::move(fn)); + } else { + wrapper->jsInvoker().invokeAsync(std::move(fn)); + } + } + } + + void callWithFunction( + std::optional priority, + std::function&& callImpl) const { + auto wrapper = callback_->wrapper_.lock(); + if (wrapper) { + auto fn = [wrapper = std::move(wrapper), + callImpl = std::move(callImpl)]() { + callImpl(wrapper->runtime(), wrapper->callback()); + }; + + if (priority) { + wrapper->jsInvoker().invokeAsync(*priority, std::move(fn)); + } else { + wrapper->jsInvoker().invokeAsync(std::move(fn)); + } } } }; @@ -97,8 +122,15 @@ class SyncCallback { R call(Args... args) const { auto wrapper = wrapper_.lock(); + + // If the wrapper has been deallocated, we can no longer provide a return + // value consistently, so our only option is to throw if (!wrapper) { - throw std::runtime_error("Failed to call invalidated sync callback"); + if constexpr (std::is_void_v) { + return; + } else { + throw std::runtime_error("Failed to call invalidated sync callback"); + } } auto& callback = wrapper->callback();