OpenTTD Source 20241224-master-gf74b0cf984
smallstack_type.hpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#ifndef SMALLSTACK_TYPE_HPP
11#define SMALLSTACK_TYPE_HPP
12
13#include <mutex>
14
20template<typename Titem, typename Tindex, Tindex Tgrowth_step, Tindex Tmax_size>
22public:
23 inline SimplePool() : first_unused(0), first_free(0) {}
24
30 inline std::mutex &GetMutex() { return this->mutex; }
31
36 inline Titem &Get(Tindex index) { return this->data[index]; }
37
42 inline Tindex Create()
43 {
44 Tindex index = this->FindFirstFree();
45 if (index < Tmax_size) {
46 this->data[index].valid = true;
47 this->first_free = index + 1;
48 this->first_unused = std::max(this->first_unused, this->first_free);
49 }
50 return index;
51 }
52
57 inline void Destroy(Tindex index)
58 {
59 this->data[index].valid = false;
60 this->first_free = std::min(this->first_free, index);
61 }
62
63private:
64
65 inline Tindex FindFirstFree()
66 {
67 Tindex index = this->first_free;
68 for (; index < this->first_unused; index++) {
69 if (!this->data[index].valid) return index;
70 }
71
72 if (index >= this->data.size() && index < Tmax_size) {
73 this->data.resize(index + 1);
74 }
75 return index;
76 }
77
78 struct SimplePoolPoolItem : public Titem {
79 bool valid;
80 };
81
82 Tindex first_unused;
83 Tindex first_free;
84
85 std::mutex mutex;
86 std::vector<SimplePoolPoolItem> data;
87};
88
93template <typename Titem, typename Tindex>
95 Tindex next;
96 Titem value;
97
103 inline SmallStackItem(const Titem &value, Tindex next) :
104 next(next), value(value) {}
105 SmallStackItem() = default;
106};
107
134template <typename Titem, typename Tindex, Titem Tinvalid, Tindex Tgrowth_step, Tindex Tmax_size>
135class SmallStack : public SmallStackItem<Titem, Tindex> {
136public:
137
139
143 struct PooledSmallStack : public Item {
145 };
146
148
153 inline SmallStack(const Titem &value = Tinvalid) : Item(value, Tmax_size) {}
154
158 inline ~SmallStack()
159 {
160 /* Pop() locks the mutex and after each pop the pool is consistent.*/
161 while (this->next != Tmax_size) this->Pop();
162 }
163
168 inline SmallStack(const SmallStack &other) : Item(other) { this->Branch(); }
169
175 inline SmallStack &operator=(const SmallStack &other)
176 {
177 if (this == &other) return *this;
178 while (this->next != Tmax_size) this->Pop();
179 this->next = other.next;
180 this->value = other.value;
181 /* Deleting and branching are independent operations, so it's fine to
182 * acquire separate locks for them. */
183 this->Branch();
184 return *this;
185 }
186
192 inline void Push(const Titem &item)
193 {
194 if (this->value != Tinvalid) {
195 std::lock_guard<std::mutex> lock(SmallStack::GetPool().GetMutex());
196 Tindex new_item = SmallStack::GetPool().Create();
197 if (new_item != Tmax_size) {
198 PooledSmallStack &pushed = SmallStack::GetPool().Get(new_item);
199 pushed.value = this->value;
200 pushed.next = this->next;
201 pushed.branch_count = 0;
202 this->next = new_item;
203 }
204 }
205 this->value = item;
206 }
207
212 inline Titem Pop()
213 {
214 Titem ret = this->value;
215 if (this->next == Tmax_size) {
216 this->value = Tinvalid;
217 } else {
218 std::lock_guard<std::mutex> lock(SmallStack::GetPool().GetMutex());
219 PooledSmallStack &popped = SmallStack::GetPool().Get(this->next);
220 this->value = popped.value;
221 if (popped.branch_count == 0) {
222 SmallStack::GetPool().Destroy(this->next);
223 } else {
224 --popped.branch_count;
225 /* We can't use Branch() here as we already have the mutex.*/
226 if (popped.next != Tmax_size) {
227 ++(SmallStack::GetPool().Get(popped.next).branch_count);
228 }
229 }
230 /* Accessing popped here is no problem as the pool will only set
231 * the validity flag, not actually delete the item, on Destroy().
232 * It's impossible for another thread to acquire the same item in
233 * the mean time because of the mutex. */
234 this->next = popped.next;
235 }
236 return ret;
237 }
238
243 inline bool IsEmpty() const
244 {
245 return this->value == Tinvalid && this->next == Tmax_size;
246 }
247
253 inline bool Contains(const Titem &item) const
254 {
255 if (item == Tinvalid || item == this->value) return true;
256 if (this->next != Tmax_size) {
257 std::lock_guard<std::mutex> lock(SmallStack::GetPool().GetMutex());
258 const SmallStack *in_list = this;
259 do {
260 in_list = static_cast<const SmallStack *>(
261 static_cast<const Item *>(&SmallStack::GetPool().Get(in_list->next)));
262 if (in_list->value == item) return true;
263 } while (in_list->next != Tmax_size);
264 }
265 return false;
266 }
267
268protected:
269 static SmallStackPool &GetPool()
270 {
271 static SmallStackPool pool;
272 return pool;
273 }
274
278 inline void Branch()
279 {
280 if (this->next != Tmax_size) {
281 std::lock_guard<std::mutex> lock(SmallStack::GetPool().GetMutex());
282 ++(SmallStack::GetPool().Get(this->next).branch_count);
283 }
284 }
285};
286
287#endif
A simplified pool which stores values instead of pointers and doesn't redefine operator new/delete.
std::mutex & GetMutex()
Get the mutex.
Titem & Get(Tindex index)
Get the item at position index.
Tindex Create()
Create a new item and return its index.
void Destroy(Tindex index)
Destroy (or rather invalidate) the item at the given index.
Minimal stack that uses a pool to avoid pointers.
SmallStack & operator=(const SmallStack &other)
Shallow copy the stack, marking the first item as branched.
bool Contains(const Titem &item) const
Check if the given item is contained in the stack.
void Branch()
Create a branch in the pool if necessary.
Titem Pop()
Pop an item from the stack.
SmallStack(const SmallStack &other)
Shallow copy the stack, marking the first item as branched.
SmallStack(const Titem &value=Tinvalid)
Constructor for a stack with one or two items in it.
bool IsEmpty() const
Check if the stack is empty.
void Push(const Titem &item)
Pushes a new item onto the stack if there is still space in the underlying pool.
~SmallStack()
Remove the head of stack and all other items members that are unique to it.
uint8_t valid
Bits indicating what variable is valid (for each bit, 0 is invalid, 1 is valid).
Base class for SmallStack.
Titem value
Value of current item.
Tindex next
Pool index of next item.
SmallStackItem(const Titem &value, Tindex next)
Create a new item.
SmallStack item that can be kept in a pool.
Tindex branch_count
Number of branches in the tree structure this item is parent of.
std::mutex lock
synchronization for playback status fields
Definition win32_m.cpp:35