]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/seastar/src/core/future.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / seastar / src / core / future.cc
index 614193b69083706fff015d3cd16331e5db476fc8..d29cd66d1e7a6504c97f6c0ade527f2fecee6382 100644 (file)
 
 #include <seastar/core/future.hh>
 #include <seastar/core/reactor.hh>
+#include <seastar/core/thread.hh>
 #include <seastar/core/report_exception.hh>
 #include <seastar/util/backtrace.hh>
 
 namespace seastar {
+
+// We can't test future_state_base directly because its private
+// destructor is protected.
+static_assert(std::is_nothrow_move_constructible<future_state<std::tuple<int>>>::value,
+              "future_state's move constructor must not throw");
+
+static_assert(sizeof(future_state<std::tuple<>>) <= 8, "future_state<std::tuple<>> is too large");
+static_assert(sizeof(future_state<std::tuple<long>>) <= 16, "future_state<std::tuple<long>> is too large");
+static_assert(future_state<std::tuple<>>::has_trivial_move_and_destroy, "future_state<std::tuple<>> not trivial");
+#if SEASTAR_API_LEVEL < 5
+static_assert(future_state<std::tuple<long>>::has_trivial_move_and_destroy, "future_state<std::tuple<long>> not trivial");
+#else
+static_assert(future_state<long>::has_trivial_move_and_destroy, "future_state<long> not trivial");
+#endif
+
+// We need to be able to move and copy std::exception_ptr in and out
+// of future/promise/continuations without that producing a new
+// exception.
+static_assert(std::is_nothrow_copy_constructible<std::exception_ptr>::value,
+    "std::exception_ptr's copy constructor must not throw");
+static_assert(std::is_nothrow_move_constructible<std::exception_ptr>::value,
+    "std::exception_ptr's move constructor must not throw");
+
 namespace internal {
 
-promise_base::promise_base(promise_base&& x) noexcept
-    : _future(x._future), _state(x._state), _task(std::exchange(x._task, nullptr)) {
+static_assert(std::is_empty<uninitialized_wrapper<std::tuple<>>>::value, "This should still be empty");
+
+void promise_base::move_it(promise_base&& x) noexcept {
+    // Don't use std::exchange to make sure x's values are nulled even
+    // if &x == this.
+    _task = x._task;
+    x._task = nullptr;
+    _state = x._state;
     x._state = nullptr;
+    _future = x._future;
     if (auto* fut = _future) {
         fut->detach_promise();
         fut->_promise = this;
     }
 }
 
-promise_base::~promise_base() noexcept {
+static void set_to_broken_promise(future_state_base& state) noexcept {
+    try {
+        // Constructing broken_promise may throw (std::logic_error ctor is not noexcept).
+        state.set_exception(std::make_exception_ptr(broken_promise{}));
+    } catch (...) {
+        state.set_exception(std::current_exception());
+    }
+}
+
+promise_base::promise_base(promise_base&& x) noexcept {
+    move_it(std::move(x));
+}
+
+void promise_base::clear() noexcept {
+    if (__builtin_expect(bool(_task), false)) {
+        assert(_state && !_state->available());
+        set_to_broken_promise(*_state);
+        ::seastar::schedule(std::exchange(_task, nullptr));
+    }
     if (_future) {
         assert(_state);
-        assert(_state->available() || !_task);
+        if (!_state->available()) {
+            set_to_broken_promise(*_state);
+        }
         _future->detach_promise();
-    } else if (__builtin_expect(bool(_task), false)) {
-        assert(_state && !_state->available());
-        _state->set_to_broken_promise();
-        ::seastar::schedule(std::exchange(_task, nullptr));
     }
 }
 
+promise_base& promise_base::operator=(promise_base&& x) noexcept {
+    clear();
+    move_it(std::move(x));
+    return *this;
+}
+
+void promise_base::set_to_current_exception() noexcept {
+    set_exception(std::current_exception());
+}
+
 template <promise_base::urgent Urgent>
 void promise_base::make_ready() noexcept {
     if (_task) {
-        _state = nullptr;
-        if (Urgent == urgent::yes && !need_preempt()) {
+        if (Urgent == urgent::yes) {
             ::seastar::schedule_urgent(std::exchange(_task, nullptr));
         } else {
             ::seastar::schedule(std::exchange(_task, nullptr));
@@ -62,10 +118,10 @@ void promise_base::make_ready() noexcept {
 
 template void promise_base::make_ready<promise_base::urgent::no>() noexcept;
 template void promise_base::make_ready<promise_base::urgent::yes>() noexcept;
+}
 
 template
-future<> internal::current_exception_as_future() noexcept;
-}
+future<> current_exception_as_future() noexcept;
 
 /**
  * engine_exit() exits the reactor. It should be given a pointer to the
@@ -83,19 +139,72 @@ void engine_exit(std::exception_ptr eptr) {
 
 broken_promise::broken_promise() : logic_error("broken promise") { }
 
-future_state_base future_state_base::current_exception() {
-    return future_state_base(std::current_exception());
+future_state_base::future_state_base(current_exception_future_marker) noexcept
+    : future_state_base(std::current_exception()) { }
+
+void future_state_base::ignore() noexcept {
+    switch (_u.st) {
+    case state::invalid:
+    case state::future:
+    case state::result_unavailable:
+        assert(0 && "invalid state for ignore");
+    case state::result:
+        _u.st = state::result_unavailable;
+        break;
+    default:
+        // Ignore the exception
+        _u.take_exception();
+    }
 }
 
-void future_state_base::set_to_broken_promise() noexcept {
-    try {
-        // Constructing broken_promise may throw (std::logic_error ctor is not noexcept).
-        set_exception(std::make_exception_ptr(broken_promise{}));
-    } catch (...) {
-        set_exception(std::current_exception());
+nested_exception::nested_exception(std::exception_ptr inner, std::exception_ptr outer) noexcept
+    : inner(std::move(inner)), outer(std::move(outer)) {}
+
+nested_exception::nested_exception(nested_exception&&) noexcept = default;
+
+nested_exception::nested_exception(const nested_exception&) noexcept = default;
+
+const char* nested_exception::what() const noexcept {
+    return "seastar::nested_exception";
+}
+
+[[noreturn]] void nested_exception::rethrow_nested() const {
+    std::rethrow_exception(outer);
+}
+
+static std::exception_ptr make_nested(std::exception_ptr&& inner, future_state_base&& old) noexcept {
+    std::exception_ptr outer = std::move(old).get_exception();
+    nested_exception nested{std::move(inner), std::move(outer)};
+    return std::make_exception_ptr<nested_exception>(std::move(nested));
+}
+
+future_state_base::future_state_base(nested_exception_marker, future_state_base&& n, future_state_base&& old) noexcept {
+    std::exception_ptr inner = std::move(n).get_exception();
+    if (!old.failed()) {
+        new (this) future_state_base(std::move(inner));
+    } else {
+        new (this) future_state_base(make_nested(std::move(inner), std::move(old)));
     }
 }
 
+future_state_base::future_state_base(nested_exception_marker, future_state_base&& old) noexcept {
+    if (!old.failed()) {
+        new (this) future_state_base(current_exception_future_marker());
+        return;
+    } else {
+        new (this) future_state_base(make_nested(std::current_exception(), std::move(old)));
+    }
+}
+
+void future_state_base::rethrow_exception() && {
+    // Move ex out so future::~future() knows we've handled it
+    std::rethrow_exception(std::move(*this).get_exception());
+}
+
+void future_state_base::rethrow_exception() const& {
+    std::rethrow_exception(_u.ex);
+}
+
 void report_failed_future(const std::exception_ptr& eptr) noexcept {
     ++engine()._abandoned_failed_futures;
     seastar_logger.warn("Exceptional future ignored: {}, backtrace: {}", eptr, current_backtrace());
@@ -105,4 +214,49 @@ void report_failed_future(const future_state_base& state) noexcept {
     report_failed_future(state._u.ex);
 }
 
+void report_failed_future(future_state_base::any&& state) noexcept {
+    report_failed_future(std::move(state).take_exception());
+}
+
+void with_allow_abandoned_failed_futures(unsigned count, noncopyable_function<void ()> func) {
+    auto before = engine()._abandoned_failed_futures;
+    func();
+    auto after = engine()._abandoned_failed_futures;
+    assert(after - before == count);
+    engine()._abandoned_failed_futures = before;
+}
+
+namespace {
+class thread_wake_task final : public task {
+    thread_context* _thread;
+public:
+    thread_wake_task(thread_context* thread) noexcept : _thread(thread) {}
+    virtual void run_and_dispose() noexcept override {
+        thread_impl::switch_in(_thread);
+        // no need to delete, since this is always allocated on
+        // _thread's stack.
+    }
+    /// Returns the task which is waiting for this thread to be done, or nullptr.
+    virtual task* waiting_task() noexcept override {
+        return _thread->waiting_task();
+    }
+};
+}
+
+void internal::future_base::do_wait() noexcept {
+    auto thread = thread_impl::get();
+    assert(thread);
+    thread_wake_task wake_task{thread};
+    wake_task.make_backtrace();
+    _promise->_task = &wake_task;
+    thread_impl::switch_out(thread);
+}
+
+#ifdef SEASTAR_COROUTINES_ENABLED
+void internal::future_base::set_coroutine(task& coroutine) noexcept {
+    assert(_promise);
+    _promise->_task = &coroutine;
+}
+#endif
+
 }