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