]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
common: add ceph::allocate_unique()
authorCasey Bodley <cbodley@redhat.com>
Fri, 3 Apr 2020 14:41:10 +0000 (10:41 -0400)
committerAdam C. Emerson <aemerson@redhat.com>
Wed, 9 Sep 2020 02:09:40 +0000 (22:09 -0400)
adds an allocator-aware version of std::make_unique(). this is similar to
std::allocate_shared(), though it's slightly less convenient because,
unlike std::shared_ptr<T>, the Deleter has to be specified as a template
parameter in std::unique_ptr<T, Deleter>

Signed-off-by: Casey Bodley <cbodley@redhat.com>
src/common/allocate_unique.h [new file with mode: 0644]
src/test/common/CMakeLists.txt
src/test/common/test_allocate_unique.cc [new file with mode: 0644]

diff --git a/src/common/allocate_unique.h b/src/common/allocate_unique.h
new file mode 100644 (file)
index 0000000..4c5cc1c
--- /dev/null
@@ -0,0 +1,69 @@
+// -*- 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) 2020 Red Hat
+ *
+ * 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.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+
+namespace ceph {
+
+/// An allocator-aware 'Deleter' for std::unique_ptr<T, Deleter>. The
+/// allocator's traits must have a value_type of T.
+template <typename Alloc>
+class deallocator {
+  using allocator_type = Alloc;
+  using allocator_traits = std::allocator_traits<allocator_type>;
+  using pointer = typename allocator_traits::pointer;
+  allocator_type alloc;
+ public:
+  explicit deallocator(const allocator_type& alloc) noexcept : alloc(alloc) {}
+  void operator()(pointer p) {
+    allocator_traits::destroy(alloc, p);
+    allocator_traits::deallocate(alloc, p, 1);
+  }
+};
+
+/// deallocator alias that rebinds Alloc's value_type to T
+template <typename T, typename Alloc>
+using deallocator_t = deallocator<typename std::allocator_traits<Alloc>
+      ::template rebind_alloc<T>>;
+
+/// std::unique_ptr alias that rebinds Alloc if necessary, and avoids repetition
+/// of the template parameter T.
+template <typename T, typename Alloc>
+using allocated_unique_ptr = std::unique_ptr<T, deallocator_t<T, Alloc>>;
+
+
+/// Returns a std::unique_ptr whose memory is managed by the given allocator.
+template <typename T, typename Alloc, typename... Args>
+static auto allocate_unique(Alloc& alloc, Args&&... args)
+  -> allocated_unique_ptr<T, Alloc>
+{
+  static_assert(!std::is_array_v<T>, "allocate_unique() does not support T[]");
+
+  using allocator_type = typename std::allocator_traits<Alloc>
+      ::template rebind_alloc<T>;
+  using allocator_traits = std::allocator_traits<allocator_type>;
+  auto a = allocator_type{alloc};
+  auto p = allocator_traits::allocate(a, 1);
+  try {
+    allocator_traits::construct(a, p, std::forward<Args>(args)...);
+    return {p, deallocator<allocator_type>{a}};
+  } catch (...) {
+    allocator_traits::deallocate(a, p, 1);
+    throw;
+  }
+}
+
+} // namespace ceph
index 3bb6d3f8644a5403947c06e54bb0bfb4ed648e98..4bf46a00873d09cf67171cd9bd5e53871ce6eeaf 100644 (file)
@@ -337,6 +337,10 @@ add_ceph_unittest(unittest_cdc)
 add_executable(unittest_ceph_timer test_ceph_timer.cc)
 add_ceph_unittest(unittest_ceph_timer)
 
+
 add_executable(unittest_blocked_completion test_blocked_completion.cc)
 add_ceph_unittest(unittest_blocked_completion)
 target_link_libraries(unittest_blocked_completion Boost::system GTest::GTest)
+
+add_executable(unittest_allocate_unique test_allocate_unique.cc)
+add_ceph_unittest(unittest_allocate_unique)
diff --git a/src/test/common/test_allocate_unique.cc b/src/test/common/test_allocate_unique.cc
new file mode 100644 (file)
index 0000000..0994495
--- /dev/null
@@ -0,0 +1,97 @@
+// -*- 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) 2020 Red Hat
+ *
+ * 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/allocate_unique.h"
+#include <string>
+#include <vector>
+#include <gtest/gtest.h>
+
+namespace {
+
+// allocation events recorded by logging_allocator
+struct event {
+  size_t size;
+  bool allocated; // true for allocate(), false for deallocate()
+};
+using event_log = std::vector<event>;
+
+template <typename T>
+struct logging_allocator {
+  event_log *const log;
+
+  using value_type = T;
+
+  explicit logging_allocator(event_log *log) : log(log) {}
+  logging_allocator(const logging_allocator& other) : log(other.log) {}
+
+  template <typename U>
+  logging_allocator(const logging_allocator<U>& other) : log(other.log) {}
+
+  T* allocate(size_t n, const void* hint=0)
+  {
+    auto p = std::allocator<T>{}.allocate(n, hint);
+    log->emplace_back(event{n * sizeof(T), true});
+    return p;
+  }
+  void deallocate(T* p, size_t n)
+  {
+    std::allocator<T>{}.deallocate(p, n);
+    if (p) {
+      log->emplace_back(event{n * sizeof(T), false});
+    }
+  }
+};
+
+} // anonymous namespace
+
+namespace ceph {
+
+TEST(AllocateUnique, Allocate)
+{
+  event_log log;
+  auto alloc = logging_allocator<char>{&log};
+  {
+    auto p = allocate_unique<char>(alloc, 'a');
+    static_assert(std::is_same_v<decltype(p),
+                  std::unique_ptr<char, deallocator<logging_allocator<char>>>>);
+    ASSERT_TRUE(p);
+    ASSERT_EQ(1, log.size());
+    EXPECT_EQ(1, log.front().size);
+    EXPECT_EQ(true, log.front().allocated);
+  }
+  ASSERT_EQ(2, log.size());
+  EXPECT_EQ(1, log.back().size);
+  EXPECT_EQ(false, log.back().allocated);
+}
+
+TEST(AllocateUnique, RebindAllocate)
+{
+  event_log log;
+  auto alloc = logging_allocator<char>{&log};
+  {
+    auto p = allocate_unique<std::string>(alloc, "a");
+    static_assert(std::is_same_v<decltype(p),
+                  std::unique_ptr<std::string,
+                                  deallocator<logging_allocator<std::string>>>>);
+    ASSERT_TRUE(p);
+    ASSERT_EQ(1, log.size());
+    EXPECT_EQ(sizeof(std::string), log.front().size);
+    EXPECT_EQ(true, log.front().allocated);
+  }
+  ASSERT_EQ(2, log.size());
+  EXPECT_EQ(sizeof(std::string), log.back().size);
+  EXPECT_EQ(false, log.back().allocated);
+}
+
+} // namespace ceph