1 //  static pooled class
2 //  Copyright (C) 2009 Tim Blechmann
3 //
4 //  This program is free software; you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation; either version 2 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program; see the file COPYING.  If not, write to
16 //  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 //  Boston, MA 02111-1307, USA.
18 
19 #pragma once
20 
21 #include <cstddef>
22 
23 #include <boost/mpl/if.hpp>
24 
25 #include "freelist.hpp"
26 #include "static_pool.hpp"
27 
28 namespace nova {
29 
30 /**
31  * base class for a class, which uses a static memory pool for
32  * memory allocation.
33  *
34  * memory is allocated from a static pool and freed to a lock-free
35  * freelist, which is freed during the memory allocation routine.
36  * this way, the derived class can be allocated and freed from
37  * different threads, while all the access to the pool is done from
38  * the allocating thread.
39  *
40  * \tparam pool_locking specifies the locking policy for the object
41  *         pool (default: nonblocking)
42  *
43  * \tparam recover_count limits the number of objects to be disposed
44  *         from the freelist to avoid (default: dispose all objects
45  *         of freelist)
46  *
47  * \todo   we could allocate one word more and store the size of the
48  *         chunk just before the object. if a disposed object is large
49  *         enough for a request, it wouldn't need to be added to the
50  *         memory pool.
51  *
52  * */
53 template <typename tag, std::size_t pool_size, bool pool_locking = false, unsigned int recover_count = 0>
54 class static_pooled_class {
55 protected:
56     static_pooled_class(void) = default;
57     static_pooled_class(static_pooled_class const& rhs) = default;
58     ~static_pooled_class(void) = default;
59 
60 private:
61     /** free one object from freelist
62      *
63      *  \return true if freelist is empty
64      *
65      * */
free_one_disposed_object(void)66     static inline bool free_one_disposed_object(void) {
67         void* chunk = disposed_objects.pop();
68         if (chunk == nullptr)
69             return true;
70         object_pool.free(chunk);
71         return false;
72     }
73 
74     struct disposing_allocator {
allocatenova::static_pooled_class::disposing_allocator75         static void* allocate(std::size_t size) {
76             free_disposed_objects();
77             return object_pool.malloc(size);
78         }
79     };
80 
81     struct dispose_n_object_allocator {
allocatenova::static_pooled_class::dispose_n_object_allocator82         static void* allocate(std::size_t size) {
83             for (unsigned int i = 0; i != recover_count; ++i) {
84                 bool freelist_empty = free_one_disposed_object();
85                 if (freelist_empty)
86                     break;
87             }
88 
89             for (;;) {
90                 void* ret = object_pool.malloc(size);
91 
92                 if (ret)
93                     return ret;
94                 if (free_one_disposed_object())
95                     return nullptr; /* no object in freelist, we  */
96             }
97         }
98     };
99 
100     typedef
101         typename boost::mpl::if_c<(recover_count > 0), dispose_n_object_allocator, disposing_allocator>::type allocator;
102 
103 public:
allocate(std::size_t size)104     static inline void* allocate(std::size_t size) {
105 #ifndef NOVA_MEMORY_DEBUGGING
106         size = std::max(2 * sizeof(void*), size); /* size requirement for lockfree freelist */
107         return allocator::allocate(size);
108 #else
109         return malloc(size);
110 #endif
111     }
112 
operator new(std::size_t size)113     inline void* operator new(std::size_t size) { return allocate(size); }
114 
free_disposed_objects(void)115     static inline void free_disposed_objects(void) {
116         for (;;) {
117             if (free_one_disposed_object())
118                 return;
119         }
120     }
121 
deallocate(void * p)122     static inline void deallocate(void* p) {
123 #ifndef NOVA_MEMORY_DEBUGGING
124         disposed_objects.push(p);
125 #else
126         free(p);
127 #endif
128     }
129 
operator delete(void * p)130     inline void operator delete(void* p) { deallocate(p); }
131 
132     typedef static_pool<pool_size, pool_locking> object_pool_type;
133 
134     static object_pool_type object_pool;
135     static freelist disposed_objects;
136 };
137 
138 template <typename tag, std::size_t pool_size, bool pool_locking, unsigned int recover_count>
139 typename static_pooled_class<tag, pool_size, pool_locking, recover_count>::object_pool_type
140     static_pooled_class<tag, pool_size, pool_locking, recover_count>::object_pool(true);
141 
142 template <typename tag, std::size_t pool_size, bool pool_locking, unsigned int recover_count>
143 freelist static_pooled_class<tag, pool_size, pool_locking, recover_count>::disposed_objects;
144 
145 
146 } /* namespace nova */
147