Commit 8cde5e00 authored by Mark Santaniello's avatar Mark Santaniello Committed by Facebook GitHub Bot

Introduce FallbackSysArenaAllocator

Summary:
It's difficult to use C++ allocators if they are not default-constructible.  The non-default-constructibility "infects" the container, and any class that holds the container (transitively).

In D21967640 (https://github.com/facebook/folly/commit/f0471228b89be4a399afcf0e9692c84754d06875), I made `folly::SysArenaAllocator` default constructible, and adopted the behavior of merely throwing `std::bad_alloc` in the event that we didn't *actually* initialize it with a `folly::SysArena`.  In hindsight, it's not clear this was an improvement.  All it did was move a compile-time problem to run-time.

What I do here is a bit better.  I enhance `CxxAllocatorAdaptor` such that it has two modes:
- **Normal / classic mode:** non-default constructible.
  - Guaranteed at compile-time to have a non-null "inner allocator"
- **`FallbackToStdAlloc` mode:** default-constructible.
  - Passes allocations through to `std::allocator` if no "inner allocator" provided

Using this support, I introduce a new allocator, `folly::FallbackSysArenaAllocator`, which is similar to `SysArenaAllocator` *except* that it uses the fallback mode and is therefore default-constructible.

This is useful in a large codebase where we want to use arena-allocation on a specific *instance* of a container type -- and not every single instance.

Reviewed By: yfeldblum

Differential Revision: D22938039

fbshipit-source-id: feaba12d76564a386a03e759eedf06a77bbc8942
parent 56693404
......@@ -542,12 +542,12 @@ class AlignedSysAllocator : private Align {
*
* Note that Inner is *not* a C++ Allocator.
*/
template <typename T, class Inner>
class CxxAllocatorAdaptor {
template <typename T, class Inner, bool FallbackToStdAlloc = false>
class CxxAllocatorAdaptor : private std::allocator<T> {
private:
using Self = CxxAllocatorAdaptor<T, Inner>;
using Self = CxxAllocatorAdaptor<T, Inner, FallbackToStdAlloc>;
template <typename U, typename UAlloc>
template <typename U, typename UInner, bool UFallback>
friend class CxxAllocatorAdaptor;
Inner* inner_ = nullptr;
......@@ -559,25 +559,32 @@ class CxxAllocatorAdaptor {
using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_swap = std::true_type;
constexpr explicit CxxAllocatorAdaptor() = default;
template <bool X = FallbackToStdAlloc, std::enable_if_t<X, int> = 0>
constexpr explicit CxxAllocatorAdaptor() {}
constexpr explicit CxxAllocatorAdaptor(Inner& ref) : inner_(&ref) {}
constexpr CxxAllocatorAdaptor(CxxAllocatorAdaptor const&) = default;
template <typename U, std::enable_if_t<!std::is_same<U, T>::value, int> = 0>
constexpr CxxAllocatorAdaptor(CxxAllocatorAdaptor<U, Inner> const& other)
constexpr CxxAllocatorAdaptor(
CxxAllocatorAdaptor<U, Inner, FallbackToStdAlloc> const& other)
: inner_(other.inner_) {}
T* allocate(std::size_t n) {
if (inner_ == nullptr) {
throw_exception<std::bad_alloc>();
if (FallbackToStdAlloc && inner_ == nullptr) {
return std::allocator<T>::allocate(n);
}
return static_cast<T*>(inner_->allocate(sizeof(T) * n));
}
void deallocate(T* p, std::size_t n) {
assert(inner_);
inner_->deallocate(p, sizeof(T) * n);
if (inner_ != nullptr) {
inner_->deallocate(p, sizeof(T) * n);
} else {
assert(FallbackToStdAlloc);
std::allocator<T>::deallocate(p, n);
}
}
friend bool operator==(Self const& a, Self const& b) noexcept {
......@@ -586,6 +593,11 @@ class CxxAllocatorAdaptor {
friend bool operator!=(Self const& a, Self const& b) noexcept {
return a.inner_ != b.inner_;
}
template <typename U>
struct rebind {
using other = CxxAllocatorAdaptor<U, Inner, FallbackToStdAlloc>;
};
};
/*
......
......@@ -327,6 +327,13 @@ using ArenaAllocator = CxxAllocatorAdaptor<T, Arena<Alloc>>;
template <typename T>
using SysArenaAllocator = ArenaAllocator<T, SysAllocator<char>>;
template <typename T, typename Alloc>
using FallbackArenaAllocator =
CxxAllocatorAdaptor<T, Arena<Alloc>, /* FallbackToStdAlloc */ true>;
template <typename T>
using FallbackSysArenaAllocator = FallbackArenaAllocator<T, SysAllocator<char>>;
} // namespace folly
#include <folly/memory/Arena-inl.h>
......@@ -148,8 +148,8 @@ TEST(Arena, Vector) {
}
TEST(Arena, DefaultConstructible) {
std::vector<size_t, SysArenaAllocator<size_t>> vec;
EXPECT_THROW(vec.push_back(42), std::bad_alloc);
std::vector<size_t, FallbackSysArenaAllocator<size_t>> vec;
EXPECT_NO_THROW(vec.push_back(42));
}
TEST(Arena, Compare) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment