mutable
A Database System for Research and Fast Prototyping
Loading...
Searching...
No Matches
Pool.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <functional>
4#include <mutable/util/fn.hpp>
8#include <type_traits>
9#include <unordered_map>
10#include <utility>
11
12
13namespace m {
14
15// forward declaration
16template<typename T, typename Pool, bool CanBeNone = false>
17struct Pooled;
18
20template<typename T, typename Hash = std::hash<T>, typename KeyEqual = std::equal_to<T>, typename Copy = std::identity,
21 bool ThreadSafe = false>
22struct PODPool
23{
24 using counter_type = std::conditional_t<ThreadSafe, std::atomic<uint32_t>, uint32_t>;
25 using table_type = std::unordered_map<T, counter_type, Hash, KeyEqual>;
26 using pooled_type = T;
29 using hasher = Hash;
30 using key_equal = KeyEqual;
31 using copy = Copy;
32
33 template<typename, typename, bool>
34 friend struct Pooled;
35
36 static constexpr bool is_thread_safe = ThreadSafe;
37
38 private:
40
42
43 public:
44 using const_iterator = table_type::const_iterator;
45
46 public:
47 PODPool() = default;
48 PODPool(std::size_t initial_capacity) : table_(initial_capacity) { }
49 virtual ~PODPool() {
50#ifndef NDEBUG
51 for (auto& [_, count] : table_)
52 M_insist(count == 0, "deleting would create a dangling reference to pooled object");
53#endif
54 }
55
57 std::size_t size() const { return table_.size(); }
58
59 const_iterator begin() { return table_.cbegin(); }
60 const_iterator end() { return table_.cend(); }
61 const_iterator cbegin() const { return table_.begin(); }
62 const_iterator cend() const { return table_.end(); }
63
65 template<typename U>
67
68 private:
70 template<bool CanBeNone>
71 static const T & Get(const Pooled<T, PODPool, CanBeNone> &pooled);
72
74 template<bool CanBeNone>
76};
77
79template<typename T, typename Hash = std::hash<T>, typename KeyEqual = std::equal_to<T>, bool ThreadSafe = false>
80struct Pool
81{
83 {
86 using is_transparent = void;
87
88 template<typename U>
89 auto operator()(U &&u) const { return Hash{}(*u); }
90 };
91
93 {
96 using is_transparent = void;
97
98 template<typename U, typename V>
99 auto operator()(U &&first, V &&second) const { return KeyEqual{}(*first, *second); }
100 };
101
102 using counter_type = std::conditional_t<ThreadSafe, std::atomic<uint32_t>, uint32_t>;
103 using table_type = std::unordered_map<std::unique_ptr<T>, counter_type, dereference_hash, dereference_equal_to>;
104 using pooled_type = T;
105 template<typename U> using proxy_type = Pooled<U, Pool, false>;
106 template<typename U> using proxy_optional_type = Pooled<U, Pool, true>;
107 using hasher = Hash;
108 using key_equal = KeyEqual;
109
110 template<typename, typename, bool>
111 friend struct Pooled;
112
113 static constexpr bool is_thread_safe = ThreadSafe;
114
115 private:
117
119
120 public:
121 using const_iterator = table_type::const_iterator;
122
123 public:
124 Pool() = default;
125 Pool(std::size_t initial_capacity) : table_(initial_capacity) { }
127#ifndef NDEBUG
128 /* Manually delete all entries and make sure we don't have any dangling references. */
129 while (not table_.empty()) {
130 typename table_type::node_type nh = table_.extract(table_.begin());
131 M_insist(nh.mapped() == 0, "deleting would create a dangling reference to pooled object");
132 }
133#endif
134 }
135
137 std::size_t size() const { return table_.size(); }
138
139 const_iterator begin() { return table_.cbegin(); }
140 const_iterator end() { return table_.cend(); }
141 const_iterator cbegin() const { return table_.begin(); }
142 const_iterator cend() const { return table_.end(); }
143
145 template<typename U>
146 requires std::derived_from<U, T>
148
149 private:
151 template<typename U, bool CanBeNone>
152 requires std::derived_from<U, T>
153 static const U & Get(const Pooled<U, Pool, CanBeNone> &pooled);
154
156 template<typename U, bool CanBeNone>
158};
159
166template<typename T, typename Pool, bool CanBeNone>
167struct Pooled
168{
170 static constexpr bool can_be_none = CanBeNone;
171
172 using value_type = typename Pool::table_type::value_type;
173
175 friend Pool;
176
178 template<typename, typename, bool>
179 friend struct Pooled;
180
181 friend void swap(Pooled &first, Pooled &second) {
182 using std::swap;
183 swap(first.pool_, second.pool_);
184 swap(first.ref_, second.ref_);
185 }
186
187 private:
189 Pool *pool_ = nullptr;
191 value_type *ref_ = nullptr;
192
194 Pooled(Pool *pool, value_type *value) : pool_(pool), ref_(value) {
195 M_insist(bool(pool_) == bool(ref_), "inconsistent pooled state");
196 if constexpr (CanBeNone) {
197 if (ref_) ++ref_->second; // increase reference count
198 } else {
199 ++M_notnull(ref_)->second; // increase reference count
200 }
201 }
202
203 public:
204 Pooled() requires can_be_none = default;
205 Pooled(const Pooled &other) : Pooled(other.pool_, other.ref_) { }
206 Pooled(Pooled &&other) { swap(*this, other); }
207
210 : Pooled(M_notnull(other.pool_), M_notnull(other.ref_))
211 { }
212
215 : pool_(M_notnull(std::exchange(other.pool_, nullptr)))
216 , ref_(M_notnull(std::exchange(other.ref_, nullptr)))
217 { }
218
220 explicit Pooled(const Pooled<T, Pool, true> &other) requires (not can_be_none)
221 : Pooled(M_notnull(other.pool_), other.ref_)
222 {
223 if (not other.has_value())
224 throw invalid_argument("value must be present");
225 }
226
228 explicit Pooled(Pooled<T, Pool, true> &&other) requires (not can_be_none)
229 : pool_(M_notnull(std::exchange(other.pool_, nullptr)))
230 , ref_([&other](){
231 if (not other.has_value())
232 throw invalid_argument("value must be present");
233 return std::exchange(other.ref_, nullptr);
234 }())
235 { }
236
237 bool has_value() const requires can_be_none { return ref_ != nullptr; }
238
240 if (not has_value())
241 throw invalid_argument("value must be present");
242 return { pool_, ref_ };
243 }
244
249 uint32_t count() const { return ref_ ? uint32_t(ref_->second) : 0; }
250
252 M_insist(bool(pool_) == bool(ref_), "inconsistent pooled state");
253 if (ref_) {
254 M_insist(ref_->second > 0, "underflow reference count");
255 if (--ref_->second == 0)
256 /* TODO: free object in pool, as it is not referenced anymore */
257 // pool_->erase(*this);
258 ;
259 }
260 }
261
262 Pooled & operator=(Pooled other) { swap(*this, other); return *this; }
263
264 explicit operator const T & () const { return Pool::Get(*this); }
265 operator const T * () const { return &Pool::Get(*this); }
266
267 const T & operator*() const { return Pool::Get(*this); }
268 const T * operator->() const { return &Pool::Get(*this); }
269
274 template<typename U>
275 requires std::is_polymorphic_v<typename Pool::pooled_type> and (std::derived_from<U, T> or std::derived_from<T, U>)
276 Pooled<U, Pool, false> as() const {
277 M_insist(ref_, "cannot cast empty pooled object");
278 M_insist(m::is<U>(ref_->first)); // check if the cast is valid
279 return { pool_, ref_ };
280 }
281
286 template<typename U>
287 requires std::is_polymorphic_v<typename Pool::pooled_type> and std::derived_from<T, U>
289 return as<U>();
290 }
291
297 template<typename U, bool _CanBeNone>
298 requires std::is_polymorphic_v<typename Pool::pooled_type> and std::derived_from<U, T>
300 M_insist(other.ref_, "rhs can not be empty");
301 using std::swap;
302 swap(this->pool_, other.pool_);
303 swap(this->ref_ , other.ref_);
304 return *this;
305 }
306
307 template<typename U, bool _CanBeNone>
308 bool operator==(Pooled<U, Pool, _CanBeNone> other) const { return this->ref_ == other.ref_; } // check referential equality
309 template<typename U, bool _CanBeNone>
310 bool operator!=(Pooled<U, Pool, _CanBeNone> other) const { return not operator==(other); }
311 template<typename U>
312 bool operator==(const U *other) const { return &Pool::Get(*this) == other; } // check referential equality
313 template<typename U>
314 bool operator!=(const U *other) const { return not operator==(other); }
315
316 friend std::ostream & operator<<(std::ostream &out, const Pooled &pooled) {
317 if constexpr (streamable<std::ostream, T>)
318 return out << *pooled;
319 else
320 return out << "Pooled<" << typeid(T).name() << ">";
321 }
322
323 void dump(std::ostream &out) const {
324 out << *this
325 << " (" << &Pool::Get(*this) << ")"
326 << " count: " << this->count() << std::endl;
327 }
328 void dump() const { dump(std::cerr); }
329};
330
331template<typename T, typename Hash, typename KeyEqual, typename Copy, bool ThreadSafe>
332template<typename U>
334{
335 if constexpr (ThreadSafe) {
336 reader_writer_lock lock{table_mutex_};
337 typename table_type::iterator it;
338 do {
339 lock.lock_read();
340 it = table_.find(u);
341 if (it != table_.end())
342 return proxy_type{this, &*it};
343 } while (not lock.upgrade());
344 M_insist(lock.owns_write_lock());
345 it = table_.emplace_hint(it, Copy{}(std::forward<U>(u)), 0); // perfect forwarding
346 return proxy_type{this, &*it};
347 } else {
348 auto it = table_.find(u);
349 if (it == table_.end())
350 it = table_.emplace_hint(it, Copy{}(std::forward<U>(u)), 0); // perfect forwarding
351 return proxy_type{this, &*it};
352 }
353}
354
355template<typename T, typename Hash, typename KeyEqual, typename Copy, bool ThreadSafe>
356template<bool CanBeNone>
358{
359 M_insist(pooled.ref_, "cannot erase w/o valid reference");
360 if constexpr (ThreadSafe) {
361 write_lock lock{table_mutex_};
362 if (pooled.ref_->second != 0) return false; // entity was concurrently pooled
363 table_.erase(pooled.ref_->first);
364 return true;
365 } else {
366 M_insist(pooled.ref_->second == 0, "reference count must be 0 to erase");
367 table_.erase(pooled.ref_->first);
368 return true;
369 }
370}
371
372template<typename T, typename Hash, typename KeyEqual, typename Copy, bool ThreadSafe>
373template<bool CanBeNone>
375{
376 M_insist(pooled.ref_);
377 return pooled.ref_->first;
378}
379
380template<typename T, typename Hash, typename KeyEqual, bool ThreadSafe>
381template<typename U>
382requires std::derived_from<U, T>
384{
385 if constexpr (ThreadSafe) {
386 reader_writer_lock lock{table_mutex_};
387 typename table_type::iterator it;
388 do {
389 lock.lock_read();
390 it = table_.find(&u);
391 if (it != table_.end())
392 return proxy_type<U>{this, &*it};
393 } while (not lock.upgrade());
394 M_insist(lock.owns_write_lock());
395 it = table_.emplace_hint(it, as<T>(std::make_unique<U>(std::forward<U>(u))), 0); // perfect forwarding
396 return proxy_type<U>{this, &*it};
397 } else {
398 auto it = table_.find(&u);
399 if (it == table_.end())
400 it = table_.emplace_hint(it, as<T>(std::make_unique<U>(std::forward<U>(u))), 0); // perfect forwarding
401 return proxy_type<U>{this, &*it};
402 }
403}
404
405template<typename T, typename Hash, typename KeyEqual, bool ThreadSafe>
406template<typename U, bool CanBeNone>
408{
409 M_insist(pooled.ref_, "cannot erase w/o valid reference");
410 if constexpr (ThreadSafe) {
411 write_lock lock{table_mutex_}; // acquire write lock
412 if (pooled.ref_->second != 0) return false; // entity was concurrently pooled
413 table_.erase(pooled.ref_->first);
414 return true;
415 } else {
416 M_insist(pooled.ref_->second == 0, "reference count must be 0 to erase");
417 table_.erase(pooled.ref_->first);
418 return true;
419 }
420}
421
422template<typename T, typename Hash, typename KeyEqual, bool ThreadSafe>
423template<typename U, bool CanBeNone>
424requires std::derived_from<U, T>
426{
427 M_insist(pooled.ref_);
428 return as<U>(*pooled.ref_->first); // additional dereference because of `std::unique_ptr` indirection
429}
430
431template<typename T, typename Hash = std::hash<T>, typename KeyEqual = std::equal_to<T>, typename Copy = std::identity>
433template<typename T, typename Hash = std::hash<T>, typename KeyEqual = std::equal_to<T>>
435
437{
438 const char * operator()(const char *str) const { return M_notnull(strdup(str)); }
439 const char * operator()(std::string_view sv) const { return M_notnull(strndup(sv.data(), sv.size())); }
440};
441
442namespace detail {
443
445template<bool ThreadSafe = false>
446struct _StringPool : PODPool<const char*, StrHash, StrEqual, StrClone, ThreadSafe>
447{
448 private:
450
451 public:
452 _StringPool() = default;
453 _StringPool(std::size_t initial_capacity) : super(initial_capacity) { }
454
456 for (const auto& [str, _] : *this)
457 free((void*) str);
458 }
459};
460
461}
462
464using ThreadSafePooledString = ThreadSafeStringPool::proxy_type;
465using ThreadSafePooledOptionalString = ThreadSafeStringPool::proxy_optional_type;
466
470
471}
472
473namespace std {
474
475template<typename T, typename Pool, bool CanBeNone>
476struct std::hash<m::Pooled<T, Pool, CanBeNone>>
477{
478 uint64_t operator()(const m::Pooled<T, Pool, CanBeNone> &pooled) const {
479 return std::hash<const T*>{}(&*pooled); // hash of the address where the object is stored in the pool
480 }
481};
482
483}
Check whether.
Definition: concepts.hpp:208
#define M_notnull(ARG)
Definition: macro.hpp:182
#define M_insist(...)
Definition: macro.hpp:129
‍mutable namespace
Definition: Backend.hpp:10
T(x)
ThreadSafeStringPool::proxy_type ThreadSafePooledString
Definition: Pool.hpp:464
and
Definition: enum_ops.hpp:12
and arithmetic< U > and same_signedness< T, U > U
Definition: concepts.hpp:90
ThreadSafeStringPool::proxy_optional_type ThreadSafePooledOptionalString
Definition: Pool.hpp:465
STL namespace.
The PODPool implements an implicitly garbage-collected set of pooled (or internalized) POD struct ent...
Definition: Pool.hpp:23
PODPool(std::size_t initial_capacity)
Definition: Pool.hpp:48
static constexpr bool is_thread_safe
Definition: Pool.hpp:36
const_iterator end()
Definition: Pool.hpp:60
Copy copy
Definition: Pool.hpp:31
T pooled_type
Definition: Pool.hpp:26
const_iterator cbegin() const
Definition: Pool.hpp:61
OptField< ThreadSafe, reader_writer_mutex > table_mutex_
Definition: Pool.hpp:41
std::unordered_map< T, counter_type, Hash, KeyEqual > table_type
Definition: Pool.hpp:25
Hash hasher
Definition: Pool.hpp:29
bool erase(const Pooled< T, PODPool, CanBeNone > &pooled)
Erases the pooled entity from the pool.
Definition: Pool.hpp:357
table_type table_
Definition: Pool.hpp:39
const_iterator begin()
Definition: Pool.hpp:59
PODPool()=default
virtual ~PODPool()
Definition: Pool.hpp:49
table_type::const_iterator const_iterator
Definition: Pool.hpp:44
proxy_type operator()(U &&u)
Returns the pooled.
Definition: Pool.hpp:333
static const T & Get(const Pooled< T, PODPool, CanBeNone > &pooled)
Returns a reference to the value referenced by.
Definition: Pool.hpp:374
const_iterator cend() const
Definition: Pool.hpp:62
std::conditional_t< ThreadSafe, std::atomic< uint32_t >, uint32_t > counter_type
Definition: Pool.hpp:24
KeyEqual key_equal
Definition: Pool.hpp:30
std::size_t size() const
Returns the number of elements in the pool.
Definition: Pool.hpp:57
auto operator()(U &&first, V &&second) const
Definition: Pool.hpp:99
void is_transparent
‍Mark this callable as transparent, allowing for comparing various types that are interoperable.
Definition: Pool.hpp:96
void is_transparent
‍Mark this callable as transparent, allowing for computing the hash of various types that are interop...
Definition: Pool.hpp:86
auto operator()(U &&u) const
Definition: Pool.hpp:89
A pool implements an implicitly garbage-collected set of instances of a class hierarchy.
Definition: Pool.hpp:81
std::size_t size() const
Returns the number of elements in the pool.
Definition: Pool.hpp:137
Hash hasher
Definition: Pool.hpp:107
const_iterator begin()
Definition: Pool.hpp:139
T pooled_type
Definition: Pool.hpp:104
OptField< ThreadSafe, reader_writer_mutex > table_mutex_
Definition: Pool.hpp:118
~Pool()
Definition: Pool.hpp:126
const_iterator cend() const
Definition: Pool.hpp:142
std::conditional_t< ThreadSafe, std::atomic< uint32_t >, uint32_t > counter_type
Definition: Pool.hpp:102
Pooled< U, Pool, false > operator()(U &&u)
Returns the pooled.
table_type::const_iterator const_iterator
Definition: Pool.hpp:121
static constexpr bool is_thread_safe
Definition: Pool.hpp:113
Pool(std::size_t initial_capacity)
Definition: Pool.hpp:125
table_type table_
Definition: Pool.hpp:116
static const U & Get(const Pooled< U, Pool, CanBeNone > &pooled)
Returns a reference to the value referenced by.
Definition: Pool.hpp:425
std::unordered_map< std::unique_ptr< T >, counter_type, dereference_hash, dereference_equal_to > table_type
Definition: Pool.hpp:103
KeyEqual key_equal
Definition: Pool.hpp:108
const_iterator cbegin() const
Definition: Pool.hpp:141
bool erase(const Pooled< U, Pool, CanBeNone > &pooled)
Erases the pooled entity from the pool.
Definition: Pool.hpp:407
const_iterator end()
Definition: Pool.hpp:140
Pool()=default
A data type representing a pooled (or internalized) object.
Definition: Pool.hpp:168
Pooled(Pooled< T, Pool, true > &&other)
‍Move-constructs a non-optional Pooled from an optional one. Can only be used if value is present.
Definition: Pool.hpp:228
Pooled< T, Pool, false > assert_not_none() const
Definition: Pool.hpp:239
bool operator!=(Pooled< U, Pool, _CanBeNone > other) const
Definition: Pool.hpp:310
friend std::ostream & operator<<(std::ostream &out, const Pooled &pooled)
Definition: Pool.hpp:316
Pooled & operator=(Pooled other)
Definition: Pool.hpp:262
friend struct Pooled
‍Access privilege for conversion between optional and non-optional Pooled instances.
Definition: Pool.hpp:179
Pooled(Pooled &&other)
Definition: Pool.hpp:206
void dump() const
Definition: Pool.hpp:328
bool operator==(const U *other) const
Definition: Pool.hpp:312
static constexpr bool can_be_none
‍Can this Pooled not reference an object?
Definition: Pool.hpp:170
and(std::derived_from< U, T > or std::derived_from< T, U >) Pooled< U
Explicitly casts this Pooled<T> to Pooled<U>.
Pooled(Pool *pool, value_type *value)
Constucts a fresh Pooled from a pooled.
Definition: Pool.hpp:194
const T * operator->() const
Definition: Pool.hpp:268
false as() const
Definition: Pool.hpp:276
void dump(std::ostream &out) const
Definition: Pool.hpp:323
Pooled(const Pooled< T, Pool, false > &other)
‍Constructs an optional Pooled with a present value from a non-optional one.
Definition: Pool.hpp:209
bool operator!=(const U *other) const
Definition: Pool.hpp:314
friend void swap(Pooled &first, Pooled &second)
Definition: Pool.hpp:181
friend Pool
Definition: Pool.hpp:175
bool operator==(Pooled< U, Pool, _CanBeNone > other) const
Definition: Pool.hpp:308
bool has_value() const
Definition: Pool.hpp:237
Pooled(Pooled< T, Pool, false > &&other)
‍Move-constructs an optional Pooled with a present value from a non-optional one.
Definition: Pool.hpp:214
Pooled()=default
and std::derived_from< U, T > Pooled & operator=(Pooled< U, Pool, _CanBeNone > other)
Assigns a Pooled<U> to this Pooled<T>.
Definition: Pool.hpp:299
~Pooled()
Definition: Pool.hpp:251
typename Pool::table_type::value_type value_type
Definition: Pool.hpp:172
const T & operator*() const
Definition: Pool.hpp:267
Pool * pool_
‍The pool holding this value. Can only be nullptr if CanBeNone or if the Pooled instance is moved.
Definition: Pool.hpp:189
uint32_t count() const
Returns the number of references to the pooled object or 0 if this Pooled CanBeNone and does not hold...
Definition: Pool.hpp:249
Pooled(const Pooled< T, Pool, true > &other)
‍Constructs a non-optional Pooled from an optional one. Can only be used if value is present.
Definition: Pool.hpp:220
value_type * ref_
‍The pointer to the pooled object. Can only be nullptr if CanBeNone or if the Pooled instance is move...
Definition: Pool.hpp:191
const char * operator()(const char *str) const
Definition: Pool.hpp:438
const char * operator()(std::string_view sv) const
Definition: Pool.hpp:439
Explicit specialization of PODPool for strings (const char *).
Definition: Pool.hpp:447
_StringPool(std::size_t initial_capacity)
Definition: Pool.hpp:453
Signals that an argument to a function of method was invalid.
Definition: exception.hpp:37
This is a helper class that helps managing reader and writer locks claimed from reader_writer_mutex.
void lock_read()
Acquire a read lock.
uint64_t operator()(const m::Pooled< T, Pool, CanBeNone > &pooled) const
Definition: Pool.hpp:478