Luanti 5.17.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 setLimit(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 = std::max<size_t>(1, limit);
291 invalidate();
292 }
293
295 {
296 m_map.clear();
297 m_queue.clear();
298 }
299
300 const V *lookupCache(const K &key)
301 {
302 auto it = m_map.find(key);
303 const V *ret;
304 if (it != m_map.end()) {
305 // found!
306 cache_entry &entry = it->second;
307
308 ret = &entry.second;
309
310 // update the usage information
311 if (entry.first != m_queue.begin()) {
312 m_queue.erase(entry.first);
313 m_queue.push_front(key);
314 entry.first = m_queue.begin();
315 }
316 } else {
317 // cache miss -- enter into cache
318 cache_entry &entry = m_map[key];
319 m_cache_miss(m_cache_miss_data, key, &entry.second);
320 ret = &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 }
334
335private:
336 typedef typename std::pair<typename std::list<K>::iterator, V> cache_entry;
337 typedef std::map<K, cache_entry> cache_type;
338
339 void (*m_cache_miss)(void *data, const K &key, V *dest);
341 size_t m_limit;
342
344 // we can't use std::deque here, because its iterators get invalidated
345 std::list<K> m_queue;
346};
347
348/*
349 Map that can be safely modified (insertion, deletion) during iteration
350 Caveats:
351 - you cannot insert null elements
352 - you have to check for null elements during iteration, those are ones already deleted
353 - size() and empty() don't work during iteration
354 - not thread-safe in any way
355
356 How this is implemented:
357 - there are two maps: new and real
358 - if inserting during iteration, the value is inserted into the "new" map
359 - if deleting during iteration, the value is set to null (to be GC'd later)
360 - when iteration finishes the "new" map is merged into the "real" map
361*/
362
363template<typename K, typename V>
365{
366public:
367 // this allows bare pointers but also e.g. std::unique_ptr
368 static_assert(std::is_default_constructible<V>::value,
369 "Value type must be default constructible");
370 static_assert(std::is_constructible<bool, V>::value,
371 "Value type must be convertible to bool");
372 static_assert(std::is_move_assignable<V>::value,
373 "Value type must be move-assignable");
374
375 typedef K key_type;
376 typedef V mapped_type;
377
379 // the null value must convert to false and all others to true, but
380 // we can't statically check the latter.
382 }
384 assert(!m_iterating);
385 }
386
387 // possible to implement but we don't need it
390
391 const V &get(const K &key) const {
392 if (m_iterating) {
393 auto it = m_new.find(key);
394 if (it != m_new.end())
395 return it->second;
396 }
397 auto it = m_values.find(key);
398 // This conditional block was converted from a ternary to ensure no
399 // temporary values are created in evaluating the return expression,
400 // which could cause a dangling reference.
401 if (it != m_values.end())
402 return it->second;
403 else
404 return null_value;
405 }
406
407 void put(const K &key, const V &value) {
408 if (!value) {
409 assert(false);
410 return;
411 }
412 if (m_iterating) {
413 auto it = m_values.find(key);
414 if (it != m_values.end()) {
415 it->second = V();
416 m_garbage++;
417 }
418 m_new[key] = value;
419 } else {
420 m_values[key] = value;
421 }
422 }
423
424 void put(const K &key, V &&value) {
425 if (!value) {
426 assert(false);
427 return;
428 }
429 if (m_iterating) {
430 auto it = m_values.find(key);
431 if (it != m_values.end()) {
432 it->second = V();
433 m_garbage++;
434 }
435 m_new[key] = std::move(value);
436 } else {
437 m_values[key] = std::move(value);
438 }
439 }
440
441 V take(const K &key) {
442 V ret = V();
443 if (m_iterating) {
444 auto it = m_new.find(key);
445 if (it != m_new.end()) {
446 ret = std::move(it->second);
447 m_new.erase(it);
448 }
449 }
450 auto it = m_values.find(key);
451 if (it == m_values.end())
452 return ret;
453 if (!ret)
454 ret = std::move(it->second);
455 if (m_iterating) {
456 it->second = V();
457 m_garbage++;
458 } else {
459 m_values.erase(it);
460 }
461 return ret;
462 }
463
464 bool remove(const K &key) {
465 return !!take(key);
466 }
467
469 size_t size() const {
470 if (m_iterating) {
471 // This is by no means impossible to determine, it's just annoying
472 // to code and we happen to not need this.
473 return unknown;
474 }
475 assert(m_new.empty());
476 if (m_garbage == 0)
477 return m_values.size();
478 size_t n = 0;
479 for (auto &it : m_values)
480 n += !it.second ? 0 : 1;
481 return n;
482 }
483
485 bool empty() const {
486 if (m_iterating)
487 return false; // maybe
488 if (m_garbage == 0)
489 return m_values.empty();
490 for (auto &it : m_values) {
491 if (!!it.second)
492 return false;
493 }
494 return true;
495 }
496
497 auto iter() { return IterationHelper(this); }
498
499 void clear() {
500 if (m_iterating) {
501 for (auto &it : m_values)
502 it.second = V();
503 m_garbage = m_values.size();
504 } else {
505 m_values.clear();
506 m_garbage = 0;
507 }
508 }
509
510 static inline const V null_value = V();
511
512 // returned by size() if called during iteration
513 static constexpr size_t unknown = static_cast<size_t>(-1);
514
515protected:
516 void merge_new() {
517 assert(!m_iterating);
518 if (!m_new.empty()) {
519 m_new.merge(m_values); // entries in m_new take precedence
520 m_values.clear();
521 std::swap(m_values, m_new);
522 }
523 }
524
526 assert(!m_iterating);
527 if (m_values.size() < GC_MIN_SIZE || m_garbage < m_values.size() / 2)
528 return;
529 for (auto it = m_values.begin(); it != m_values.end(); ) {
530 if (!it->second)
531 it = m_values.erase(it);
532 else
533 ++it;
534 }
535 m_garbage = 0;
536 }
537
539 friend class ModifySafeMap<K, V>;
541 assert(m->m_iterating);
542 m->m_iterating--;
543 if (!m->m_iterating) {
544 m->merge_new();
545 m->collect_garbage();
546 }
547 }
548
549 auto begin() { return m->m_values.cbegin(); }
550 auto end() { return m->m_values.cend(); }
551
552 private:
554 assert(m->m_iterating < std::numeric_limits<decltype(m_iterating)>::max());
555 m->m_iterating++;
556 }
557
559 };
560
561private:
562 std::map<K, V> m_values;
563 std::map<K, V> m_new;
564 unsigned int m_iterating = 0;
565 // approximate amount of null-placeholders in m_values, reliable for != 0 tests
566 size_t m_garbage = 0;
567
568 static constexpr size_t GC_MIN_SIZE = 30;
569};
#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:341
cache_type m_map
Definition container.h:343
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
const V * lookupCache(const K &key)
Definition container.h:300
void * m_cache_miss_data
Definition container.h:340
std::map< K, cache_entry > cache_type
Definition container.h:337
std::pair< typename std::list< K >::iterator, V > cache_entry
Definition container.h:336
void invalidate()
Definition container.h:294
std::list< K > m_queue
Definition container.h:345
void(* m_cache_miss)(void *data, const K &key, V *dest)
Definition container.h:339
Definition container.h:365
const void *& get(const u16 &key) const
Definition container.h:391
size_t m_garbage
Definition container.h:566
void * mapped_type
Definition container.h:376
~ModifySafeMap()
Definition container.h:383
u16 key_type
Definition container.h:375
void clear()
Definition container.h:499
size_t size() const
Definition container.h:469
auto iter()
Definition container.h:497
void put(const K &key, V &&value)
Definition container.h:424
ModifySafeMap()
Definition container.h:378
V take(const K &key)
Definition container.h:441
static constexpr size_t GC_MIN_SIZE
Definition container.h:568
static constexpr size_t unknown
Definition container.h:513
void put(const K &key, const V &value)
Definition container.h:407
void collect_garbage()
Definition container.h:525
bool empty() const
Definition container.h:485
bool remove(const K &key)
Definition container.h:464
std::map< u16, void * > m_values
Definition container.h:562
unsigned int m_iterating
Definition container.h:564
static const void * null_value
Definition container.h:510
void merge_new()
Definition container.h:516
std::map< u16, void * > m_new
Definition container.h:563
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:558
IterationHelper(ModifySafeMap< K, V > *parent)
Definition container.h:553
~IterationHelper()
Definition container.h:540
auto end()
Definition container.h:550
auto begin()
Definition container.h:549
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