summaryrefslogtreecommitdiffstats
path: root/src/test/crimson/test_interruptible_future.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/crimson/test_interruptible_future.cc')
-rw-r--r--src/test/crimson/test_interruptible_future.cc301
1 files changed, 301 insertions, 0 deletions
diff --git a/src/test/crimson/test_interruptible_future.cc b/src/test/crimson/test_interruptible_future.cc
new file mode 100644
index 000000000..bb938de24
--- /dev/null
+++ b/src/test/crimson/test_interruptible_future.cc
@@ -0,0 +1,301 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <seastar/core/sleep.hh>
+
+#include "test/crimson/gtest_seastar.h"
+
+#include "crimson/common/interruptible_future.h"
+#include "crimson/common/log.h"
+
+using namespace crimson;
+
+class test_interruption : public std::exception
+{};
+
+class TestInterruptCondition {
+public:
+ TestInterruptCondition(bool interrupt)
+ : interrupt(interrupt) {}
+
+ template <typename T>
+ std::optional<T> may_interrupt() {
+ if (interrupt) {
+ return seastar::futurize<T>::make_exception_future(test_interruption());
+ } else {
+ return std::optional<T>();
+ }
+ }
+
+ template <typename T>
+ static constexpr bool is_interruption_v = std::is_same_v<T, test_interruption>;
+
+ static bool is_interruption(std::exception_ptr& eptr) {
+ if (*eptr.__cxa_exception_type() == typeid(test_interruption))
+ return true;
+ return false;
+ }
+private:
+ bool interrupt = false;
+};
+
+namespace crimson::interruptible {
+template
+thread_local interrupt_cond_t<TestInterruptCondition>
+interrupt_cond<TestInterruptCondition>;
+}
+
+TEST_F(seastar_test_suite_t, basic)
+{
+ using interruptor =
+ interruptible::interruptor<TestInterruptCondition>;
+ run_async([] {
+ interruptor::with_interruption(
+ [] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return interruptor::make_interruptible(seastar::now())
+ .then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ }).then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return errorator<ct_error::enoent>::make_ready_future<>();
+ }).safe_then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::now();
+ }, errorator<ct_error::enoent>::all_same_way([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ })
+ );
+ }, [](std::exception_ptr) {}, false).get0();
+
+ interruptor::with_interruption(
+ [] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return interruptor::make_interruptible(seastar::now())
+ .then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ });
+ }, [](std::exception_ptr) {
+ ceph_assert(!interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::now();
+ }, true).get0();
+
+
+ });
+}
+
+TEST_F(seastar_test_suite_t, loops)
+{
+ using interruptor =
+ interruptible::interruptor<TestInterruptCondition>;
+ std::cout << "testing interruptible loops" << std::endl;
+ run_async([] {
+ std::cout << "beginning" << std::endl;
+ interruptor::with_interruption(
+ [] {
+ std::cout << "interruptiion enabled" << std::endl;
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return interruptor::make_interruptible(seastar::now())
+ .then_interruptible([] {
+ std::cout << "test seastar future do_for_each" << std::endl;
+ std::vector<int> vec = {1, 2};
+ return seastar::do_with(std::move(vec), [](auto& vec) {
+ return interruptor::do_for_each(std::begin(vec), std::end(vec), [](int) {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::now();
+ });
+ });
+ }).then_interruptible([] {
+ std::cout << "test interruptible seastar future do_for_each" << std::endl;
+ std::vector<int> vec = {1, 2};
+ return seastar::do_with(std::move(vec), [](auto& vec) {
+ return interruptor::do_for_each(std::begin(vec), std::end(vec), [](int) {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return interruptor::make_interruptible(seastar::now());
+ });
+ });
+ }).then_interruptible([] {
+ std::cout << "test seastar future repeat" << std::endl;
+ return interruptor::repeat([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return interruptor::make_interruptible(
+ seastar::make_ready_future<
+ seastar::stop_iteration>(
+ seastar::stop_iteration::yes));
+ });
+ }).then_interruptible([] {
+ std::cout << "test interruptible seastar future repeat" << std::endl;
+ return interruptor::repeat([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::make_ready_future<
+ seastar::stop_iteration>(
+ seastar::stop_iteration::yes);
+ });
+ }).then_interruptible([] {
+ std::cout << "test interruptible errorated future do_for_each" << std::endl;
+ std::vector<int> vec = {1, 2};
+ return seastar::do_with(std::move(vec), [](auto& vec) {
+ using namespace std::chrono_literals;
+ return interruptor::make_interruptible(seastar::now()).then_interruptible([&vec] {
+ return interruptor::do_for_each(std::begin(vec), std::end(vec), [](int) {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return interruptor::make_interruptible(
+ errorator<ct_error::enoent>::make_ready_future<>());
+ }).safe_then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::now();
+ }, errorator<ct_error::enoent>::all_same_way([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ }));
+ });
+ });
+ }).then_interruptible([] {
+ std::cout << "test errorated future do_for_each" << std::endl;
+ std::vector<int> vec;
+ // set a big enough iteration times to test if there is stack overflow in do_for_each
+ for (int i = 0; i < 1000000; i++) {
+ vec.push_back(i);
+ }
+ return seastar::do_with(std::move(vec), [](auto& vec) {
+ using namespace std::chrono_literals;
+ return interruptor::make_interruptible(seastar::now()).then_interruptible([&vec] {
+ return interruptor::do_for_each(std::begin(vec), std::end(vec), [](int) {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return errorator<ct_error::enoent>::make_ready_future<>();
+ }).safe_then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::now();
+ }, errorator<ct_error::enoent>::all_same_way([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ }));
+ });
+ });
+ }).then_interruptible([] {
+ ceph_assert(interruptible::interrupt_cond<TestInterruptCondition>.interrupt_cond);
+ return seastar::now();
+ });
+ }, [](std::exception_ptr) {}, false).get0();
+ });
+}
+
+using base_intr = interruptible::interruptor<TestInterruptCondition>;
+
+using base_ertr = errorator<ct_error::enoent, ct_error::eagain>;
+using base_iertr = interruptible::interruptible_errorator<
+ TestInterruptCondition,
+ base_ertr>;
+
+using base2_ertr = base_ertr::extend<ct_error::input_output_error>;
+using base2_iertr = interruptible::interruptible_errorator<
+ TestInterruptCondition,
+ base2_ertr>;
+
+template <typename F>
+auto with_intr(F &&f) {
+ return base_intr::with_interruption_to_error<ct_error::eagain>(
+ std::forward<F>(f),
+ TestInterruptCondition(false));
+}
+
+TEST_F(seastar_test_suite_t, errorated)
+{
+ run_async([] {
+ base_ertr::future<> ret = with_intr(
+ []() {
+ return base_iertr::now();
+ }
+ );
+ ret.unsafe_get0();
+ });
+}
+
+TEST_F(seastar_test_suite_t, errorated_value)
+{
+ run_async([] {
+ base_ertr::future<int> ret = with_intr(
+ []() {
+ return base_iertr::make_ready_future<int>(
+ 1
+ );
+ });
+ EXPECT_EQ(ret.unsafe_get0(), 1);
+ });
+}
+
+TEST_F(seastar_test_suite_t, expand_errorated_value)
+{
+ run_async([] {
+ base2_ertr::future<> ret = with_intr(
+ []() {
+ return base_iertr::make_ready_future<int>(
+ 1
+ ).si_then([](auto) {
+ return base2_iertr::make_ready_future<>();
+ });
+ });
+ ret.unsafe_get0();
+ });
+}
+
+TEST_F(seastar_test_suite_t, interruptible_async)
+{
+ using interruptor =
+ interruptible::interruptor<TestInterruptCondition>;
+
+ run_async([] {
+ interruptor::with_interruption([] {
+ auto fut = interruptor::async([] {
+ interruptor::make_interruptible(
+ seastar::sleep(std::chrono::milliseconds(10))).get();
+ ceph_assert(interruptible::interrupt_cond<
+ TestInterruptCondition>.interrupt_cond);
+ ceph_assert(interruptible::interrupt_cond<
+ TestInterruptCondition>.ref_count == 1);
+ });
+ ceph_assert(interruptible::interrupt_cond<
+ TestInterruptCondition>.interrupt_cond);
+ ceph_assert(interruptible::interrupt_cond<
+ TestInterruptCondition>.ref_count == 1);
+ return fut;
+ }, [](std::exception_ptr) {}, false).get0();
+ });
+}
+
+TEST_F(seastar_test_suite_t, DISABLED_nested_interruptors)
+{
+ run_async([] {
+ base_ertr::future<> ret = with_intr(
+ []() {
+ return base_iertr::now().safe_then_interruptible([]() {
+ return with_intr(
+ []() {
+ return base_iertr::now();
+ }
+ );
+ });
+ }
+ );
+ ret.unsafe_get0();
+ });
+}
+
+#if 0
+// This seems to cause a hang in the gcc-9 linker on bionic
+TEST_F(seastar_test_suite_t, handle_error)
+{
+ run_async([] {
+ base_ertr::future<> ret = with_intr(
+ []() {
+ return base2_iertr::make_ready_future<int>(
+ 1
+ ).handle_error_interruptible(
+ base_iertr::pass_further{},
+ ct_error::assert_all{"crash on eio"}
+ ).si_then([](auto) {
+ return base_iertr::now();
+ });
+ });
+ ret.unsafe_get0();
+ });
+}
+#endif