1 /* Copyright (C) 2013 Codership Oy <info@codership.com> */ 2 /** 3 * @file Self-adjusting pool of same size memory buffers. 4 * 5 * How it works: pool is never allowed to keep more than half of total 6 * allocated buffers (plus min_count), so at least half of buffers must be 7 * in use. As more than half goes out of use they will be deallocated rather 8 * than placed back in the pool. 9 * 10 * $Id$ 11 */ 12 13 #ifndef _GU_MEM_POOL_HPP_ 14 #define _GU_MEM_POOL_HPP_ 15 16 #include "gu_lock.hpp" 17 #include "gu_macros.hpp" 18 19 #include <assert.h> 20 21 #include <vector> 22 #include <ostream> 23 24 namespace gu 25 { 26 typedef std::vector<void*> MemPoolVector; 27 28 /* Since we specialize this template iwth thread_safe=true parameter below, 29 * this makes it implicit thread_safe=false specialization. */ 30 template <bool thread_safe> 31 class MemPool 32 { 33 public: 34 35 explicit MemPool(int buf_size,int reserve=0,const char * name="")36 MemPool(int buf_size, int reserve = 0, const char* name = "") 37 : pool_ (), 38 hits_ (0), 39 misses_ (0), 40 allocd_ (0), 41 name_ (name), 42 buf_size_ (buf_size), 43 reserve_ (reserve) 44 { 45 assert(buf_size_ > 0); 46 assert(reserve >= 0); 47 pool_.reserve(reserve_); 48 } 49 ~MemPool()50 ~MemPool() 51 { 52 /* all buffers must be returned to pool before destruction */ 53 assert(pool_.size() == allocd_); 54 55 for (size_t i(0); i < pool_.size(); ++i) 56 { 57 assert(pool_[i]); 58 free(pool_[i]); 59 } 60 } 61 acquire()62 void* acquire() 63 { 64 void* ret(from_pool()); 65 66 if (!ret) ret = alloc(); 67 68 return ret; 69 } 70 recycle(void * buf)71 void recycle(void* buf) 72 { 73 if (!to_pool(buf)) free(buf); 74 } 75 print(std::ostream & os) const76 void print(std::ostream& os) const 77 { 78 double hr(hits_); 79 80 if (hr > 0) 81 { 82 assert(misses_ > 0); 83 hr /= hits_ + misses_; 84 } 85 86 os << "MemPool(" << name_ 87 << "): hit ratio: " << hr 88 << ", misses: " << misses_ 89 << ", in use: " << allocd_ - pool_.size() 90 << ", in pool: " << pool_.size(); 91 } 92 buf_size() const93 size_t buf_size() const { return buf_size_; } 94 95 protected: 96 97 /* from_pool() and to_pool() will need to be called under mutex 98 * in thread-safe version, so all object data are modified there. 99 * alloc() and free() then can be called outside critical section. */ from_pool()100 void* from_pool() 101 { 102 void* ret(NULL); 103 104 if (pool_.size() > 0) 105 { 106 ret = pool_.back(); 107 assert(ret); 108 pool_.pop_back(); 109 ++hits_; 110 } 111 else 112 { 113 ++allocd_; 114 ++misses_; 115 } 116 117 return ret; 118 } 119 120 // returns false if buffer can't be returned to pool to_pool(void * buf)121 bool to_pool(void* buf) 122 { 123 assert(buf); 124 125 bool const ret(reserve_ + allocd_/2 > pool_.size()); 126 127 if (ret) 128 { 129 pool_.push_back(buf); 130 } 131 else 132 { 133 assert(allocd_ > 0); 134 --allocd_; 135 } 136 137 return ret; 138 } 139 alloc()140 void* alloc() 141 { 142 return (operator new(buf_size_)); 143 } 144 free(void * const buf)145 void free(void* const buf) 146 { 147 assert(buf); 148 operator delete(buf); 149 } 150 151 friend class MemPool<true>; 152 153 private: 154 155 MemPoolVector pool_; 156 size_t hits_; 157 size_t misses_; 158 size_t allocd_; 159 const char* const name_; 160 unsigned int const buf_size_; 161 unsigned int const reserve_; 162 163 MemPool (const MemPool&); 164 MemPool operator= (const MemPool&); 165 166 }; /* class MemPool<false>: thread-unsafe */ 167 168 169 /* Thread-safe MemPool specialization. 170 * Even though MemPool<true> technically IS-A MemPool<false>, the need to 171 * overload nearly all public methods and practical uselessness of 172 * polymorphism in this case make inheritance undesirable. */ 173 template <> 174 class MemPool<true> 175 { 176 public: 177 178 explicit MemPool(int buf_size,int reserve=0,const char * name="")179 MemPool(int buf_size, int reserve = 0, const char* name = "") 180 : base_(buf_size, reserve, name), mtx_ () {} 181 ~MemPool()182 ~MemPool() {} 183 acquire()184 void* acquire() 185 { 186 void* ret; 187 188 { 189 Lock lock(mtx_); 190 ret = base_.from_pool(); 191 } 192 193 if (!ret) ret = base_.alloc(); 194 195 return ret; 196 } 197 recycle(void * buf)198 void recycle(void* buf) 199 { 200 bool pooled; 201 202 { 203 Lock lock(mtx_); 204 pooled = base_.to_pool(buf); 205 } 206 207 if (!pooled) base_.free(buf); 208 } 209 print(std::ostream & os) const210 void print(std::ostream& os) const 211 { 212 Lock lock(mtx_); 213 base_.print(os); 214 } 215 buf_size() const216 size_t buf_size() const { return base_.buf_size(); } 217 218 private: 219 220 MemPool<false> base_; 221 Mutex mtx_; 222 223 }; /* class MemPool<true>: thread-safe */ 224 225 template <bool thread_safe> operator <<(std::ostream & os,const MemPool<thread_safe> & mp)226 std::ostream& operator << (std::ostream& os, 227 const MemPool<thread_safe>& mp) 228 { 229 mp.print(os); return os; 230 } 231 232 typedef MemPool<false> MemPoolUnsafe; 233 typedef MemPool<true> MemPoolSafe; 234 235 } /* namespace gu */ 236 237 238 #endif /* _GU_MEM_POOL_HPP_ */ 239