smallstack_type.hpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * 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.
00006  * 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.
00007  * 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/>.
00008  */
00009 
00012 #ifndef SMALLSTACK_TYPE_HPP
00013 #define SMALLSTACK_TYPE_HPP
00014 
00015 #include "smallvec_type.hpp"
00016 #include "../thread/thread.h"
00017 
00023 template<typename Titem, typename Tindex, Tindex Tgrowth_step, Tindex Tmax_size>
00024 class SimplePool {
00025 public:
00026   inline SimplePool() : first_unused(0), first_free(0), mutex(ThreadMutex::New()) {}
00027   inline ~SimplePool() { delete this->mutex; }
00028 
00034   inline ThreadMutex *GetMutex() { return this->mutex; }
00035 
00040   inline Titem &Get(Tindex index) { return this->data[index]; }
00041 
00046   inline Tindex Create()
00047   {
00048     Tindex index = this->FindFirstFree();
00049     if (index < Tmax_size) {
00050       this->data[index].valid = true;
00051       this->first_free = index + 1;
00052       this->first_unused = max(this->first_unused, this->first_free);
00053     }
00054     return index;
00055   }
00056 
00061   inline void Destroy(Tindex index)
00062   {
00063     this->data[index].valid = false;
00064     this->first_free = min(this->first_free, index);
00065   }
00066 
00067 private:
00068 
00069   inline Tindex FindFirstFree()
00070   {
00071     Tindex index = this->first_free;
00072     for (; index < this->first_unused; index++) {
00073       if (!this->data[index].valid) return index;
00074     }
00075 
00076     if (index >= this->data.Length() && index < Tmax_size) {
00077       this->data.Resize(index + 1);
00078     }
00079     return index;
00080   }
00081 
00082   struct SimplePoolPoolItem : public Titem {
00083     bool valid;
00084   };
00085 
00086   Tindex first_unused;
00087   Tindex first_free;
00088 
00089   ThreadMutex *mutex;
00090   SmallVector<SimplePoolPoolItem, Tgrowth_step> data;
00091 };
00092 
00097 template <typename Titem, typename Tindex>
00098 struct SmallStackItem {
00099   Tindex next; 
00100   Titem value; 
00101 
00107   inline SmallStackItem(const Titem &value, Tindex next) :
00108     next(next), value(value) {}
00109 };
00110 
00137 template <typename Titem, typename Tindex, Titem Tinvalid, Tindex Tgrowth_step, Tindex Tmax_size>
00138 class SmallStack : public SmallStackItem<Titem, Tindex> {
00139 public:
00140 
00141   typedef SmallStackItem<Titem, Tindex> Item;
00142 
00146   struct PooledSmallStack : public Item {
00147     Tindex branch_count; 
00148   };
00149 
00150   typedef SimplePool<PooledSmallStack, Tindex, Tgrowth_step, Tmax_size> SmallStackPool;
00151 
00156   inline SmallStack(const Titem &value = Tinvalid) : Item(value, Tmax_size) {}
00157 
00161   inline ~SmallStack()
00162   {
00163     /* Pop() locks the mutex and after each pop the pool is consistent.*/
00164     while (this->next != Tmax_size) this->Pop();
00165   }
00166 
00171   inline SmallStack(const SmallStack &other) : Item(other) { this->Branch(); }
00172 
00178   inline SmallStack &operator=(const SmallStack &other)
00179   {
00180     if (this == &other) return *this;
00181     while (this->next != Tmax_size) this->Pop();
00182     this->next = other.next;
00183     this->value = other.value;
00184     /* Deleting and branching are independent operations, so it's fine to
00185      * acquire separate locks for them. */
00186     this->Branch();
00187     return *this;
00188   }
00189 
00195   inline void Push(const Titem &item)
00196   {
00197     if (this->value != Tinvalid) {
00198       ThreadMutexLocker lock(_pool.GetMutex());
00199       Tindex new_item = _pool.Create();
00200       if (new_item != Tmax_size) {
00201         PooledSmallStack &pushed = _pool.Get(new_item);
00202         pushed.value = this->value;
00203         pushed.next = this->next;
00204         pushed.branch_count = 0;
00205         this->next = new_item;
00206       }
00207     }
00208     this->value = item;
00209   }
00210 
00215   inline Titem Pop()
00216   {
00217     Titem ret = this->value;
00218     if (this->next == Tmax_size) {
00219       this->value = Tinvalid;
00220     } else {
00221       ThreadMutexLocker lock(_pool.GetMutex());
00222       PooledSmallStack &popped = _pool.Get(this->next);
00223       this->value = popped.value;
00224       if (popped.branch_count == 0) {
00225         _pool.Destroy(this->next);
00226       } else {
00227         --popped.branch_count;
00228         /* We can't use Branch() here as we already have the mutex.*/
00229         if (popped.next != Tmax_size) {
00230           ++(_pool.Get(popped.next).branch_count);
00231         }
00232       }
00233       /* Accessing popped here is no problem as the pool will only set
00234        * the validity flag, not actually delete the item, on Destroy().
00235        * It's impossible for another thread to acquire the same item in
00236        * the mean time because of the mutex. */
00237       this->next = popped.next;
00238     }
00239     return ret;
00240   }
00241 
00246   inline bool IsEmpty() const
00247   {
00248     return this->value == Tinvalid && this->next == Tmax_size;
00249   }
00250 
00256   inline bool Contains(const Titem &item) const
00257   {
00258     if (item == Tinvalid || item == this->value) return true;
00259     if (this->next != Tmax_size) {
00260       ThreadMutexLocker lock(_pool.GetMutex());
00261       const SmallStack *in_list = this;
00262       do {
00263         in_list = static_cast<const SmallStack *>(
00264             static_cast<const Item *>(&_pool.Get(in_list->next)));
00265         if (in_list->value == item) return true;
00266       } while (in_list->next != Tmax_size);
00267     }
00268     return false;
00269   }
00270 
00271 protected:
00272   static SmallStackPool _pool;
00273 
00277   inline void Branch()
00278   {
00279     if (this->next != Tmax_size) {
00280       ThreadMutexLocker lock(_pool.GetMutex());
00281       ++(_pool.Get(this->next).branch_count);
00282     }
00283   }
00284 };
00285 
00286 #endif