OpenDDS  Snapshot(2023/04/28-20:55)
Dynamic_Cached_Allocator_With_Overflow_T.h
Go to the documentation of this file.
1 /*
2  *
3  *
4  * Distributed under the OpenDDS License.
5  * See: http://www.opendds.org/license.html
6  */
7 
8 #ifndef OPENDDS_DCPS_DYNAMIC_CACHED_ALLOCATOR_WITH_OVERFLOW_T_H
9 #define OPENDDS_DCPS_DYNAMIC_CACHED_ALLOCATOR_WITH_OVERFLOW_T_H
10 
11 #include "debug.h"
12 #include "Atomic.h"
13 #include "PoolAllocationBase.h"
14 
15 #include <ace/Free_List.h>
16 #include <ace/Guard_T.h>
17 #include <ace/Malloc_Allocator.h>
18 #include <ace/Malloc_T.h>
19 
20 #if !defined (ACE_LACKS_PRAGMA_ONCE)
21 # pragma once
22 #endif /* ACE_LACKS_PRAGMA_ONCE */
23 
25 
26 namespace OpenDDS {
27 namespace DCPS {
28 
29 /**
30 * @class Dynamic_Cached_Allocator_With_Overflow
31 *
32 * @brief A size-based allocator that caches blocks for quicker access,
33 * but if the pool is exhausted it will use the heap.
34 *
35 * This class enables caching of dynamically allocated,
36 * fixed-size chunks. Notice that the <code>chunk_size</code>
37 * must be greater than or equal to <code> sizeof (void*) </code> for
38 * this to work properly.
39 *
40 * This class can be configured flexibly with different types of
41 * ACE_LOCK strategies that support the @a ACE_Thread_Mutex and @a
42 * ACE_Process_Mutex constructor API.
43 */
44 template <class ACE_LOCK>
46 public:
47  /// Create a cached memory pool with @a n_chunks chunks
48  /// each with @a chunk_size size.
49  Dynamic_Cached_Allocator_With_Overflow(size_t n_chunks, size_t chunk_size)
50  : allocs_from_heap_(0),
52  frees_to_heap_(0),
53  frees_to_pool_(0),
55  {
57  begin_ = static_cast<unsigned char*> (ACE_Allocator::instance()->malloc(n_chunks * chunk_size_));
58  // Remember end of the pool.
59  end_ = begin_ + n_chunks * chunk_size_;
60 
61  // Put into free list using placement contructor, no real memory
62  // allocation in the <new> below.
63  for (size_t c = 0;
64  c < n_chunks;
65  c++) {
66  void* placement = begin_ + c * chunk_size_;
67 
69  }
70  }
71 
72  /// Clear things up.
75  begin_ = 0;
76  chunk_size_ = 0;
77  }
78 
79  /**
80  * Get a chunk of memory from free list cache. Note that @a nbytes is
81  * only checked to make sure that it's less or equal to @a chunk_size,
82  * and is otherwise ignored since malloc() always returns a pointer to an
83  * item of @a chunk_size size.
84  */
85  void *malloc(size_t nbytes = 0) {
86  // Check if size requested fits within pre-determined size.
87  if (nbytes > chunk_size_)
88  return 0;
89 
90  // addr() call is really not absolutely necessary because of the way
91  // ACE_Cached_Mem_Pool_Node's internal structure arranged.
92  void* rtn = free_list_.remove()->addr();
93 
94  if (0 == rtn) {
97 
98  if (DCPS_debug_level >= 2) {
99  if (allocs_from_heap_ == 1 && DCPS_debug_level >= 2)
101  "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::malloc %x"
102  " %d heap allocs with %d outstanding\n",
103  this, allocs_from_heap_.load(),
105 
106  if (DCPS_debug_level >= 6)
107  if (allocs_from_heap_ % 500 == 0)
109  "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::malloc %@"
110  " %Lu heap allocs with %Lu outstanding\n",
111  this, allocs_from_heap_.load(),
113  }
114 
115  } else {
117 
118  if (DCPS_debug_level >= 6)
119  if (allocs_from_pool_ % 500 == 0)
121  "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::malloc %x"
122  " %d pool allocs %d pool free with %d available\n",
123  this, allocs_from_pool_.load(),
125  available()));
126  }
127 
128  return rtn;
129  }
130 
131  /**
132  * Get a chunk of memory from free list cache, giving them
133  * @a initial_value. Note that @a nbytes is only checked to make sure
134  * that it's less or equal to @a chunk_size, and is otherwise ignored
135  * since calloc() always returns a pointer to an item of @a chunk_size.
136  */
137  virtual void *calloc(size_t /* nbytes */,
138  char /* initial_value */ = '\0') {
140  }
141 
142  /// This method is a no-op and just returns 0 since the free list
143  /// only works with fixed sized entities.
144  virtual void *calloc(size_t /* n_elem */,
145  size_t /* elem_size */,
146  char /* initial_value */ = '\0') {
148  }
149 
150  /// Return a chunk of memory back to free list cache.
151  void free(void * ptr) {
152  unsigned char* tmp = static_cast<unsigned char*> (ptr);
153  if (tmp < begin_ ||
154  tmp >= end_) {
156  frees_to_heap_ ++;
157 
160  "(%P|%t) ERROR: Dynamic_Cached_Allocator_With_Overflow::free %x"
161  " more deletes %d than allocs %d to the heap\n",
162  this,
165  }
166 
167  if (DCPS_debug_level >= 6) {
168  if (frees_to_heap_ % 500 == 0) {
170  "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::free %@"
171  " %Lu heap allocs with %Lu outstanding\n",
172  this, allocs_from_heap_.load(),
174  }
175  }
176 
177  return;
178 
179  } else if (ptr != 0) {
180  frees_to_pool_ ++;
181 
184  "(%P|%t) ERROR: Dynamic_Cached_Allocator_With_Overflow::free %x"
185  " more deletes %d than allocs %d from the pool\n",
186  this,
189  }
190 
192 
193  if (DCPS_debug_level >= 6)
194  if (available() % 500 == 0)
196  "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::malloc %x"
197  " %d pool allocs %d pool frees with %d available\n",
199  available()));
200  }
201  }
202 
203  /// Return the number of chunks available in the cache.
204  size_t pool_depth() {
205  return free_list_.size() ;
206  }
207 
208  // -- for debug
209 
210  /** How many chunks are available at this time.
211  */
212  size_t available() {
213  return free_list_.size();
214  };
215 
216  /// number of allocations from the heap.
218  /// number of allocations from the pool.
220  /// number of frees returned to the heap
222  /// number of frees returned to the pool
224 private:
225  /// Remember how we allocate the memory in the first place so
226  /// we can clear things up later.
227  unsigned char* begin_;
228  /// The end of the pool.
229  unsigned char* end_;
230 
231  /// Maintain a cached memory free list. We use @c char as template
232  /// parameter, although sizeof(char) is usually less than
233  /// sizeof(void*). Really important is that @a chunk_size
234  /// must be greater or equal to sizeof(void*).
236 
237  /// Remember the size of our chunks.
238  size_t chunk_size_;
239 };
240 
241 } // namespace DCPS
242 } // namespace OpenDDS
243 
245 
246 #endif /* DYNAMIC_CACHED_ALLOCATOR_WITH_OVERFLOW_T_H */
#define ACE_MALLOC_ALIGN
#define ACE_DEBUG(X)
#define ACE_ERROR(X)
virtual ACE_Cached_Mem_Pool_Node< char > * remove(void)
#define ACE_MALLOC_ROUNDUP(X, Y)
virtual void free(void *ptr)=0
size_t pool_depth()
Return the number of chunks available in the cache.
Atomic< unsigned long > allocs_from_heap_
number of allocations from the heap.
ACE_Locked_Free_List< ACE_Cached_Mem_Pool_Node< char >, ACE_LOCK > free_list_
LM_DEBUG
Atomic< unsigned long > frees_to_heap_
number of frees returned to the heap
static ACE_Allocator * instance(void)
virtual void add(ACE_Cached_Mem_Pool_Node< char > *element)
A size-based allocator that caches blocks for quicker access, but if the pool is exhausted it will us...
void free(void *ptr)
Return a chunk of memory back to free list cache.
OpenDDS_Dcps_Export unsigned int DCPS_debug_level
Definition: debug.cpp:30
Atomic< unsigned long > allocs_from_pool_
number of allocations from the pool.
#define OPENDDS_END_VERSIONED_NAMESPACE_DECL
T load() const
Definition: Atomic.h:33
LM_ERROR
Atomic< unsigned long > frees_to_pool_
number of frees returned to the pool
The Internal API and Implementation of OpenDDS.
Definition: AddressCache.h:28
#define ACE_NOTSUP_RETURN(FAILVALUE)
#define ACE_PURE_FREE_LIST
virtual void * malloc(size_type nbytes)=0