Skip to content

Commit

Permalink
Allow AsyncCallback to be used with lambda (#39717)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #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
  • Loading branch information
javache authored and facebook-github-bot committed Oct 3, 2023
1 parent f174f02 commit 0e10ee6
Showing 1 changed file with 51 additions and 19 deletions.
70 changes: 51 additions & 19 deletions packages/react-native/ReactCommon/react/bridging/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,42 +29,67 @@ 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:
friend Bridging<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");
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));
}
}
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));
}

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));
}
}
}
};
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 0e10ee6

Please sign in to comment.