blob: 81c623e2f8cd5d90e5df352595b877676a91beda [file] [log] [blame] [edit]
// 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>
#include <memory>
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>(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(fxbug.dev/119641): 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>(
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>(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(fxbug.dev/119641): 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>(
callable, typename Pop<typename fit::callable_traits<StatelessLambda>::args>::pack{});
}
private:
template <template <typename... Args> typename FunctionOrCallback, typename Callable,
typename... Args>
FunctionOrCallback<Args...> BindImpl(Callable&& callable, fit::parameter_pack<Args...>) {
return FunctionOrCallback<Args...>{
task_queue_handle(),
[callable = std::forward<Callable>(callable), owner = owner_](Args... args) mutable {
internal::CheckArguments<Args...>::Check();
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_