Luanti 5.10.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 <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 u32 size() const
57 {
58 return m_queue.size();
59 }
60
61private:
62 std::set<Value> m_set;
63 std::queue<Value> m_queue;
64};
65
66/*
67 Thread-safe map
68*/
69
70template<typename Key, typename Value>
72{
73public:
74 MutexedMap() = default;
75
76 void set(const Key &name, const Value &value)
77 {
79 m_values[name] = value;
80 }
81
82 bool get(const Key &name, Value *result) const
83 {
85 auto n = m_values.find(name);
86 if (n == m_values.end())
87 return false;
88 if (result)
89 *result = n->second;
90 return true;
91 }
92
93 std::vector<Value> getValues() const
94 {
96 std::vector<Value> result;
97 result.reserve(m_values.size());
98 for (auto it = m_values.begin(); it != m_values.end(); ++it)
99 result.push_back(it->second);
100 return result;
101 }
102
103 void clear() { m_values.clear(); }
104
105private:
106 std::map<Key, Value> m_values;
107 mutable std::mutex m_mutex;
108};
109
110
111/*
112 Thread-safe double-ended queue
113*/
114
115template<typename T>
117{
118public:
119 template<typename Key, typename U, typename Caller, typename CallerData>
120 friend class RequestQueue;
121
122 MutexedQueue() = default;
123
124 bool empty() const
125 {
127 return m_queue.empty();
128 }
129
130 void push_back(const T &t)
131 {
133 m_queue.push_back(t);
134 m_signal.post();
135 }
136
137 void push_back(T &&t)
138 {
140 m_queue.push_back(std::move(t));
141 m_signal.post();
142 }
143
144 /* this version of pop_front returns an empty element of T on timeout.
145 * Make sure default constructor of T creates a recognizable "empty" element
146 */
147 T pop_frontNoEx(u32 wait_time_max_ms)
148 {
149 if (m_signal.wait(wait_time_max_ms)) {
151
152 T t = std::move(m_queue.front());
153 m_queue.pop_front();
154 return t;
155 }
156
157 return T();
158 }
159
160 T pop_front(u32 wait_time_max_ms)
161 {
162 if (m_signal.wait(wait_time_max_ms)) {
164
165 T t = std::move(m_queue.front());
166 m_queue.pop_front();
167 return t;
168 }
169
170 throw ItemNotFoundException("MutexedQueue: queue is empty");
171 }
172
174 {
175 m_signal.wait();
176
178
179 T t = std::move(m_queue.front());
180 m_queue.pop_front();
181 return t;
182 }
183
184 T pop_back(u32 wait_time_max_ms=0)
185 {
186 if (m_signal.wait(wait_time_max_ms)) {
188
189 T t = std::move(m_queue.back());
190 m_queue.pop_back();
191 return t;
192 }
193
194 throw ItemNotFoundException("MutexedQueue: queue is empty");
195 }
196
197 /* this version of pop_back returns an empty element of T on timeout.
198 * Make sure default constructor of T creates a recognizable "empty" element
199 */
200 T pop_backNoEx(u32 wait_time_max_ms)
201 {
202 if (m_signal.wait(wait_time_max_ms)) {
204
205 T t = std::move(m_queue.back());
206 m_queue.pop_back();
207 return t;
208 }
209
210 return T();
211 }
212
214 {
215 m_signal.wait();
216
218
219 T t = std::move(m_queue.back());
220 m_queue.pop_back();
221 return t;
222 }
223
224protected:
225 std::mutex &getMutex() { return m_mutex; }
226
227 std::deque<T> &getQueue() { return m_queue; }
228
229 std::deque<T> m_queue;
230 mutable std::mutex m_mutex;
232};
233
234/*
235 LRU cache
236*/
237
238template<typename K, typename V>
240{
241public:
242 LRUCache(size_t limit, void (*cache_miss)(void *data, const K &key, V *dest),
243 void *data)
244 {
245 m_limit = limit;
246 m_cache_miss = cache_miss;
247 m_cache_miss_data = data;
248 }
249
250 void setLimit(size_t limit)
251 {
252 m_limit = limit;
253 invalidate();
254 }
255
257 {
258 m_map.clear();
259 m_queue.clear();
260 }
261
262 const V *lookupCache(K key)
263 {
264 typename cache_type::iterator it = m_map.find(key);
265 V *ret;
266 if (it != m_map.end()) {
267 // found!
268
269 cache_entry_t &entry = it->second;
270
271 ret = &entry.second;
272
273 // update the usage information
274 m_queue.erase(entry.first);
275 m_queue.push_front(key);
276 entry.first = m_queue.begin();
277 } else {
278 // cache miss -- enter into cache
279 cache_entry_t &entry =
280 m_map[key];
281 ret = &entry.second;
282 m_cache_miss(m_cache_miss_data, key, &entry.second);
283
284 // delete old entries
285 if (m_queue.size() == m_limit) {
286 const K &id = m_queue.back();
287 m_map.erase(id);
288 m_queue.pop_back();
289 }
290
291 m_queue.push_front(key);
292 entry.first = m_queue.begin();
293 }
294 return ret;
295 }
296private:
297 void (*m_cache_miss)(void *data, const K &key, V *dest);
299 size_t m_limit;
300 typedef typename std::template pair<typename std::template list<K>::iterator, V> cache_entry_t;
301 typedef std::template map<K, cache_entry_t> cache_type;
303 // we can't use std::deque here, because its iterators get invalidated
304 std::list<K> m_queue;
305};
306
307/*
308 Map that can be safely modified (insertion, deletion) during iteration
309 Caveats:
310 - you cannot insert null elements
311 - you have to check for null elements during iteration, those are ones already deleted
312 - size() and empty() don't work during iteration
313 - not thread-safe in any way
314
315 How this is implemented:
316 - there are two maps: new and real
317 - if inserting duration iteration, the value is inserted into the "new" map
318 - if deleting during iteration, the value is set to null (to be GC'd later)
319 - when iteration finishes the "new" map is merged into the "real" map
320*/
321
322template<typename K, typename V>
324{
325public:
326 // this allows bare pointers but also e.g. std::unique_ptr
327 static_assert(std::is_default_constructible<V>::value,
328 "Value type must be default constructible");
329 static_assert(std::is_constructible<bool, V>::value,
330 "Value type must be convertible to bool");
331 static_assert(std::is_move_assignable<V>::value,
332 "Value type must be move-assignable");
333
334 typedef K key_type;
335 typedef V mapped_type;
336
338 // the null value must convert to false and all others to true, but
339 // we can't statically check the latter.
341 }
343 assert(!m_iterating);
344 }
345
346 // possible to implement but we don't need it
349
350 const V &get(const K &key) const {
351 if (m_iterating) {
352 auto it = m_new.find(key);
353 if (it != m_new.end())
354 return it->second;
355 }
356 auto it = m_values.find(key);
357 // This conditional block was converted from a ternary to ensure no
358 // temporary values are created in evaluating the return expression,
359 // which could cause a dangling reference.
360 if (it != m_values.end()) {
361 return it->second;
362 } else {
363 return null_value;
364 }
365 }
366
367 void put(const K &key, const V &value) {
368 if (!value) {
369 assert(false);
370 return;
371 }
372 if (m_iterating) {
373 auto it = m_values.find(key);
374 if (it != m_values.end()) {
375 it->second = V();
376 m_garbage++;
377 }
378 m_new[key] = value;
379 } else {
380 m_values[key] = value;
381 }
382 }
383
384 void put(const K &key, V &&value) {
385 if (!value) {
386 assert(false);
387 return;
388 }
389 if (m_iterating) {
390 auto it = m_values.find(key);
391 if (it != m_values.end()) {
392 it->second = V();
393 m_garbage++;
394 }
395 m_new[key] = std::move(value);
396 } else {
397 m_values[key] = std::move(value);
398 }
399 }
400
401 V take(const K &key) {
402 V ret = V();
403 if (m_iterating) {
404 auto it = m_new.find(key);
405 if (it != m_new.end()) {
406 ret = std::move(it->second);
407 m_new.erase(it);
408 }
409 }
410 auto it = m_values.find(key);
411 if (it == m_values.end())
412 return ret;
413 if (!ret)
414 ret = std::move(it->second);
415 if (m_iterating) {
416 it->second = V();
417 m_garbage++;
418 } else {
419 m_values.erase(it);
420 }
421 return ret;
422 }
423
424 bool remove(const K &key) {
425 return !!take(key);
426 }
427
428 // Warning: not constant-time!
429 size_t size() const {
430 if (m_iterating) {
431 // This is by no means impossible to determine, it's just annoying
432 // to code and we happen to not need this.
433 return unknown;
434 }
435 assert(m_new.empty());
436 if (m_garbage == 0)
437 return m_values.size();
438 size_t n = 0;
439 for (auto &it : m_values)
440 n += !it.second ? 0 : 1;
441 return n;
442 }
443
444 // Warning: not constant-time!
445 bool empty() const {
446 if (m_iterating)
447 return false; // maybe
448 if (m_garbage == 0)
449 return m_values.empty();
450 for (auto &it : m_values) {
451 if (!!it.second)
452 return false;
453 }
454 return true;
455 }
456
457 auto iter() { return IterationHelper(this); }
458
459 void clear() {
460 if (m_iterating) {
461 for (auto &it : m_values)
462 it.second = V();
463 m_garbage = m_values.size();
464 } else {
465 m_values.clear();
466 m_garbage = 0;
467 }
468 }
469
470 static inline const V null_value = V();
471
472 // returned by size() if called during iteration
473 static constexpr size_t unknown = static_cast<size_t>(-1);
474
475protected:
476 void merge_new() {
477 assert(!m_iterating);
478 if (!m_new.empty()) {
479 m_new.merge(m_values); // entries in m_new take precedence
480 m_values.clear();
481 std::swap(m_values, m_new);
482 }
483 }
484
486 assert(!m_iterating);
487 if (m_values.size() < GC_MIN_SIZE || m_garbage < m_values.size() / 2)
488 return;
489 for (auto it = m_values.begin(); it != m_values.end(); ) {
490 if (!it->second)
491 it = m_values.erase(it);
492 else
493 ++it;
494 }
495 m_garbage = 0;
496 }
497
499 friend class ModifySafeMap<K, V>;
501 assert(m->m_iterating);
502 m->m_iterating--;
503 if (!m->m_iterating) {
504 m->merge_new();
505 m->collect_garbage();
506 }
507 }
508
509 auto begin() { return m->m_values.cbegin(); }
510 auto end() { return m->m_values.cend(); }
511
512 private:
514 assert(m->m_iterating < std::numeric_limits<decltype(m_iterating)>::max());
515 m->m_iterating++;
516 }
517
519 };
520
521private:
522 std::map<K, V> m_values;
523 std::map<K, V> m_new;
524 unsigned int m_iterating = 0;
525 // approximate amount of null-placeholders in m_values, reliable for != 0 tests
526 size_t m_garbage = 0;
527
528 static constexpr size_t GC_MIN_SIZE = 30;
529};
#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:240
size_t m_limit
Definition container.h:299
cache_type m_map
Definition container.h:302
LRUCache(size_t limit, void(*cache_miss)(void *data, const K &key, V *dest), void *data)
Definition container.h:242
void setLimit(size_t limit)
Definition container.h:250
void * m_cache_miss_data
Definition container.h:298
const V * lookupCache(K key)
Definition container.h:262
void invalidate()
Definition container.h:256
std::list< K > m_queue
Definition container.h:304
std::template pair< typename std::template list< K >::iterator, V > cache_entry_t
Definition container.h:300
std::template map< K, cache_entry_t > cache_type
Definition container.h:301
void(* m_cache_miss)(void *data, const K &key, V *dest)
Definition container.h:297
Definition container.h:324
const V & get(const K &key) const
Definition container.h:350
size_t m_garbage
Definition container.h:526
V mapped_type
Definition container.h:335
~ModifySafeMap()
Definition container.h:342
K key_type
Definition container.h:334
void clear()
Definition container.h:459
size_t size() const
Definition container.h:429
auto iter()
Definition container.h:457
void put(const K &key, V &&value)
Definition container.h:384
ModifySafeMap()
Definition container.h:337
V take(const K &key)
Definition container.h:401
static constexpr size_t GC_MIN_SIZE
Definition container.h:528
static constexpr size_t unknown
Definition container.h:473
void put(const K &key, const V &value)
Definition container.h:367
void collect_garbage()
Definition container.h:485
bool empty() const
Definition container.h:445
bool remove(const K &key)
Definition container.h:424
std::map< K, V > m_values
Definition container.h:522
unsigned int m_iterating
Definition container.h:524
static const V null_value
Definition container.h:470
void merge_new()
Definition container.h:476
std::map< K, V > m_new
Definition container.h:523
Definition container.h:72
std::vector< Value > getValues() const
Definition container.h:93
bool get(const Key &name, Value *result) const
Definition container.h:82
MutexedMap()=default
std::map< Key, Value > m_values
Definition container.h:106
std::mutex m_mutex
Definition container.h:107
void set(const Key &name, const Value &value)
Definition container.h:76
void clear()
Definition container.h:103
Definition container.h:117
bool empty() const
Definition container.h:124
void push_back(T &&t)
Definition container.h:137
std::deque< T > m_queue
Definition container.h:229
Semaphore m_signal
Definition container.h:231
T pop_front(u32 wait_time_max_ms)
Definition container.h:160
T pop_back(u32 wait_time_max_ms=0)
Definition container.h:184
T pop_frontNoEx()
Definition container.h:173
T pop_backNoEx()
Definition container.h:213
std::deque< T > & getQueue()
Definition container.h:227
std::mutex m_mutex
Definition container.h:230
T pop_backNoEx(u32 wait_time_max_ms)
Definition container.h:200
T pop_frontNoEx(u32 wait_time_max_ms)
Definition container.h:147
void push_back(const T &t)
Definition container.h:130
MutexedQueue()=default
std::mutex & getMutex()
Definition container.h:225
Notes for RequestQueue usage.
Definition thread.h:85
Definition semaphore.h:18
void post(unsigned int num=1)
Definition semaphore.cpp:70
void wait()
Definition semaphore.cpp:85
Definition container.h:26
u32 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:63
std::set< Value > m_set
Definition container.h:62
const Value & front() const
Definition container.h:51
#define sanity_check(expr)
Definition debug.h:56
std::lock_guard< std::mutex > MutexAutoLock
Definition mutex_auto_lock.h:31
Definition container.h:498
ModifySafeMap< K, V > * m
Definition container.h:518
IterationHelper(ModifySafeMap< K, V > *parent)
Definition container.h:513
~IterationHelper()
Definition container.h:500
auto end()
Definition container.h:510
auto begin()
Definition container.h:509