summaryrefslogtreecommitdiffstats
path: root/src/rgw/rgw_aio_throttle.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rgw/rgw_aio_throttle.cc')
-rw-r--r--src/rgw/rgw_aio_throttle.cc202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/rgw/rgw_aio_throttle.cc b/src/rgw/rgw_aio_throttle.cc
new file mode 100644
index 000000000..8ada6db34
--- /dev/null
+++ b/src/rgw/rgw_aio_throttle.cc
@@ -0,0 +1,202 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * 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 "include/rados/librados.hpp"
+
+#include "rgw_aio_throttle.h"
+
+namespace rgw {
+
+bool Throttle::waiter_ready() const
+{
+ switch (waiter) {
+ case Wait::Available: return is_available();
+ case Wait::Completion: return has_completion();
+ case Wait::Drained: return is_drained();
+ default: return false;
+ }
+}
+
+AioResultList BlockingAioThrottle::get(const RGWSI_RADOS::Obj& obj,
+ OpFunc&& f,
+ uint64_t cost, uint64_t id)
+{
+ auto p = std::make_unique<Pending>();
+ p->obj = obj;
+ p->id = id;
+ p->cost = cost;
+
+ std::unique_lock lock{mutex};
+ if (cost > window) {
+ p->result = -EDEADLK; // would never succeed
+ completed.push_back(*p);
+ } else {
+ // wait for the write size to become available
+ pending_size += p->cost;
+ if (!is_available()) {
+ ceph_assert(waiter == Wait::None);
+ waiter = Wait::Available;
+ cond.wait(lock, [this] { return is_available(); });
+ waiter = Wait::None;
+ }
+
+ // register the pending write and attach a completion
+ p->parent = this;
+ pending.push_back(*p);
+ lock.unlock();
+ std::move(f)(this, *static_cast<AioResult*>(p.get()));
+ lock.lock();
+ }
+ p.release();
+ return std::move(completed);
+}
+
+void BlockingAioThrottle::put(AioResult& r)
+{
+ auto& p = static_cast<Pending&>(r);
+ std::scoped_lock lock{mutex};
+
+ // move from pending to completed
+ pending.erase(pending.iterator_to(p));
+ completed.push_back(p);
+
+ pending_size -= p.cost;
+
+ if (waiter_ready()) {
+ cond.notify_one();
+ }
+}
+
+AioResultList BlockingAioThrottle::poll()
+{
+ std::unique_lock lock{mutex};
+ return std::move(completed);
+}
+
+AioResultList BlockingAioThrottle::wait()
+{
+ std::unique_lock lock{mutex};
+ if (completed.empty() && !pending.empty()) {
+ ceph_assert(waiter == Wait::None);
+ waiter = Wait::Completion;
+ cond.wait(lock, [this] { return has_completion(); });
+ waiter = Wait::None;
+ }
+ return std::move(completed);
+}
+
+AioResultList BlockingAioThrottle::drain()
+{
+ std::unique_lock lock{mutex};
+ if (!pending.empty()) {
+ ceph_assert(waiter == Wait::None);
+ waiter = Wait::Drained;
+ cond.wait(lock, [this] { return is_drained(); });
+ waiter = Wait::None;
+ }
+ return std::move(completed);
+}
+
+template <typename CompletionToken>
+auto YieldingAioThrottle::async_wait(CompletionToken&& token)
+{
+ using boost::asio::async_completion;
+ using Signature = void(boost::system::error_code);
+ async_completion<CompletionToken, Signature> init(token);
+ completion = Completion::create(context.get_executor(),
+ std::move(init.completion_handler));
+ return init.result.get();
+}
+
+AioResultList YieldingAioThrottle::get(const RGWSI_RADOS::Obj& obj,
+ OpFunc&& f,
+ uint64_t cost, uint64_t id)
+{
+ auto p = std::make_unique<Pending>();
+ p->obj = obj;
+ p->id = id;
+ p->cost = cost;
+
+ if (cost > window) {
+ p->result = -EDEADLK; // would never succeed
+ completed.push_back(*p);
+ } else {
+ // wait for the write size to become available
+ pending_size += p->cost;
+ if (!is_available()) {
+ ceph_assert(waiter == Wait::None);
+ ceph_assert(!completion);
+
+ boost::system::error_code ec;
+ waiter = Wait::Available;
+ async_wait(yield[ec]);
+ }
+
+ // register the pending write and initiate the operation
+ pending.push_back(*p);
+ std::move(f)(this, *static_cast<AioResult*>(p.get()));
+ }
+ p.release();
+ return std::move(completed);
+}
+
+void YieldingAioThrottle::put(AioResult& r)
+{
+ auto& p = static_cast<Pending&>(r);
+
+ // move from pending to completed
+ pending.erase(pending.iterator_to(p));
+ completed.push_back(p);
+
+ pending_size -= p.cost;
+
+ if (waiter_ready()) {
+ ceph_assert(completion);
+ ceph::async::post(std::move(completion), boost::system::error_code{});
+ waiter = Wait::None;
+ }
+}
+
+AioResultList YieldingAioThrottle::poll()
+{
+ return std::move(completed);
+}
+
+AioResultList YieldingAioThrottle::wait()
+{
+ if (!has_completion() && !pending.empty()) {
+ ceph_assert(waiter == Wait::None);
+ ceph_assert(!completion);
+
+ boost::system::error_code ec;
+ waiter = Wait::Completion;
+ async_wait(yield[ec]);
+ }
+ return std::move(completed);
+}
+
+AioResultList YieldingAioThrottle::drain()
+{
+ if (!is_drained()) {
+ ceph_assert(waiter == Wait::None);
+ ceph_assert(!completion);
+
+ boost::system::error_code ec;
+ waiter = Wait::Drained;
+ async_wait(yield[ec]);
+ }
+ return std::move(completed);
+}
+} // namespace rgw