Luanti 5.16.0-dev
Loading...
Searching...
No Matches
container.h
Go to the documentation of this file.
1// Luanti
2// SPDX-License-Identifier: LGPL-2.1-or-later
3// Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5#pragma once
6
7#include "irrlichttypes.h"
8#include "debug.h" // sanity_check
9#include "exceptions.h"
11#include "threading/semaphore.h"
12#include <list>
13#include <vector>
14#include <map>
15#include <unordered_set>
16#include <queue>
17#include <cassert>
18#include <limits>
19
20/*
21 Queue with unique values with fast checking of value existence
22*/
23
24template<typename Value>
26{
27public:
28
29 /*
30 Does nothing if value is already queued.
31 Return value:
32 true: value added
33 false: value already exists
34 */
35 bool push_back(const Value &value)
36 {
37 if (m_set.insert(value).second)
38 {
39 m_queue.push(value);
40 return true;
41 }
42 return false;
43 }
44
45 void pop_front()
46 {
47 m_set.erase(m_queue.front());
48 m_queue.pop();
49 }
50
51 const Value &front() const
52 {
53 return m_queue.front();
54 }
55
56 size_t size() const
57 {
58 return m_queue.size();
59 }
60
61 bool empty() const
62 {
63 return m_queue.empty();
64 }
65
66private:
67 std::unordered_set<Value> m_set;
68 std::queue<Value> m_queue;
69};
70
71/*
72 Thread-safe map
73*/
74
75template<typename Key, typename Value>
77{
78public:
79 MutexedMap() = default;
80
81 void set(const Key &name, const Value &value)
82 {
84 m_values[name] = value;
85 }
86
87 bool get(const Key &name, Value *result) const
88 {
90 auto n = m_values.find(name);
91 if (n == m_values.end())
92 return false;
93 if (result)
94 *result = n->second;
95 return true;
96 }
97
98 std::vector<Value> getValues() const
99 {
101 std::vector<Value> result;
102 result.reserve(m_values.size());
103 for (auto it = m_values.begin(); it != m_values.end(); ++it)
104 result.push_back(it->second);
105 return result;
106 }
107
108 void clear()
109 {
111 m_values.clear();
112 }
113
114private:
115 std::map<Key, Value> m_values;
116 mutable std::mutex m_mutex;
117};
118
119
120/*
121 Thread-safe double-ended queue
122*/
123
124template<typename T>
126{
127public:
128 template<typename Key, typename U, typename Caller, typename CallerData>
129 friend class RequestQueue;
130
131 MutexedQueue() = default;
132
133 bool empty() const
134 {
136 return m_queue.empty();
137 }
138
139 void push_back(const T &t)
140 {
142 m_queue.push_back(t);
143 m_signal.post();
144 }
145
146 void push_back(T &&t)
147 {
149 m_queue.push_back(std::move(t));
150 m_signal.post();
151 }
152
153 /* this version of pop_front returns an empty element of T on timeout.
154 * Make sure default constructor of T creates a recognizable "empty" element
155 */
156 T pop_frontNoEx(u32 wait_time_max_ms)
157 {
158 if (m_signal.wait(wait_time_max_ms)) {
160
161 T t = std::move(m_queue.front());
162 m_queue.pop_front();
163 return t;
164 }
165
166 return T();
167 }
168
169 T pop_front(u32 wait_time_max_ms)
170 {
171 if (m_signal.wait(wait_time_max_ms)) {
173
174 T t = std::move(m_queue.front());
175 m_queue.pop_front();
176 return t;
177 }
178
179 throw ItemNotFoundException("MutexedQueue: queue is empty");
180 }
181
183 {
184 m_signal.wait();
185
187
188 T t = std::move(m_queue.front());
189 m_queue.pop_front();
190 return t;
191 }
192
193 T pop_back(u32 wait_time_max_ms=0)
194 {
195 if (m_signal.wait(wait_time_max_ms)) {
197
198 T t = std::move(m_queue.back());
199 m_queue.pop_back();
200 return t;
201 }
202
203 throw ItemNotFoundException("MutexedQueue: queue is empty");
204 }
205
206 /* this version of pop_back returns an empty element of T on timeout.
207 * Make sure default constructor of T creates a recognizable "empty" element
208 */
209 T pop_backNoEx(u32 wait_time_max_ms)
210 {
211 if (m_signal.wait(wait_time_max_ms)) {
213
214 T t = std::move(m_queue.back());
215 m_queue.pop_back();
216 return t;
217 }
218
219 return T();
220 }
221
223 {
224 m_signal.wait();
225
227
228 T t = std::move(m_queue.back());
229 m_queue.pop_back();
230 return t;
231 }
232
234 {
235 return IterationHelper(this);
236 }
237
238protected:
239 // Helper class that allows direct access to the queue with locking
241 friend class MutexedQueue<T>;
243 q->getMutex().unlock();
244 q->getSignal().post(); // assume modified
245 }
246
247 auto begin() { return q->getQueue().begin(); }
248 auto end() { return q->getQueue().end(); }
249
250 auto erase(typename std::deque<T>::iterator it) {
251 return q->getQueue().erase(it);
252 }
253
254 private:
255 IterationHelper(MutexedQueue<T> *parent) : q(parent) {
256 q->getMutex().lock();
257 }
258
260 };
261
262 std::mutex &getMutex() { return m_mutex; }
264
265 std::deque<T> &getQueue() { return m_queue; }
266
267 std::deque<T> m_queue;
268 mutable std::mutex m_mutex;
270};
271
272/*
273 LRU cache
274*/
275
276template<typename K, typename V>
278{
279public:
280 LRUCache(size_t limit, void (*cache_miss)(void *data, const K &key, V *dest),
281 void *data)
282 {
283 m_limit = limit;
284 m_cache_miss = cache_miss;
285 m_cache_miss_data = data;
286 }
287
288 void setLimit(size_t limit)
289 {
290 m_limit = limit;
291 invalidate();
292 }
293
295 {
296 m_map.clear();
297 m_queue.clear();
298 }
299
300 const V *lookupCache(K key)
301 {
302 typename cache_type::iterator it = m_map.find(key);
303 V *ret;
304 if (it != m_map.end()) {
305 // found!
306
307 cache_entry_t &entry = it->second;
308
309 ret = &entry.second;
310
311 // update the usage information
312 m_queue.erase(entry.first);
313 m_queue.push_front(key);
314 entry.first = m_queue.begin();
315 } else {
316 // cache miss -- enter into cache
317 cache_entry_t &entry =
318 m_map[key];
319 ret = &entry.second;
320 m_cache_miss(m_cache_miss_data, key, &entry.second);
321
322 // delete old entries
323 if (m_queue.size() == m_limit) {
324 const K &id = m_queue.back();
325 m_map.erase(id);
326 m_queue.pop_back();
327 }
328
329 m_queue.push_front(key);
330 entry.first = m_queue.begin();
331 }
332 return ret;
333 }
334private:
335 void (*m_cache_miss)(void *data, const K &key, V *dest);
337 size_t m_limit;
338 typedef typename std::template pair<typename std::template list<K>::iterator, V> cache_entry_t;
339 typedef std::template map<K, cache_entry_t> cache_type;
341 // we can't use std::deque here, because its iterators get invalidated
342 std::list<K> m_queue;
343};
344
345/*
346 Map that can be safely modified (insertion, deletion) during iteration
347 Caveats:
348 - you cannot insert null elements
349 - you have to check for null elements during iteration, those are ones already deleted
350 - size() and empty() don't work during iteration
351 - not thread-safe in any way
352
353 How this is implemented:
354 - there are two maps: new and real
355 - if inserting duration iteration, the value is inserted into the "new" map
356 - if deleting during iteration, the value is set to null (to be GC'd later)
357 - when iteration finishes the "new" map is merged into the "real" map
358*/
359
360template<typename K, typename V>
362{
363public:
364 // this allows bare pointers but also e.g. std::unique_ptr
365 static_assert(std::is_default_constructible<V>::value,
366 "Value type must be default constructible");
367 static_assert(std::is_constructible<bool, V>::value,
368 "Value type must be convertible to bool");
369 static_assert(std::is_move_assignable<V>::value,
370 "Value type must be move-assignable");
371
372 typedef K key_type;
373 typedef V mapped_type;
374
376 // the null value must convert to false and all others to true, but
377 // we can't statically check the latter.
379 }
381 assert(!m_iterating);
382 }
383
384 // possible to implement but we don't need it
387
388 const V &get(const K &key) const {
389 if (m_iterating) {
390 auto it = m_new.find(key);
391 if (it != m_new.end())
392 return it->second;
393 }
394 auto it = m_values.find(key);
395 // This conditional block was converted from a ternary to ensure no
396 // temporary values are created in evaluating the return expression,
397 // which could cause a dangling reference.
398 if (it != m_values.end())
399 return it->second;
400 else
401 return null_value;
402 }
403
404 void put(const K &key, const V &value) {
405 if (!value) {
406 assert(false);
407 return;
408 }
409 if (m_iterating) {
410 auto it = m_values.find(key);
411 if (it != m_values.end()) {
412 it->second = V();
413 m_garbage++;
414 }
415 m_new[key] = value;
416 } else {
417 m_values[key] = value;
418 }
419 }
420
421 void put(const K &key, V &&value) {
422 if (!value) {
423 assert(false);
424 return;
425 }
426 if (m_iterating) {
427 auto it = m_values.find(key);
428 if (it != m_values.end()) {
429 it->second = V();
430 m_garbage++;
431 }
432 m_new[key] = std::move(value);
433 } else {
434 m_values[key] = std::move(value);
435 }
436 }
437
438 V take(const K &key) {
439 V ret = V();
440 if (m_iterating) {
441 auto it = m_new.find(key);
442 if (it != m_new.end()) {
443 ret = std::move(it->second);
444 m_new.erase(it);
445 }
446 }
447 auto it = m_values.find(key);
448 if (it == m_values.end())
449 return ret;
450 if (!ret)
451 ret = std::move(it->second);
452 if (m_iterating) {
453 it->second = V();
454 m_garbage++;
455 } else {
456 m_values.erase(it);
457 }
458 return ret;
459 }
460
461 bool remove(const K &key) {
462 return !!take(key);
463 }
464
466 size_t size() const {
467 if (m_iterating) {
468 // This is by no means impossible to determine, it's just annoying
469 // to code and we happen to not need this.
470 return unknown;
471 }
472 assert(m_new.empty());
473 if (m_garbage == 0)
474 return m_values.size();
475 size_t n = 0;
476 for (auto &it : m_values)
477 n += !it.second ? 0 : 1;
478 return n;
479 }
480
482 bool empty() const {
483 if (m_iterating)
484 return false; // maybe
485 if (m_garbage == 0)
486 return m_values.empty();
487 for (auto &it : m_values) {
488 if (!!it.second)
489 return false;
490 }
491 return true;
492 }
493
494 auto iter() { return IterationHelper(this); }
495
496 void clear() {
497 if (m_iterating) {
498 for (auto &it : m_values)
499 it.second = V();
500 m_garbage = m_values.size();
501 } else {
502 m_values.clear();
503 m_garbage = 0;
504 }
505 }
506
507 static inline const V null_value = V();
508
509 // returned by size() if called during iteration
510 static constexpr size_t unknown = static_cast<size_t>(-1);
511
512protected:
513 void merge_new() {
514 assert(!m_iterating);
515 if (!m_new.empty()) {
516 m_new.merge(m_values); // entries in m_new take precedence
517 m_values.clear();
518 std::swap(m_values, m_new);
519 }
520 }
521
523 assert(!m_iterating);
524 if (m_values.size() < GC_MIN_SIZE || m_garbage < m_values.size() / 2)
525 return;
526 for (auto it = m_values.begin(); it != m_values.end(); ) {
527 if (!it->second)
528 it = m_values.erase(it);
529 else
530 ++it;
531 }
532 m_garbage = 0;
533 }
534
536 friend class ModifySafeMap<K, V>;
538 assert(m->m_iterating);
539 m->m_iterating--;
540 if (!m->m_iterating) {
541 m->merge_new();
542 m->collect_garbage();
543 }
544 }
545
546 auto begin() { return m->m_values.cbegin(); }
547 auto end() { return m->m_values.cend(); }
548
549 private:
551 assert(m->m_iterating < std::numeric_limits<decltype(m_iterating)>::max());
552 m->m_iterating++;
553 }
554
556 };
557
558private:
559 std::map<K, V> m_values;
560 std::map<K, V> m_new;
561 unsigned int m_iterating = 0;
562 // approximate amount of null-placeholders in m_values, reliable for != 0 tests
563 size_t m_garbage = 0;
564
565 static constexpr size_t GC_MIN_SIZE = 30;
566};
#define DISABLE_CLASS_COPY(C)
Definition basic_macros.h:26
#define ALLOW_CLASS_MOVE(C)
Definition basic_macros.h:32
Definition exceptions.h:66
size_t m_limit
Definition container.h:337
cache_type m_map
Definition container.h:340
LRUCache(size_t limit, void(*cache_miss)(void *data, const K &key, V *dest), void *data)
Definition container.h:280
void setLimit(size_t limit)
Definition container.h:288
void * m_cache_miss_data
Definition container.h:336
const V * lookupCache(K key)
Definition container.h:300
void invalidate()
Definition container.h:294
std::list< K > m_queue
Definition container.h:342
std::template pair< typename std::template list< K >::iterator, V > cache_entry_t
Definition container.h:338
std::template map< K, cache_entry_t > cache_type
Definition container.h:339
void(* m_cache_miss)(void *data, const K &key, V *dest)
Definition container.h:335
Definition container.h:362
const void *& get(const u16 &key) const
Definition container.h:388
size_t m_garbage
Definition container.h:563
void * mapped_type
Definition container.h:373
~ModifySafeMap()
Definition container.h:380
u16 key_type
Definition container.h:372
void clear()
Definition container.h:496
size_t size() const
Definition container.h:466
auto iter()
Definition container.h:494
void put(const K &key, V &&value)
Definition container.h:421
ModifySafeMap()
Definition container.h:375
V take(const K &key)
Definition container.h:438
static constexpr size_t GC_MIN_SIZE
Definition container.h:565
static constexpr size_t unknown
Definition container.h:510
void put(const K &key, const V &value)
Definition container.h:404
void collect_garbage()
Definition container.h:522
bool empty() const
Definition container.h:482
bool remove(const K &key)
Definition container.h:461
std::map< u16, void * > m_values
Definition container.h:559
unsigned int m_iterating
Definition container.h:561
static const void * null_value
Definition container.h:507
void merge_new()
Definition container.h:513
std::map< u16, void * > m_new
Definition container.h:560
std::vector< Value > getValues() const
Definition container.h:98
bool get(const Key &name, Value *result) const
Definition container.h:87
MutexedMap()=default
std::map< Key, Value > m_values
Definition container.h:115
std::mutex m_mutex
Definition container.h:116
void set(const Key &name, const Value &value)
Definition container.h:81
void clear()
Definition container.h:108
friend class RequestQueue
Definition container.h:129
bool empty() const
Definition container.h:133
void push_back(T &&t)
Definition container.h:146
std::deque< MeshUpdateResult > m_queue
Definition container.h:267
Semaphore m_signal
Definition container.h:269
T pop_front(u32 wait_time_max_ms)
Definition container.h:169
T pop_back(u32 wait_time_max_ms=0)
Definition container.h:193
T pop_frontNoEx()
Definition container.h:182
T pop_backNoEx()
Definition container.h:222
std::deque< T > & getQueue()
Definition container.h:265
std::mutex m_mutex
Definition container.h:268
auto iterLocked()
Definition container.h:233
Semaphore & getSignal()
Definition container.h:263
T pop_backNoEx(u32 wait_time_max_ms)
Definition container.h:209
T pop_frontNoEx(u32 wait_time_max_ms)
Definition container.h:156
void push_back(const T &t)
Definition container.h:139
MutexedQueue()=default
std::mutex & getMutex()
Definition container.h:262
Definition semaphore.h:18
Definition container.h:26
size_t size() const
Definition container.h:56
void pop_front()
Definition container.h:45
bool push_back(const Value &value)
Definition container.h:35
std::queue< Value > m_queue
Definition container.h:68
const Value & front() const
Definition container.h:51
bool empty() const
Definition container.h:61
std::unordered_set< Value > m_set
Definition container.h:67
#define sanity_check(expr)
Definition debug.h:55
std::lock_guard< std::mutex > MutexAutoLock
Definition mutex_auto_lock.h:31
ModifySafeMap< K, V > * m
Definition container.h:555
IterationHelper(ModifySafeMap< K, V > *parent)
Definition container.h:550
~IterationHelper()
Definition container.h:537
auto end()
Definition container.h:547
auto begin()
Definition container.h:546
auto end()
Definition container.h:248
auto erase(typename std::deque< T >::iterator it)
Definition container.h:250
IterationHelper(MutexedQueue< T > *parent)
Definition container.h:255
~IterationHelper()
Definition container.h:242
auto begin()
Definition container.h:247
MutexedQueue< T > * q
Definition container.h:259