blob: 3df6d14b5f3a963cb3a81faf60f33b8f1d508b23 [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_CALLBACK_H_
#define LIB_ASYNC_PATTERNS_CPP_CALLBACK_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 Callback;
/// An asynchronous |Callback| that will always execute on the async dispatcher
/// associated with a |Receiver|. Invoking this callback translates to posting a
/// task to the destination dispatcher. It will not block the caller.
///
/// The receiver may not necessarily receive the callback. The callback will be
/// a no-op if:
/// - The |Receiver| object goes out of scope.
/// - The async dispatcher of the |Receiver| shuts down.
///
/// A callback can only be invoked once. It is akin to a one-shot,
/// 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 Callback<ReturnType(Args...)> {
public:
/// Schedules the callback 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::Callback<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_ASSERT(task_queue_handle_.has_value());
return PendingCall{BindForSending(std::move(callback_), std::forward<Args>(args)...),
internal::SubmitWithTaskQueueHandle{std::move(task_queue_handle_)},
internal::Tag<ReturnType>{}};
}
/// Returns a functor that performs the same actions as this |Callback|, but returns
/// void, instead of potentially a promise object. This is useful when converting the
/// |Callback| into a |fit::callback<void(ReturnType)|.
auto ignore_result() && {
return [callback = std::move(*this)](Args... args) mutable {
callback(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 CallbackType = fit::inline_callback<ReturnType(Args...), sizeof(void*) * 4>;
template <typename Owner>
friend class ::async_patterns::Receiver;
explicit Callback(internal::TaskQueueHandle handle, CallbackType callback)
: task_queue_handle_(std::move(handle)), callback_(std::move(callback)) {}
internal::TaskQueueHandle task_queue_handle_;
CallbackType callback_;
};
} // namespace async_patterns
#endif // LIB_ASYNC_PATTERNS_CPP_CALLBACK_H_