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

Generated on 10 Aug 2018 for OpenDDS by  doxygen 1.6.1