]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
common: add waiter template
authorAdam C. Emerson <aemerson@redhat.com>
Tue, 9 Feb 2016 04:49:06 +0000 (23:49 -0500)
committerAdam C. Emerson <aemerson@redhat.com>
Fri, 15 May 2020 14:55:10 +0000 (10:55 -0400)
It's no fair taking away people's C_SafeCond without giving them something new
to replace it.

Signed-off-by: Adam C. Emerson <aemerson@redhat.com>
src/common/async/waiter.h [new file with mode: 0644]

diff --git a/src/common/async/waiter.h b/src/common/async/waiter.h
new file mode 100644 (file)
index 0000000..219a27c
--- /dev/null
@@ -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 <sage@newdream.net>
+ *
+ * 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 <condition_variable>
+#include <tuple>
+
+#include <boost/asio/async_result.hpp>
+
+#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<typename T>
+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<typename... Args>
+  std::result_of_t<T&(Args&&...)> operator ()(Args&&... args ) const {
+    return (*p)(std::forward<Args>(args)...);
+  }
+
+  template<typename... Args>
+  std::result_of_t<T&&(Args&&...)> operator ()(Args&&... args ) {
+    return std::move(*p)(std::forward<Args>(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<typename ...S>
+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<void() &&>() {
+    return fu2::unique_function<void() &&>(ref());
+  }
+};
+
+template<typename Ret>
+class waiter<Ret> final : public detail::base {
+  std::aligned_storage_t<sizeof(Ret)> ret;
+
+public:
+  Ret wait() {
+    auto l = wait_base();
+    auto r = reinterpret_cast<Ret*>(&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*>(&ret);
+    *r = std::move(_ret);
+  }
+
+  void operator()(const Ret& _ret) {
+    auto l = exec_base();
+    auto r = reinterpret_cast<Ret*>(&ret);
+    *r = std::move(_ret);
+  }
+
+  auto ref() {
+    return detail::rvalue_reference_wrapper(*this);
+  }
+
+  operator fu2::unique_function<void(Ret) &&>() {
+    return fu2::unique_function<void(Ret) &&>(ref());
+  }
+
+  ~waiter() {
+    if (has_value)
+      reinterpret_cast<Ret*>(&ret)->~Ret();
+  }
+};
+
+template<typename ...Ret>
+class waiter final : public detail::base {
+  std::tuple<Ret...> ret;
+
+public:
+  std::tuple<Ret...> wait() {
+    using std::tuple;
+    auto l = wait_base();
+    return std::move(ret);
+    auto r = reinterpret_cast<std::tuple<Ret...>*>(&ret);
+    auto t = std::move(*r);
+    r->~tuple<Ret...>();
+    has_value = false;
+    return t;
+  }
+
+  void operator()(Ret&&... _ret) {
+    auto l = exec_base();
+    auto r = reinterpret_cast<std::tuple<Ret...>*>(&ret);
+    *r = std::forward_as_tuple(_ret...);
+  }
+
+  void operator()(const Ret&... _ret) {
+    auto l = exec_base();
+    auto r = reinterpret_cast<std::tuple<Ret...>*>(&ret);
+    *r = std::forward_as_tuple(_ret...);
+  }
+
+  auto ref() {
+    return detail::rvalue_reference_wrapper(*this);
+  }
+
+  operator fu2::unique_function<void(Ret...) &&>() {
+    return fu2::unique_function<void(Ret...) &&>(ref());
+  }
+
+  ~waiter() {
+    using std::tuple;
+    if (has_value)
+      reinterpret_cast<tuple<Ret...>*>(&ret)->~tuple<Ret...>();
+  }
+};
+}
+
+#endif // CEPH_COMMON_WAITER_H