Luanti 5.15.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
233protected:
234 std::mutex &getMutex() { return m_mutex; }
235
236 std::deque<T> &getQueue() { return m_queue; }
237
238 std::deque<T> m_queue;
239 mutable std::mutex m_mutex;
241};
242
243/*
244 LRU cache
245*/
246
247template<typename K, typename V>
249{
250public:
251 LRUCache(size_t limit, void (*cache_miss)(void *data, const K &key, V *dest),
252 void *data)
253 {
254 m_limit = limit;
255 m_cache_miss = cache_miss;
256 m_cache_miss_data = data;
257 }
258
259 void setLimit(size_t limit)
260 {
261 m_limit = limit;
262 invalidate();
263 }
264
266 {
267 m_map.clear();
268 m_queue.clear();
269 }
270
271 const V *lookupCache(K key)
272 {
273 typename cache_type::iterator it = m_map.find(key);
274 V *ret;
275 if (it != m_map.end()) {
276 // found!
277
278 cache_entry_t &entry = it->second;
279
280 ret = &entry.second;
281
282 // update the usage information
283 m_queue.erase(entry.first);
284 m_queue.push_front(key);
285 entry.first = m_queue.begin();
286 } else {
287 // cache miss -- enter into cache
288 cache_entry_t &entry =
289 m_map[key];
290 ret = &entry.second;
291 m_cache_miss(m_cache_miss_data, key, &entry.second);
292
293 // delete old entries
294 if (m_queue.size() == m_limit) {
295 const K &id = m_queue.back();
296 m_map.erase(id);
297 m_queue.pop_back();
298 }
299
300 m_queue.push_front(key);
301 entry.first = m_queue.begin();
302 }
303 return ret;
304 }
305private:
306 void (*m_cache_miss)(void *data, const K &key, V *dest);
308 size_t m_limit;
309 typedef typename std::template pair<typename std::template list<K>::iterator, V> cache_entry_t;
310 typedef std::template map<K, cache_entry_t> cache_type;
312 // we can't use std::deque here, because its iterators get invalidated
313 std::list<K> m_queue;
314};
315
316/*
317 Map that can be safely modified (insertion, deletion) during iteration
318 Caveats:
319 - you cannot insert null elements
320 - you have to check for null elements during iteration, those are ones already deleted
321 - size() and empty() don't work during iteration
322 - not thread-safe in any way
323
324 How this is implemented:
325 - there are two maps: new and real
326 - if inserting duration iteration, the value is inserted into the "new" map
327 - if deleting during iteration, the value is set to null (to be GC'd later)
328 - when iteration finishes the "new" map is merged into the "real" map
329*/
330
331template<typename K, typename V>
333{
334public:
335 // this allows bare pointers but also e.g. std::unique_ptr
336 static_assert(std::is_default_constructible<V>::value,
337 "Value type must be default constructible");
338 static_assert(std::is_constructible<bool, V>::value,
339 "Value type must be convertible to bool");
340 static_assert(std::is_move_assignable<V>::value,
341 "Value type must be move-assignable");
342
343 typedef K key_type;
344 typedef V mapped_type;
345
347 // the null value must convert to false and all others to true, but
348 // we can't statically check the latter.
350 }
352 assert(!m_iterating);
353 }
354
355 // possible to implement but we don't need it
358
359 const V &get(const K &key) const {
360 if (m_iterating) {
361 auto it = m_new.find(key);
362 if (it != m_new.end())
363 return it->second;
364 }
365 auto it = m_values.find(key);
366 // This conditional block was converted from a ternary to ensure no
367 // temporary values are created in evaluating the return expression,
368 // which could cause a dangling reference.
369 if (it != m_values.end())
370 return it->second;
371 else
372 return null_value;
373 }
374
375 void put(const K &key, const V &value) {
376 if (!value) {
377 assert(false);
378 return;
379 }
380 if (m_iterating) {
381 auto it = m_values.find(key);
382 if (it != m_values.end()) {
383 it->second = V();
384 m_garbage++;
385 }
386 m_new[key] = value;
387 } else {
388 m_values[key] = value;
389 }
390 }
391
392 void put(const K &key, V &&value) {
393 if (!value) {
394 assert(false);
395 return;
396 }
397 if (m_iterating) {
398 auto it = m_values.find(key);
399 if (it != m_values.end()) {
400 it->second = V();
401 m_garbage++;
402 }
403 m_new[key] = std::move(value);
404 } else {
405 m_values[key] = std::move(value);
406 }
407 }
408
409 V take(const K &key) {
410 V ret = V();
411 if (m_iterating) {
412 auto it = m_new.find(key);
413 if (it != m_new.end()) {
414 ret = std::move(it->second);
415 m_new.erase(it);
416 }
417 }
418 auto it = m_values.find(key);
419 if (it == m_values.end())
420 return ret;
421 if (!ret)
422 ret = std::move(it->second);
423 if (m_iterating) {
424 it->second = V();
425 m_garbage++;
426 } else {
427 m_values.erase(it);
428 }
429 return ret;
430 }
431
432 bool remove(const K &key) {
433 return !!take(key);
434 }
435
437 size_t size() const {
438 if (m_iterating) {
439 // This is by no means impossible to determine, it's just annoying
440 // to code and we happen to not need this.
441 return unknown;
442 }
443 assert(m_new.empty());
444 if (m_garbage == 0)
445 return m_values.size();
446 size_t n = 0;
447 for (auto &it : m_values)
448 n += !it.second ? 0 : 1;
449 return n;
450 }
451
453 bool empty() const {
454 if (m_iterating)
455 return false; // maybe
456 if (m_garbage == 0)
457 return m_values.empty();
458 for (auto &it : m_values) {
459 if (!!it.second)
460 return false;
461 }
462 return true;
463 }
464
465 auto iter() { return IterationHelper(this); }
466
467 void clear() {
468 if (m_iterating) {
469 for (auto &it : m_values)
470 it.second = V();
471 m_garbage = m_values.size();
472 } else {
473 m_values.clear();
474 m_garbage = 0;
475 }
476 }
477
478 static inline const V null_value = V();
479
480 // returned by size() if called during iteration
481 static constexpr size_t unknown = static_cast<size_t>(-1);
482
483protected:
484 void merge_new() {
485 assert(!m_iterating);
486 if (!m_new.empty()) {
487 m_new.merge(m_values); // entries in m_new take precedence
488 m_values.clear();
489 std::swap(m_values, m_new);
490 }
491 }
492
494 assert(!m_iterating);
495 if (m_values.size() < GC_MIN_SIZE || m_garbage < m_values.size() / 2)
496 return;
497 for (auto it = m_values.begin(); it != m_values.end(); ) {
498 if (!it->second)
499 it = m_values.erase(it);
500 else
501 ++it;
502 }
503 m_garbage = 0;
504 }
505
507 friend class ModifySafeMap<K, V>;
509 assert(m->m_iterating);
510 m->m_iterating--;
511 if (!m->m_iterating) {
512 m->merge_new();
513 m->collect_garbage();
514 }
515 }
516
517 auto begin() { return m->m_values.cbegin(); }
518 auto end() { return m->m_values.cend(); }
519
520 private:
522 assert(m->m_iterating < std::numeric_limits<decltype(m_iterating)>::max());
523 m->m_iterating++;
524 }
525
527 };
528
529private:
530 std::map<K, V> m_values;
531 std::map<K, V> m_new;
532 unsigned int m_iterating = 0;
533 // approximate amount of null-placeholders in m_values, reliable for != 0 tests
534 size_t m_garbage = 0;
535
536 static constexpr size_t GC_MIN_SIZE = 30;
537};
#define DISABLE_CLASS_COPY(C)
Definition basic_macros.h:26
#define ALLOW_CLASS_MOVE(C)
Definition basic_macros.h:32
Definition exceptions.h:60
Definition container.h:249
size_t m_limit
Definition container.h:308
cache_type m_map
Definition container.h:311
LRUCache(size_t limit, void(*cache_miss)(void *data, const K &key, V *dest), void *data)
Definition container.h:251
void setLimit(size_t limit)
Definition container.h:259
void * m_cache_miss_data
Definition container.h:307
const V * lookupCache(K key)
Definition container.h:271
void invalidate()
Definition container.h:265
std::list< K > m_queue
Definition container.h:313
std::template pair< typename std::template list< K >::iterator, V > cache_entry_t
Definition container.h:309
std::template map< K, cache_entry_t > cache_type
Definition container.h:310
void(* m_cache_miss)(void *data, const K &key, V *dest)
Definition container.h:306
Definition container.h:333
const V & get(const K &key) const
Definition container.h:359
size_t m_garbage
Definition container.h:534
V mapped_type
Definition container.h:344
~ModifySafeMap()
Definition container.h:351
K key_type
Definition container.h:343
void clear()
Definition container.h:467
size_t size() const
Definition container.h:437
auto iter()
Definition container.h:465
void put(const K &key, V &&value)
Definition container.h:392
ModifySafeMap()
Definition container.h:346
V take(const K &key)
Definition container.h:409
static constexpr size_t GC_MIN_SIZE
Definition container.h:536
static constexpr size_t unknown
Definition container.h:481
void put(const K &key, const V &value)
Definition container.h:375
void collect_garbage()
Definition container.h:493
bool empty() const
Definition container.h:453
bool remove(const K &key)
Definition container.h:432
std::map< K, V > m_values
Definition container.h:530
unsigned int m_iterating
Definition container.h:532
static const V null_value
Definition container.h:478
void merge_new()
Definition container.h:484
std::map< K, V > m_new
Definition container.h:531
Definition container.h:77
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
Definition container.h:126
bool empty() const
Definition container.h:133
void push_back(T &&t)
Definition container.h:146
std::deque< T > m_queue
Definition container.h:238
Semaphore m_signal
Definition container.h:240
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:236
std::mutex m_mutex
Definition container.h:239
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:234
Notes for RequestQueue usage.
Definition thread.h:95
Definition semaphore.h:18
void post(unsigned int num=1)
Definition semaphore.cpp:70
void wait()
Definition semaphore.cpp:85
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
Definition container.h:506
ModifySafeMap< K, V > * m
Definition container.h:526
IterationHelper(ModifySafeMap< K, V > *parent)
Definition container.h:521
~IterationHelper()
Definition container.h:508
auto end()
Definition container.h:518
auto begin()
Definition container.h:517