KTL
shared.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "../utility/aligned_malloc.h"
4 #include "../utility/alignment.h"
5 #include "../utility/empty_base.h"
6 #include "../utility/meta.h"
7 #include "shared_fwd.h"
8 
9 #include <atomic>
10 #include <memory>
11 #include <type_traits>
12 
13 namespace ktl
14 {
15  template<typename T>
16  class notomic
17  {
18  public:
19  notomic() noexcept = default;
20  notomic(T value) noexcept :
21  m_Count(value) {}
22 
23  notomic(const notomic&) = delete;
24  notomic(notomic&&) = delete;
25 
26  T fetch_add(T add, std::memory_order) noexcept
27  {
28  return m_Count++;
29  }
30 
31  T fetch_sub(T add, std::memory_order) noexcept
32  {
33  return m_Count--;
34  }
35 
36  private:
37  T m_Count;
38  };
39 
40  template<typename Alloc, template<typename> typename Atomic>
41  class shared
42  {
43  private:
44  static_assert(detail::has_no_value_type_v<Alloc>, "Building on top of typed allocators is not allowed. Use allocators without a type");
45 
46  struct block
47  {
48  KTL_EMPTY_BASE Alloc Allocator;
49  Atomic<detail::get_size_type_t<Alloc>> UseCount;
50 
51  template<typename... Args,
52  typename = std::enable_if_t<
53  std::is_constructible_v<Alloc, Args...>>>
54  block(Args&&... alloc)
55  noexcept(std::is_nothrow_constructible_v<Alloc, Args...>) :
56  Allocator(std::forward<Args>(alloc)...),
57  UseCount(1) {}
58  };
59 
60  public:
62 
63  template<typename A = Alloc>
65  noexcept(std::is_nothrow_default_constructible_v<block>) :
66  m_Block(detail::aligned_new<block>(detail::ALIGNMENT)) {}
67 
71  template<typename... Args,
72  typename = std::enable_if_t<
73  std::is_constructible_v<Alloc, Args...>>>
74  explicit shared(Args&&... alloc)
75  noexcept(std::is_nothrow_constructible_v<block, Args...>) :
76  m_Block(detail::aligned_new<block>(detail::ALIGNMENT, std::forward<Args>(alloc)...)) {}
77 
78  shared(const shared& other) noexcept :
79  m_Block(other.m_Block)
80  {
81  increment();
82  }
83 
84  shared(shared&& other) noexcept :
85  m_Block(other.m_Block)
86  {
87  other.m_Block = nullptr;
88  }
89 
91  {
92  decrement();
93  }
94 
95  shared& operator=(const shared& rhs)
96  noexcept(noexcept(decrement()))
97  {
98  decrement();
99 
100  m_Block = rhs.m_Block;
101 
102  increment();
103 
104  return *this;
105  }
106 
108  noexcept(noexcept(decrement()))
109  {
110  decrement();
111 
112  m_Block = rhs.m_Block;
113 
114  rhs.m_Block = nullptr;
115 
116  return *this;
117  }
118 
119  bool operator==(const shared& rhs) const
120  noexcept(detail::has_nothrow_equal_v<Alloc>)
121  {
122  return m_Block == rhs.m_Block && m_Block->Allocator == rhs.m_Block->Allocator;
123  }
124 
125  bool operator!=(const shared& rhs) const
126  noexcept(detail::has_nothrow_not_equal_v<Alloc>)
127  {
128  return m_Block != rhs.m_Block || m_Block->Allocator != rhs.m_Block->Allocator;
129  }
130 
131 #pragma region Allocation
132  void* allocate(size_t n)
133  noexcept(detail::has_nothrow_allocate_v<Alloc>)
134  {
135  return m_Block->Allocator.allocate(n);
136  }
137 
138  void deallocate(void* p, size_t n)
139  noexcept(detail::has_nothrow_deallocate_v<Alloc>)
140  {
141  m_Block->Allocator.deallocate(p, n);
142  }
143 #pragma endregion
144 
145 #pragma region Construction
146  template<typename T, typename... Args>
147  typename std::enable_if<detail::has_construct_v<Alloc, T*, Args...>, void>::type
148  construct(T* p, Args&&... args)
149  noexcept(detail::has_nothrow_construct_v<Alloc, T*, Args...>)
150  {
151  m_Block->Allocator.construct(p, std::forward<Args>(args)...);
152  }
153 
154  template<typename T>
155  typename std::enable_if<detail::has_destroy_v<Alloc, T*>, void>::type
156  destroy(T* p)
157  noexcept(detail::has_nothrow_destroy_v<Alloc, T*>)
158  {
159  m_Block->Allocator.destroy(p);
160  }
161 #pragma endregion
162 
163 #pragma region Utility
164  template<typename A = Alloc>
165  typename std::enable_if<detail::has_max_size_v<A>, size_type>::type
166  max_size() const
167  noexcept(detail::has_nothrow_max_size_v<A>)
168  {
169  return m_Block->Allocator.max_size();
170  }
171 
172  template<typename A = Alloc>
173  typename std::enable_if<detail::has_owns_v<A>, bool>::type
174  owns(void* p) const
175  noexcept(detail::has_nothrow_owns_v<A>)
176  {
177  return m_Block->Allocator.owns(p);
178  }
179 #pragma endregion
180 
181  Alloc& get_allocator() noexcept
182  {
183  return m_Block->Allocator;
184  }
185 
186  const Alloc& get_allocator() const noexcept
187  {
188  return m_Block->Allocator;
189  }
190 
191  private:
192  void increment() noexcept
193  {
194  if (!m_Block) return;
195 
196  m_Block->UseCount.fetch_add(1, std::memory_order_acq_rel);
197  }
198 
199  void decrement()
200  noexcept(std::is_nothrow_destructible_v<Alloc>)
201  {
202  if (!m_Block) return;
203 
204  if (m_Block->UseCount.fetch_sub(1, std::memory_order_acq_rel) == 1)
205  detail::aligned_delete(m_Block);
206  }
207 
208  private:
209  block* m_Block;
210  };
211 }
Definition: shared.h:17
notomic() noexcept=default
T fetch_add(T add, std::memory_order) noexcept
Definition: shared.h:26
notomic(const notomic &)=delete
T fetch_sub(T add, std::memory_order) noexcept
Definition: shared.h:31
notomic(notomic &&)=delete
Definition: shared.h:42
shared() noexcept(std::is_nothrow_default_constructible_v< block >)
Definition: shared.h:64
bool operator!=(const shared &rhs) const noexcept(detail::has_nothrow_not_equal_v< Alloc >)
Definition: shared.h:125
std::enable_if< detail::has_owns_v< A >, bool >::type owns(void *p) const noexcept(detail::has_nothrow_owns_v< A >)
Definition: shared.h:174
std::enable_if< detail::has_destroy_v< Alloc, T * >, void >::type destroy(T *p) noexcept(detail::has_nothrow_destroy_v< Alloc, T * >)
Definition: shared.h:156
shared & operator=(shared &&rhs) noexcept(noexcept(decrement()))
Definition: shared.h:107
shared(shared &&other) noexcept
Definition: shared.h:84
Alloc & get_allocator() noexcept
Definition: shared.h:181
~shared()
Definition: shared.h:90
bool operator==(const shared &rhs) const noexcept(detail::has_nothrow_equal_v< Alloc >)
Definition: shared.h:119
std::enable_if< detail::has_construct_v< Alloc, T *, Args... >, void >::type construct(T *p, Args &&... args) noexcept(detail::has_nothrow_construct_v< Alloc, T *, Args... >)
Definition: shared.h:148
const Alloc & get_allocator() const noexcept
Definition: shared.h:186
void deallocate(void *p, size_t n) noexcept(detail::has_nothrow_deallocate_v< Alloc >)
Definition: shared.h:138
shared(const shared &other) noexcept
Definition: shared.h:78
void * allocate(size_t n) noexcept(detail::has_nothrow_allocate_v< Alloc >)
Definition: shared.h:132
detail::get_size_type_t< Alloc > size_type
Definition: shared.h:61
shared & operator=(const shared &rhs) noexcept(noexcept(decrement()))
Definition: shared.h:95
shared(Args &&... alloc) noexcept(std::is_nothrow_constructible_v< block, Args... >)
Constructor for forwarding any arguments to the underlying allocator.
Definition: shared.h:74
std::enable_if< detail::has_max_size_v< A >, size_type >::type max_size() const noexcept(detail::has_nothrow_max_size_v< A >)
Definition: shared.h:166
#define KTL_EMPTY_BASE
Definition: empty_base.h:6
constexpr bool has_construct_v
Definition: meta.h:41
void aligned_delete(T *p) noexcept(noexcept(p->~T()))
Definition: aligned_malloc.h:99
typename get_size_type< Alloc, void >::type get_size_type_t
Definition: meta.h:31
constexpr size_t ALIGNMENT
Definition: alignment.h:7
T * aligned_new(size_t alignment, Args &&... args) noexcept(noexcept(T(std::declval< Args >()...)))
Definition: aligned_malloc.h:90
constexpr bool has_nothrow_max_size_v
Definition: meta.h:122
Definition: cascading.h:15