blob: 2c6e2f51b27ed97442195f9ecaae39a3a3e8f9a2 [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_FUNCTION_H_
#define LIB_ASYNC_PATTERNS_CPP_FUNCTION_H_
#include <lib/async_patterns/cpp/internal/tag.h>
#include <lib/async_patterns/cpp/internal/task_queue.h>
#include <lib/async_patterns/cpp/pending_call.h>
#include <lib/async_patterns/cpp/sendable.h>
#include <lib/fit/function.h>
#include <zircon/assert.h>
namespace async_patterns {
template <typename Owner>
class Receiver;
template <typename F>
class Function;
// An asynchronous |Function| that will always execute on the async dispatcher
// associated with a |Receiver|. Invoking this function translates to posting a
// task to the destination dispatcher. It will not block the caller.
//
// The receiver may not necessarily receive the function call. The call will be
// a no-op if:
// - The |Receiver| object goes out of scope.
// - The async dispatcher of the |Receiver| shuts down.
//
// A function can be invoked many times, and distributed to many senders. It is
// akin to a multi-producer, single-consumer, uni-directional channel. 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.
template <typename ReturnType, typename... Args>
class Function<ReturnType(Args...)> {
public:
// Schedules the call to be asynchronously run on the receiver's
// dispatcher.
//
// See |async_patterns::BindForSending| for detailed requirements on |args|.
//
// This operator returns a pending call. You may either:
//
// - Make a fire-and-forget call, by discarding the returned object, or
// - Get a promise carrying the return value of the function by calling
// `promise()` on the object, yielding a |fpromise::promise<ReturnType>|, or
// - Call `Then()` on the object and pass a |Callback<void(ReturnType)>|
//
// See |async_patterns::PendingCall| for details.
//
// Example:
//
// async_patterns::Function<int(std::string)> parse = ...;
//
// // Ignore the returned integer.
// parse(std::string("abc"));
//
// // Get a promise that will resolve when the function is asynchronously
// // executed on the receiver's async dispatcher.
// fpromise::promise<int> promise = parse(std::string("abc")).promise();
//
auto operator()(Args... args) {
ZX_DEBUG_ASSERT(task_queue_handle_.has_value());
return PendingCall{
BindForSending([f = function_](auto&&... args) { return (*f)(std::move(args)...); },
std::forward<Args>(args)...),
internal::SubmitWithTaskQueueHandle{task_queue_handle_}, internal::Tag<ReturnType>{}};
}
// Returns a functor that performs the same actions as this |Function|, but returns
// void, instead of potentially a promise object. This is useful when converting the
// |Function| into a |fit::function<void(ReturnType)|.
auto ignore_result() && {
return [function = std::move(*this)](Args... args) mutable {
function(std::forward<Args>(args)...);
};
}
private:
// The worst case scenario is a pointer-to-member (2 words) and a weak pointer (2 words).
// We can improve this further using custom weak pointer types if necessary.
using FunctionType = fit::inline_function<ReturnType(Args...), sizeof(void*) * 4>;
template <typename Owner>
friend class ::async_patterns::Receiver;
explicit Function(internal::TaskQueueHandle handle, FunctionType callback)
: task_queue_handle_(std::move(handle)),
function_(std::make_shared<FunctionType>(std::move(callback))) {}
internal::TaskQueueHandle task_queue_handle_;
// |function_| is reference counted because both |Function| and the remote
// task queue need to access it. |Function| uses it to schedule more calls.
// The task queue uses it to invoke user supplied logic.
std::shared_ptr<FunctionType> function_;
};
} // namespace async_patterns
#endif // LIB_ASYNC_PATTERNS_CPP_FUNCTION_H_