/* * This file is part of OpenTTD. * 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. * 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. * 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 . */ /** @file smallstack_type.hpp Minimal stack that uses a pool to avoid pointers and doesn't allocate any heap memory if there is only one valid item. */ #ifndef SMALLSTACK_TYPE_HPP #define SMALLSTACK_TYPE_HPP #include /** * A simplified pool which stores values instead of pointers and doesn't * redefine operator new/delete. It also never zeroes memory and always reuses * it. */ template class SimplePool { public: inline SimplePool() : first_unused(0), first_free(0) {} /** * Get the mutex. We don't lock the mutex in the pool methods as the * SmallStack isn't necessarily in a consistent state after each method. * @return Mutex. */ inline std::mutex &GetMutex() { return this->mutex; } /** * Get the item at position index. * @return Item at index. */ inline Titem &Get(Tindex index) { return this->data[index]; } /** * Create a new item and return its index. * @return Index of new item. */ inline Tindex Create() { Tindex index = this->FindFirstFree(); if (index < Tmax_size) { this->data[index].valid = true; this->first_free = index + 1; this->first_unused = std::max(this->first_unused, this->first_free); } return index; } /** * Destroy (or rather invalidate) the item at the given index. * @param index Index of item to be destroyed. */ inline void Destroy(Tindex index) { this->data[index].valid = false; this->first_free = std::min(this->first_free, index); } private: inline Tindex FindFirstFree() { Tindex index = this->first_free; for (; index < this->first_unused; index++) { if (!this->data[index].valid) return index; } if (index >= this->data.size() && index < Tmax_size) { this->data.resize(index + 1); } return index; } struct SimplePoolPoolItem : public Titem { bool valid; }; Tindex first_unused; Tindex first_free; std::mutex mutex; std::vector data; }; /** * Base class for SmallStack. We cannot add this into SmallStack itself as * certain compilers don't like it. */ template struct SmallStackItem { Tindex next; ///< Pool index of next item. Titem value; ///< Value of current item. /** * Create a new item. * @param value Value of the item. * @param next Next item in the stack. */ inline SmallStackItem(const Titem &value, Tindex next) : next(next), value(value) {} SmallStackItem() = default; }; /** * Minimal stack that uses a pool to avoid pointers. It has some peculiar * properties that make it useful for passing around lists of IDs but not much * else: * 1. It always includes an invalid item as bottom. * 2. It doesn't have a deep copy operation but uses smart pointers instead. * Every copy is thus implicitly shared. * 3. Its items are immutable. * 4. Due to 2. and 3. memory management can be done by "branch counting". * Whenever you copy a smallstack, the first item on the heap increases its * branch_count, signifying that there are multiple items "in front" of it. * When deleting a stack items are deleted up to the point where * branch_count > 0. * 5. You can choose your own index type, so that you can align it with your * value type. E.G. value types of 16 bits length like to be combined with * index types of the same length. * 6. All accesses to the underlying pool are guarded by a mutex and atomic in * the sense that the mutex stays locked until the pool has reacquired a * consistent state. This means that even though a common data structure is * used the SmallStack is still reentrant. * @tparam Titem Value type to be used. * @tparam Tindex Index type to use for the pool. * @tparam Tinvalid Invalid item to keep at the bottom of each stack. * @tparam Tgrowth_step Growth step for pool. * @tparam Tmax_size Maximum size for pool. */ template class SmallStack : public SmallStackItem { public: typedef SmallStackItem Item; /** * SmallStack item that can be kept in a pool. */ struct PooledSmallStack : public Item { Tindex branch_count; ///< Number of branches in the tree structure this item is parent of }; typedef SimplePool SmallStackPool; /** * Constructor for a stack with one or two items in it. * @param value Initial item. If not missing or Tinvalid there will be Tinvalid below it. */ inline SmallStack(const Titem &value = Tinvalid) : Item(value, Tmax_size) {} /** * Remove the head of stack and all other items members that are unique to it. */ inline ~SmallStack() { /* Pop() locks the mutex and after each pop the pool is consistent.*/ while (this->next != Tmax_size) this->Pop(); } /** * Shallow copy the stack, marking the first item as branched. * @param other Stack to copy from */ inline SmallStack(const SmallStack &other) : Item(other) { this->Branch(); } /** * Shallow copy the stack, marking the first item as branched. * @param other Stack to copy from * @return This smallstack. */ inline SmallStack &operator=(const SmallStack &other) { if (this == &other) return *this; while (this->next != Tmax_size) this->Pop(); this->next = other.next; this->value = other.value; /* Deleting and branching are independent operations, so it's fine to * acquire separate locks for them. */ this->Branch(); return *this; } /** * Pushes a new item onto the stack if there is still space in the * underlying pool. Otherwise the topmost item's value gets overwritten. * @param item Item to be pushed. */ inline void Push(const Titem &item) { if (this->value != Tinvalid) { std::lock_guard lock(SmallStack::GetPool().GetMutex()); Tindex new_item = SmallStack::GetPool().Create(); if (new_item != Tmax_size) { PooledSmallStack &pushed = SmallStack::GetPool().Get(new_item); pushed.value = this->value; pushed.next = this->next; pushed.branch_count = 0; this->next = new_item; } } this->value = item; } /** * Pop an item from the stack. * @return Current top of stack. */ inline Titem Pop() { Titem ret = this->value; if (this->next == Tmax_size) { this->value = Tinvalid; } else { std::lock_guard lock(SmallStack::GetPool().GetMutex()); PooledSmallStack &popped = SmallStack::GetPool().Get(this->next); this->value = popped.value; if (popped.branch_count == 0) { SmallStack::GetPool().Destroy(this->next); } else { --popped.branch_count; /* We can't use Branch() here as we already have the mutex.*/ if (popped.next != Tmax_size) { ++(SmallStack::GetPool().Get(popped.next).branch_count); } } /* Accessing popped here is no problem as the pool will only set * the validity flag, not actually delete the item, on Destroy(). * It's impossible for another thread to acquire the same item in * the mean time because of the mutex. */ this->next = popped.next; } return ret; } /** * Check if the stack is empty. * @return If the stack is empty. */ inline bool IsEmpty() const { return this->value == Tinvalid && this->next == Tmax_size; } /** * Check if the given item is contained in the stack. * @param item Item to look for. * @return If the item is in the stack. */ inline bool Contains(const Titem &item) const { if (item == Tinvalid || item == this->value) return true; if (this->next != Tmax_size) { std::lock_guard lock(SmallStack::GetPool().GetMutex()); const SmallStack *in_list = this; do { in_list = static_cast( static_cast(&SmallStack::GetPool().Get(in_list->next))); if (in_list->value == item) return true; } while (in_list->next != Tmax_size); } return false; } protected: static SmallStackPool &GetPool() { static SmallStackPool pool; return pool; } /** * Create a branch in the pool if necessary. */ inline void Branch() { if (this->next != Tmax_size) { std::lock_guard lock(SmallStack::GetPool().GetMutex()); ++(SmallStack::GetPool().Get(this->next).branch_count); } } }; #endif