diff --git a/include/ircd/allocator.h b/include/ircd/allocator.h index c796ad7a3..6aedaca43 100644 --- a/include/ircd/allocator.h +++ b/include/ircd/allocator.h @@ -24,6 +24,7 @@ namespace ircd::allocator struct profile; template struct dynamic; template struct fixed; + template struct twolevel; template struct node; profile &operator+=(profile &, const profile &); @@ -237,8 +238,9 @@ struct ircd::allocator::fixed::allocator s->state::deallocate(p - base, n); } - template - allocator(const typename fixed::allocator &) noexcept + template + allocator(const typename fixed::allocator &s) noexcept :s{reinterpret_cast *>(s.s)} { static_assert(OTHER_SIZE == SIZE); @@ -503,3 +505,119 @@ struct ircd::allocator::node::allocator return &a == &b; } }; + +/// The twolevel allocator uses both a fixed allocator (first level) and then +/// the standard allocator (second level) when the fixed allocator is exhausted. +/// This has the intent that the fixed allocator will mostly be used, but the +/// fallback to the standard allocator is seamlessly available for robustness. +template +struct ircd::allocator::twolevel +{ + struct allocator; + + fixed l0; + std::allocator l1; + + public: + allocator operator()(); + operator allocator(); + + twolevel() = default; +}; + +template +struct ircd::allocator::twolevel::allocator +{ + using value_type = T; + using pointer = T *; + using const_pointer = const T *; + using reference = T &; + using const_reference = const T &; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + twolevel *s; + + public: + template + struct rebind + { + using other = typename twolevel::allocator; + }; + + size_type max_size() const + { + return std::numeric_limits::max(); + } + + auto address(reference x) const + { + return &x; + } + + auto address(const_reference x) const + { + return &x; + } + + pointer allocate(const size_type &n, const const_pointer &hint = nullptr) + { + assert(s); + return + s->l0.allocate(std::nothrow, n, hint)?: + s->l1.allocate(n, hint); + } + + void deallocate(const pointer &p, const size_type &n) + { + assert(s); + if(likely(s->l0.in_range(p))) + s->l0.deallocate(p, n); + else + s->l1.deallocate(p, n); + } + + template + allocator(const typename twolevel::allocator &s) noexcept + :s{reinterpret_cast *>(s.s)} + { + static_assert(OTHER_L0_SIZE == L0_SIZE); + } + + allocator(twolevel &s) noexcept + :s{&s} + {} + + allocator(allocator &&) = default; + allocator(const allocator &) = default; + + friend bool operator==(const allocator &a, const allocator &b) + { + return &a == &b; + } + + friend bool operator!=(const allocator &a, const allocator &b) + { + return &a == &b; + } +}; + +template +typename ircd::allocator::twolevel::allocator +ircd::allocator::twolevel::operator()() +{ + return ircd::allocator::twolevel::allocator(*this); +} + +template +ircd::allocator::twolevel::operator +allocator() +{ + return ircd::allocator::twolevel::allocator(*this); +}