blob: 44ea849951c489c46674cf3a40086913c1397f52 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIB_ASYNC_PATTERNS_CPP_RECEIVER_H_
#define LIB_ASYNC_PATTERNS_CPP_RECEIVER_H_
#include <lib/async/dispatcher.h>
#include <lib/async_patterns/cpp/callback.h>
#include <lib/async_patterns/cpp/function.h>
#include <lib/async_patterns/cpp/internal/receiver_base.h>
#include <lib/fit/function_traits.h>
#include <lib/fit/nullable.h>
namespace async_patterns {
/// A |Receiver| is a hub for an owner object to asynchronously receive messages
/// and calls from other objects living on different async dispatchers. A
/// |Receiver| should be embedded as a member variable of an owner object which
/// wishes to receive messages. The receiver will silently discard pending
/// messages when it destructs, typically as part of its parent.
///
/// The |Receiver| is thread-unsafe, and must be used and managed from a
/// [synchronized dispatcher][synchronized-dispatcher].
///
/// Calls posted to the same |Receiver| will be processed in the order they are
/// made, regardless which |Function|s and |Callback|s they are made from.
///
/// Calls posted to different |Receiver|s living on the same dispatcher are not
/// guaranteed to be processed before or after one another.
///
/// [synchronized-dispatcher]:
/// https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/thread-safe-async#synchronized-dispatcher
template <typename Owner>
class Receiver : private internal::ReceiverBase {
public:
/// Constructs a receiver. |owner| should be `this`. |dispatcher| should be
/// the dispatcher that the current task is running on, and where |Owner|
/// typically lives.
explicit Receiver(Owner* owner, async_dispatcher_t* dispatcher)
: ReceiverBase(dispatcher), owner_(owner) {}
Receiver(const Receiver&) = delete;
Receiver& operator=(const Receiver&) = delete;
Receiver(Receiver&&) = delete;
Receiver& operator=(Receiver&&) = delete;
/// Mints a |Callback| object that holds a capability to send |Args| to the
/// owner object once. When the resulting |Callback| is invoked on some other
/// thread, |member| is scheduled to be called on the |dispatcher|.
///
/// |member| should be a pointer to member function. It should have the
/// function signature `void Owner::SomeMember(Args...)` where Args are some
/// number of arguments.
template <typename Member>
auto Once(Member Owner::*member) {
return BindImpl<Callback, typename fit::callable_traits<Member>::return_type>(
std::mem_fn(member), typename fit::callable_traits<Member>::args{});
}
/// Mints a |Callback| object that holds a capability to send |Args| to the
/// owner object once. When the resulting |Callback| is invoked on some other
/// thread, |callable| is scheduled to be called on the |dispatcher|.
///
/// |callable| should be a lambda without any captures. It should have the
/// function signature `void(Owner*, Args...)` where Args are some number of
/// arguments.
template <typename StatelessLambda>
auto Once(StatelessLambda callable) {
// TODO(https://fxbug.dev/42070737): We'll be able to support lambda with captures
// given compiler tooling that inspects the capture list and determine
// they're safe.
static_assert(internal::is_stateless<StatelessLambda>,
"|callable| must not capture any state.");
return BindImpl<Callback, typename fit::callable_traits<StatelessLambda>::return_type>(
callable, typename Pop<typename fit::callable_traits<StatelessLambda>::args>::pack{});
}
/// Mints a |Function| object that holds a capability to send |Args| to the
/// owner object repeatedly. When the resulting |Function| is invoked on some
/// other thread, |member| is scheduled to be called on the |dispatcher|.
///
/// |member| should be a pointer to member function. It should have the
/// function signature `void Owner::SomeMember(Args...)` where Args are some
/// number of arguments.
template <typename Member>
auto Repeating(Member Owner::*member) {
return BindImpl<Function, typename fit::callable_traits<Member>::return_type>(
std::mem_fn(member), typename fit::callable_traits<Member>::args{});
}
/// Mints a |Function| object that holds a capability to send |Args| to the
/// owner object repeatedly. When the resulting |Function| is invoked on some
/// other thread, |callable| is scheduled to be called on the |dispatcher|.
///
/// |callable| should be a lambda without any captures. It should have the
/// function signature `void(Owner*, Args...)` where Args are some number of
/// arguments.
template <typename StatelessLambda>
auto Repeating(StatelessLambda callable) {
// TODO(https://fxbug.dev/42070737): We'll be able to support lambda with captures
// given compiler tooling that inspects the capture list and determine
// they're safe.
static_assert(internal::is_stateless<StatelessLambda>,
"|callable| must not capture any state.");
return BindImpl<Function, typename fit::callable_traits<StatelessLambda>::return_type>(
callable, typename Pop<typename fit::callable_traits<StatelessLambda>::args>::pack{});
}
private:
template <template <typename... Args> typename FunctionOrCallback, typename ReturnType,
typename Callable, typename... Args>
FunctionOrCallback<ReturnType(Args...)> BindImpl(Callable&& callable,
fit::parameter_pack<Args...>) {
return FunctionOrCallback<ReturnType(Args...)>{
task_queue_handle(),
[callable = std::forward<Callable>(callable), owner = owner_](Args... args) mutable {
internal::CheckArguments<Args...>::Check();
internal::CheckArguments<ReturnType>::Check();
return callable(owner, std::forward<Args>(args)...);
}};
}
// |Pop| pops off the first element of a |fit::parameter_pack|.
template <typename P>
struct Pop;
template <typename First, typename... Rest>
struct Pop<fit::parameter_pack<First, Rest...>> {
using pack = fit::parameter_pack<Rest...>;
};
Owner* owner_;
};
} // namespace async_patterns
#endif // LIB_ASYNC_PATTERNS_CPP_RECEIVER_H_