OpenTTD Source  20241108-master-g80f628063a
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 
20 template<typename Titem, typename Tindex, Tindex Tgrowth_step, Tindex Tmax_size>
21 class SimplePool {
22 public:
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 
63 private:
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 
93 template <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 
134 template <typename Titem, typename Tindex, Titem Tinvalid, Tindex Tgrowth_step, Tindex Tmax_size>
135 class SmallStack : public SmallStackItem<Titem, Tindex> {
136 public:
137 
139 
143  struct PooledSmallStack : public Item {
144  Tindex branch_count;
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 
268 protected:
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.
Titem & Get(Tindex index)
Get the item at position index.
std::mutex & GetMutex()
Get the mutex.
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.
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 & operator=(const SmallStack &other)
Shallow copy the stack, marking the first item as branched.
~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