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