Allow AsyncCallback to be used with lambda (#39717)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/39717

AsyncCallback is a better abstraction than the current usage of WeakCallbackWrapper and RAIICallbackWrapperDestroyer, and has way fewer gotchas. Making a few changes here to make it easier to use in various scenarios and match the behaviour we're already seeing in CallbackWrapper.

1) Remove the explicit copy constructor, since this prevents an automatic move constructor from being generated
2) Add a call variant which takes a lambda, for callers which need to manually create JSI arguments
3) Ignore AsyncCallback invocations when the underlying runtime has gone away.

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D49684248

fbshipit-source-id: 8b49ec22cc409572ead80a85b10a190994bf0dd5
This commit is contained in:
Pieter De Baets
2023-10-03 07:55:17 -07:00
committed by Facebook GitHub Bot
parent f174f028cf
commit 0e10ee6ac6
@@ -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>(args)...);
}
void call(Args... args) const {
callInternal(std::nullopt, std::forward<Args>(args)...);
callWithArgs(std::nullopt, std::forward<Args>(args)...);
}
void callWithPriority(SchedulerPriority priority, Args... args) const {
callInternal(priority, std::forward<Args>(args)...);
callWithArgs(priority, std::forward<Args>(args)...);
}
void call(
std::function<void(jsi::Runtime&, jsi::Function&)>&& callImpl) const {
callWithFunction(std::nullopt, std::move(callImpl));
}
void callWithPriority(
SchedulerPriority priority,
std::function<void(jsi::Runtime&, jsi::Function&)>&& callImpl) const {
callWithFunction(priority, std::move(callImpl));
}
private:
@@ -49,22 +57,39 @@ class AsyncCallback {
std::shared_ptr<SyncCallback<void(Args...)>> callback_;
void callInternal(std::optional<SchedulerPriority> priority, Args... args)
void callWithArgs(std::optional<SchedulerPriority> 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::tuple<Args...>>(
std::make_tuple(std::forward<Args>(args)...))] {
callback->apply(std::move(*argsPtr));
};
if (wrapper) {
auto fn = [callback = callback_,
argsPtr = std::make_shared<std::tuple<Args...>>(
std::make_tuple(std::forward<Args>(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<SchedulerPriority> priority,
std::function<void(jsi::Runtime&, jsi::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(Args...)> {
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<R>) {
return;
} else {
throw std::runtime_error("Failed to call invalidated sync callback");
}
}
auto& callback = wrapper->callback();