Dynamic_Cached_Allocator_With_Overflow_T.h

Go to the documentation of this file.
00001 /*
00002  *
00003  *
00004  * Distributed under the OpenDDS License.
00005  * See: http://www.opendds.org/license.html
00006  */
00007 
00008 #ifndef DYNAMIC_CACHED_ALLOCATOR_WITH_OVERFLOW_T_H
00009 #define DYNAMIC_CACHED_ALLOCATOR_WITH_OVERFLOW_T_H
00010 
00011 #include "debug.h"
00012 #include "ace/Malloc_Allocator.h"
00013 #include "ace/Malloc_T.h"
00014 #include "ace/Free_List.h"
00015 #include "ace/Guard_T.h"
00016 
00017 #include "PoolAllocationBase.h"
00018 
00019 #if !defined (ACE_LACKS_PRAGMA_ONCE)
00020 # pragma once
00021 #endif /* ACE_LACKS_PRAGMA_ONCE */
00022 
00023 namespace OpenDDS {
00024 namespace DCPS {
00025 
00026 /**
00027 * @class Dynamic_Cached_Allocator_With_Overflow
00028 *
00029 * @brief A size-based allocator that caches blocks for quicker access,
00030 *        but if the pool is exhausted it will use the heap.
00031 *
00032 * This class enables caching of dynamically allocated,
00033 * fixed-size chunks.  Notice that the <code>chunk_size</code>
00034 * must be greater than or equal to <code> sizeof (void*) </code> for
00035 * this to work properly.
00036 *
00037 * This class can be configured flexibly with different types of
00038 * ACE_LOCK strategies that support the @a ACE_Thread_Mutex and @a
00039 * ACE_Process_Mutex constructor API.
00040 */
00041 template <class ACE_LOCK>
00042 class Dynamic_Cached_Allocator_With_Overflow : public ACE_New_Allocator, public PoolAllocationBase {
00043 public:
00044   /// Create a cached memory pool with @a n_chunks chunks
00045   /// each with @a chunk_size size.
00046   Dynamic_Cached_Allocator_With_Overflow(size_t n_chunks, size_t chunk_size)
00047   : allocs_from_heap_(0),
00048     allocs_from_pool_(0),
00049     frees_to_heap_(0),
00050     frees_to_pool_(0),
00051     free_list_(ACE_PURE_FREE_LIST)
00052   {
00053     chunk_size_ = ACE_MALLOC_ROUNDUP(chunk_size, ACE_MALLOC_ALIGN);
00054     begin_ = static_cast<unsigned char*> (ACE_Allocator::instance()->malloc(n_chunks * chunk_size_));
00055     // Remember end of the pool.
00056     end_ = begin_ + n_chunks * chunk_size_;
00057 
00058     // Put into free list using placement contructor, no real memory
00059     // allocation in the <new> below.
00060     for (size_t c = 0;
00061          c < n_chunks;
00062          c++) {
00063       void* placement = begin_ + c * chunk_size_;
00064 
00065       this->free_list_.add(new(placement) ACE_Cached_Mem_Pool_Node<char>);
00066     }
00067   }
00068 
00069   /// Clear things up.
00070   ~Dynamic_Cached_Allocator_With_Overflow() {
00071     ACE_Allocator::instance()->free(begin_);
00072     begin_ = 0;
00073     chunk_size_ = 0;
00074   }
00075 
00076   /**
00077   * Get a chunk of memory from free list cache.  Note that @a nbytes is
00078   * only checked to make sure that it's less or equal to @a chunk_size,
00079   * and is otherwise ignored since malloc() always returns a pointer to an
00080   * item of @a chunk_size size.
00081   */
00082   void *malloc(size_t nbytes = 0) {
00083     // Check if size requested fits within pre-determined size.
00084     if (nbytes > chunk_size_)
00085       return 0;
00086 
00087     // addr() call is really not absolutely necessary because of the way
00088     // ACE_Cached_Mem_Pool_Node's internal structure arranged.
00089     void* rtn = this->free_list_.remove()->addr();
00090 
00091     if (0 == rtn) {
00092       rtn = ACE_Allocator::instance()->malloc(chunk_size_);
00093       allocs_from_heap_++;
00094 
00095       if (DCPS_debug_level >= 2) {
00096         if (allocs_from_heap_ == 1 && DCPS_debug_level >= 2)
00097           ACE_DEBUG((LM_DEBUG,
00098                      "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::malloc %x"
00099                      " %d heap allocs with %d outstanding\n",
00100                      this, this->allocs_from_heap_.value(),
00101                      this->allocs_from_heap_.value() - this->frees_to_heap_.value()));
00102 
00103         if (DCPS_debug_level >= 6)
00104           if (allocs_from_heap_.value() % 500 == 0)
00105             ACE_DEBUG((LM_DEBUG,
00106                        "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::malloc %x"
00107                        " %d heap allocs with %d outstanding\n",
00108                        this, this->allocs_from_heap_.value(),
00109                        this->allocs_from_heap_.value() - this->frees_to_heap_.value()));
00110       }
00111 
00112     } else {
00113       allocs_from_pool_++;
00114 
00115       if (DCPS_debug_level >= 6)
00116         if (allocs_from_pool_.value() % 500 == 0)
00117           ACE_DEBUG((LM_DEBUG,
00118                      "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::malloc %x"
00119                      " %d pool allocs %d pool free with %d available\n",
00120                      this, this->allocs_from_pool_.value(),
00121                      this->frees_to_pool_.value(),
00122                      this->available()));
00123     }
00124 
00125     return rtn;
00126   }
00127 
00128   /**
00129   * Get a chunk of memory from free list cache, giving them
00130   * @a initial_value.  Note that @a nbytes is only checked to make sure
00131   * that it's less or equal to @a chunk_size, and is otherwise ignored
00132   * since calloc() always returns a pointer to an item of @a chunk_size.
00133   */
00134   virtual void *calloc(size_t /* nbytes */,
00135                        char /* initial_value */ = '\0') {
00136     ACE_NOTSUP_RETURN(0);
00137   }
00138 
00139   /// This method is a no-op and just returns 0 since the free list
00140   /// only works with fixed sized entities.
00141   virtual void *calloc(size_t /* n_elem */,
00142                        size_t /* elem_size */,
00143                        char /* initial_value */ = '\0') {
00144     ACE_NOTSUP_RETURN(0);
00145   }
00146 
00147   /// Return a chunk of memory back to free list cache.
00148   void free(void * ptr) {
00149     unsigned char* tmp = static_cast<unsigned char*> (ptr);
00150     if (tmp < begin_ ||
00151         tmp >= end_) {
00152       ACE_Allocator::instance()->free(tmp);
00153       frees_to_heap_ ++;
00154 
00155       if (frees_to_heap_.value() > allocs_from_heap_.value()) {
00156         ACE_ERROR((LM_ERROR,
00157                    "(%P|%t) ERROR: Dynamic_Cached_Allocator_With_Overflow::free %x"
00158                    " more deletes %d than allocs %d to the heap\n",
00159                    this,
00160                    this->frees_to_heap_.value(),
00161                    this->allocs_from_heap_.value()));
00162       }
00163 
00164       if (DCPS_debug_level >= 6) {
00165         if (frees_to_heap_.value() % 500 == 0) {
00166           ACE_DEBUG((LM_DEBUG,
00167                      "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::free %x"
00168                      " %d heap allocs with %d oustanding\n",
00169                      this, this->allocs_from_heap_.value(),
00170                      this->allocs_from_heap_.value() - this->frees_to_heap_.value()));
00171         }
00172       }
00173 
00174       return;
00175 
00176     } else if (ptr != 0) {
00177       this->frees_to_pool_ ++;
00178 
00179       if (frees_to_pool_.value() > allocs_from_pool_.value()) {
00180         ACE_ERROR((LM_ERROR,
00181                    "(%P|%t) ERROR: Dynamic_Cached_Allocator_With_Overflow::free %x"
00182                    " more deletes %d than allocs %d from the pool\n",
00183                    this,
00184                    this->frees_to_pool_.value(),
00185                    this->allocs_from_pool_.value()));
00186       }
00187 
00188       this->free_list_.add((ACE_Cached_Mem_Pool_Node<char> *) ptr) ;
00189 
00190       if (DCPS_debug_level >= 6)
00191         if (this->available() % 500 == 0)
00192           ACE_DEBUG((LM_DEBUG,
00193                      "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::malloc %x"
00194                      " %d pool allocs %d pool frees with %d available\n",
00195                      this, this->allocs_from_pool_.value(), this->frees_to_pool_.value(),
00196                      this->available()));
00197     }
00198   }
00199 
00200   /// Return the number of chunks available in the cache.
00201   size_t pool_depth() {
00202     return this->free_list_.size() ;
00203   }
00204 
00205   // -- for debug
00206 
00207   /** How many chunks are available at this time.
00208   */
00209   size_t available() {
00210     return free_list_.size();
00211   };
00212 
00213   /// number of allocations from the heap.
00214   ACE_Atomic_Op<ACE_Thread_Mutex, unsigned long> allocs_from_heap_;
00215   /// number of allocations from the pool.
00216   ACE_Atomic_Op<ACE_Thread_Mutex, unsigned long> allocs_from_pool_;
00217   /// number of frees returned to the heap
00218   ACE_Atomic_Op<ACE_Thread_Mutex, unsigned long> frees_to_heap_ ;
00219   /// number of frees returned to the pool
00220   ACE_Atomic_Op<ACE_Thread_Mutex, unsigned long> frees_to_pool_;
00221 private:
00222   /// Remember how we allocate the memory in the first place so
00223   /// we can clear things up later.
00224   unsigned char* begin_;
00225   /// The end of the pool.
00226   unsigned char* end_;
00227 
00228   /// Maintain a cached memory free list. We use @c char as template
00229   /// parameter, although sizeof(char) is usually less than
00230   /// sizeof(void*). Really important is that @a chunk_size
00231   /// must be greater or equal to sizeof(void*).
00232   ACE_Locked_Free_List<ACE_Cached_Mem_Pool_Node<char>, ACE_LOCK> free_list_;
00233 
00234   /// Remember the size of our chunks.
00235   size_t chunk_size_;
00236 };
00237 
00238 } // namespace DCPS
00239 } // namespace OpenDDS
00240 
00241 #endif /* DYNAMIC_CACHED_ALLOCATOR_WITH_OVERFLOW_T_H */

Generated on Fri Feb 12 20:05:23 2016 for OpenDDS by  doxygen 1.4.7