| // 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_ |