]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
common/async: Use AioCompletion as a completion for coroutines
authorAdam Emerson <aemerson@redhat.com>
Tue, 14 May 2024 19:09:14 +0000 (15:09 -0400)
committerAdam C. Emerson <aemerson@redhat.com>
Tue, 1 Apr 2025 15:10:13 +0000 (11:10 -0400)
I forgot I had this already. Just extend it so we can handle things
like coroutines that return an `exception_ptr` rather than an
`error_code`.

Signed-off-by: Adam Emerson <aemerson@redhat.com>
src/common/async/librados_completion.h
src/test/common/CMakeLists.txt
src/test/common/test_librados_completion.cc [new file with mode: 0644]

index 2fa5555e774754b869b8ece2d95da28366927a31..7bb380895e80b80aa3c4f57772a674a85d1766d1 100644 (file)
  *
  */
 
-#ifndef CEPH_COMMON_ASYNC_LIBRADOS_COMPLETION_H
-#define CEPH_COMMON_ASYNC_LIBRADOS_COMPLETION_H
+#pragma once
 
-#include <atomic>
-#include <condition_variable>
-#include <mutex>
-#include <optional>
-#include <type_traits>
+#include <exception>
 
 #include <boost/asio/async_result.hpp>
 
 #include <boost/system/system_error.hpp>
 
 #include "include/rados/librados.hpp"
+
+#include "common/error_code.h"
+
 #include "librados/AioCompletionImpl.h"
 
 // Allow librados::AioCompletion to be provided as a completion
 // handler. This is only allowed with a signature of
-// (boost::system::error_code) or (). On completion the AioCompletion
-// is completed with the error_code converted to an int with
-// ceph::from_error_code.
+// (int), (boost::system::error_code), (std::exception_ptr), or (). On
+// completion the AioCompletion is completed with the error_code
+// converted to an int with ceph::from_error_code. Exceptions we can't
+// handle any other way are converted to -EIO.
 //
 // async_result::return_type is void.
 
@@ -65,9 +64,9 @@ struct librados_handler {
     rhs.pc = nullptr;
   }
 
-  void operator()(bs::error_code ec) {
+  void operator()(int r) {
     pc->lock.lock();
-    pc->rval = ceph::from_error_code(ec);
+    pc->rval = r;
     pc->complete = true;
     pc->lock.unlock();
 
@@ -89,9 +88,17 @@ struct librados_handler {
     pc = nullptr;
   }
 
+  void operator()(bs::error_code ec) {
+    (*this)(ceph::from_error_code(ec));
+  }
+
   void operator ()() {
     (*this)(bs::error_code{});
   }
+
+  void operator ()(std::exception_ptr e) {
+    (*this)(ceph::from_exception(e));
+  }
 };
 } // namespace detail
 } // namespace ceph::async
@@ -120,6 +127,28 @@ public:
     return;
   }
 };
-}
 
-#endif // !CEPH_COMMON_ASYNC_LIBRADOS_COMPLETION_H
+template<typename ReturnType>
+class async_result<librados::AioCompletion*,
+                  ReturnType(std::exception_ptr)> {
+public:
+  using completion_handler_type = ceph::async::detail::librados_handler;
+  explicit async_result(completion_handler_type&) {};
+  using return_type = void;
+  void get() {
+    return;
+  }
+};
+
+template<typename ReturnType>
+class async_result<librados::AioCompletion*,
+                  ReturnType(int)> {
+public:
+  using completion_handler_type = ceph::async::detail::librados_handler;
+  explicit async_result(completion_handler_type&) {};
+  using return_type = void;
+  void get() {
+    return;
+  }
+};
+}
index 1402fa24fd3ff1034a2f3327790ff8525cc7b2b7..590f66f55c19630f30bd6d199f10aab113a5e444 100644 (file)
@@ -484,6 +484,11 @@ set_tests_properties(unittest_decode_start_v_checker_expect_failure
   PROPERTIES WILL_FAIL TRUE)
 
 add_executable(unittest_async_call test_async_call.cc)
-target_link_libraries(unittest_async_call librados ceph-common Boost::system
+target_link_libraries(unittest_async_call ceph-common Boost::system
   GTest::GTest)
 add_ceph_unittest(unittest_async_call)
+
+add_executable(unittest_librados_completion test_librados_completion.cc)
+target_link_libraries(unittest_librados_completion librados ceph-common
+  Boost::system GTest::GTest)
+add_ceph_unittest(unittest_librados_completion)
diff --git a/src/test/common/test_librados_completion.cc b/src/test/common/test_librados_completion.cc
new file mode 100644 (file)
index 0000000..d87f7ea
--- /dev/null
@@ -0,0 +1,109 @@
+// -*- 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) 2024 IBM
+ *
+ * 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.
+ *
+ */
+
+#include "common/async/librados_completion.h"
+
+#include <boost/system/detail/errc.hpp>
+#include <gtest/gtest.h>
+
+#include <boost/asio/awaitable.hpp>
+#include <boost/asio/co_spawn.hpp>
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/strand.hpp>
+#include <boost/asio/use_awaitable.hpp>
+
+#include <boost/system/error_code.hpp>
+#include <boost/system/errc.hpp>
+#include <boost/system/system_error.hpp>
+
+#include "include/rados/librados.hpp"
+
+#include "common/async/async_call.h"
+
+namespace asio = boost::asio;
+namespace sys = boost::system;
+namespace async = ceph::async;
+
+TEST(CoroSucc, AioComplete)
+{
+  auto lrc = librados::Rados::aio_create_completion();
+  asio::io_context c;
+  asio::co_spawn(c.get_executor(),
+                 []() -> asio::awaitable<void> {
+                   co_return;
+                 }(), lrc);
+  c.run();
+  lrc->wait_for_complete();
+  auto r = lrc->get_return_value();
+  ASSERT_EQ(0, r);
+}
+
+TEST(CoroExcept, AioComplete)
+{
+  auto lrc = librados::Rados::aio_create_completion();
+  asio::io_context c;
+  asio::co_spawn(c.get_executor(),
+                 []() -> asio::awaitable<void> {
+                   throw sys::system_error{ENOENT, sys::generic_category()};
+                   co_return;
+                 }(), lrc);
+  c.run();
+  lrc->wait_for_complete();
+  auto r = lrc->get_return_value();
+  ASSERT_EQ(-ENOENT, r);
+}
+
+TEST(CoroUnknownExcept, AioComplete)
+{
+  auto lrc = librados::Rados::aio_create_completion();
+  asio::io_context c;
+  asio::co_spawn(c.get_executor(),
+                 []() -> asio::awaitable<void> {
+                   throw std::exception{};
+                   co_return;
+                 }(), lrc);
+  c.run();
+  lrc->wait_for_complete();
+  auto r = lrc->get_return_value();
+  ASSERT_EQ(-EIO, r);
+}
+
+TEST(Int, AioComplete)
+{
+  auto lrc = librados::Rados::aio_create_completion();
+  asio::io_context c;
+  async::async_dispatch(c.get_executor(),
+                        []() {
+                         return -42;
+                       }, lrc);
+  c.run();
+  lrc->wait_for_complete();
+  auto r = lrc->get_return_value();
+  ASSERT_EQ(-42, r);
+}
+
+TEST(EC, AioComplete)
+{
+  auto lrc = librados::Rados::aio_create_completion();
+  asio::io_context c;
+  async::async_dispatch(c.get_executor(),
+                        []() {
+                          return sys::error_code(ENOENT,
+                                                 sys::generic_category());
+                        }, lrc);
+  c.run();
+  lrc->wait_for_complete();
+  auto r = lrc->get_return_value();
+  ASSERT_EQ(-ENOENT, r);
+}