From e6918187568dbd01842d8d1d2c808ce16a894239 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 21 Apr 2024 13:54:28 +0200 Subject: Adding upstream version 18.2.2. Signed-off-by: Daniel Baumann --- src/common/async/waiter.h | 223 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 src/common/async/waiter.h (limited to 'src/common/async/waiter.h') diff --git a/src/common/async/waiter.h b/src/common/async/waiter.h new file mode 100644 index 000000000..219a27cf7 --- /dev/null +++ b/src/common/async/waiter.h @@ -0,0 +1,223 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2004-2006 Sage Weil + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#ifndef CEPH_COMMON_WAITER_H +#define CEPH_COMMON_WAITER_H + +#include +#include + +#include + +#include "include/ceph_assert.h" +#include "include/function2.hpp" + +#include "common/ceph_mutex.h" + +namespace ceph::async { +namespace detail { +// For safety reasons (avoiding undefined behavior around sequence +// points) std::reference_wrapper disallows move construction. This +// harms us in cases where we want to pass a reference in to something +// that unavoidably moves. +// +// It should not be used generally. +template +class rvalue_reference_wrapper { +public: + // types + using type = T; + + rvalue_reference_wrapper(T& r) noexcept + : p(std::addressof(r)) {} + + // We write our semantics to match those of reference collapsing. If + // we're treated as an lvalue, collapse to one. + + rvalue_reference_wrapper(const rvalue_reference_wrapper&) noexcept = default; + rvalue_reference_wrapper(rvalue_reference_wrapper&&) noexcept = default; + + // assignment + rvalue_reference_wrapper& operator=( + const rvalue_reference_wrapper& x) noexcept = default; + rvalue_reference_wrapper& operator=( + rvalue_reference_wrapper&& x) noexcept = default; + + operator T& () const noexcept { + return *p; + } + T& get() const noexcept { + return *p; + } + + operator T&& () noexcept { + return std::move(*p); + } + T&& get() noexcept { + return std::move(*p); + } + + template + std::result_of_t operator ()(Args&&... args ) const { + return (*p)(std::forward(args)...); + } + + template + std::result_of_t operator ()(Args&&... args ) { + return std::move(*p)(std::forward(args)...); + } + +private: + T* p; +}; + +class base { +protected: + ceph::mutex lock = ceph::make_mutex("ceph::async::detail::base::lock"); + ceph::condition_variable cond; + bool has_value = false; + + ~base() = default; + + auto wait_base() { + std::unique_lock l(lock); + cond.wait(l, [this](){ return has_value; }); + return l; + } + + auto exec_base() { + std::unique_lock l(lock); + // There's no really good way to handle being called twice + // without being reset. + ceph_assert(!has_value); + has_value = true; + cond.notify_one(); + return l; + } +}; +} + +// waiter is a replacement for C_SafeCond and friends. It is the +// moral equivalent of a future but plays well with a world of +// callbacks. +template +class waiter; + +template<> +class waiter<> final : public detail::base { +public: + void wait() { + wait_base(); + has_value = false; + } + + void operator()() { + exec_base(); + } + + auto ref() { + return detail::rvalue_reference_wrapper(*this); + } + + + operator fu2::unique_function() { + return fu2::unique_function(ref()); + } +}; + +template +class waiter final : public detail::base { + std::aligned_storage_t ret; + +public: + Ret wait() { + auto l = wait_base(); + auto r = reinterpret_cast(&ret); + auto t = std::move(*r); + r->~Ret(); + has_value = false; + return t; + } + + void operator()(Ret&& _ret) { + auto l = exec_base(); + auto r = reinterpret_cast(&ret); + *r = std::move(_ret); + } + + void operator()(const Ret& _ret) { + auto l = exec_base(); + auto r = reinterpret_cast(&ret); + *r = std::move(_ret); + } + + auto ref() { + return detail::rvalue_reference_wrapper(*this); + } + + operator fu2::unique_function() { + return fu2::unique_function(ref()); + } + + ~waiter() { + if (has_value) + reinterpret_cast(&ret)->~Ret(); + } +}; + +template +class waiter final : public detail::base { + std::tuple ret; + +public: + std::tuple wait() { + using std::tuple; + auto l = wait_base(); + return std::move(ret); + auto r = reinterpret_cast*>(&ret); + auto t = std::move(*r); + r->~tuple(); + has_value = false; + return t; + } + + void operator()(Ret&&... _ret) { + auto l = exec_base(); + auto r = reinterpret_cast*>(&ret); + *r = std::forward_as_tuple(_ret...); + } + + void operator()(const Ret&... _ret) { + auto l = exec_base(); + auto r = reinterpret_cast*>(&ret); + *r = std::forward_as_tuple(_ret...); + } + + auto ref() { + return detail::rvalue_reference_wrapper(*this); + } + + operator fu2::unique_function() { + return fu2::unique_function(ref()); + } + + ~waiter() { + using std::tuple; + if (has_value) + reinterpret_cast*>(&ret)->~tuple(); + } +}; +} + +#endif // CEPH_COMMON_WAITER_H -- cgit v1.2.3