LCOV - code coverage report
Current view: top level - DCPS - Dynamic_Cached_Allocator_With_Overflow_T.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 63 0.0 %
Date: 2023-04-30 01:32:43 Functions: 0 8 0.0 %

          Line data    Source code
       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             : 
      24             : OPENDDS_BEGIN_VERSIONED_NAMESPACE_DECL
      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>
      45             : class Dynamic_Cached_Allocator_With_Overflow : public ACE_New_Allocator, public PoolAllocationBase {
      46             : public:
      47             :   /// Create a cached memory pool with @a n_chunks chunks
      48             :   /// each with @a chunk_size size.
      49           0 :   Dynamic_Cached_Allocator_With_Overflow(size_t n_chunks, size_t chunk_size)
      50           0 :   : allocs_from_heap_(0),
      51           0 :     allocs_from_pool_(0),
      52           0 :     frees_to_heap_(0),
      53           0 :     frees_to_pool_(0),
      54           0 :     free_list_(ACE_PURE_FREE_LIST)
      55             :   {
      56           0 :     chunk_size_ = ACE_MALLOC_ROUNDUP(chunk_size, ACE_MALLOC_ALIGN);
      57           0 :     begin_ = static_cast<unsigned char*> (ACE_Allocator::instance()->malloc(n_chunks * chunk_size_));
      58             :     // Remember end of the pool.
      59           0 :     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           0 :     for (size_t c = 0;
      64           0 :          c < n_chunks;
      65             :          c++) {
      66           0 :       void* placement = begin_ + c * chunk_size_;
      67             : 
      68           0 :       free_list_.add(new(placement) ACE_Cached_Mem_Pool_Node<char>);
      69             :     }
      70           0 :   }
      71             : 
      72             :   /// Clear things up.
      73           0 :   ~Dynamic_Cached_Allocator_With_Overflow() {
      74           0 :     ACE_Allocator::instance()->free(begin_);
      75           0 :     begin_ = 0;
      76           0 :     chunk_size_ = 0;
      77           0 :   }
      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           0 :   void *malloc(size_t nbytes = 0) {
      86             :     // Check if size requested fits within pre-determined size.
      87           0 :     if (nbytes > chunk_size_)
      88           0 :       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           0 :     void* rtn = free_list_.remove()->addr();
      93             : 
      94           0 :     if (0 == rtn) {
      95           0 :       rtn = ACE_Allocator::instance()->malloc(chunk_size_);
      96           0 :       allocs_from_heap_++;
      97             : 
      98           0 :       if (DCPS_debug_level >= 2) {
      99           0 :         if (allocs_from_heap_ == 1 && DCPS_debug_level >= 2)
     100           0 :           ACE_DEBUG((LM_DEBUG,
     101             :                      "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::malloc %x"
     102             :                      " %d heap allocs with %d outstanding\n",
     103             :                      this, allocs_from_heap_.load(),
     104             :                      allocs_from_heap_.load() - frees_to_heap_.load()));
     105             : 
     106           0 :         if (DCPS_debug_level >= 6)
     107           0 :           if (allocs_from_heap_ % 500 == 0)
     108           0 :             ACE_DEBUG((LM_DEBUG,
     109             :                        "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::malloc %@"
     110             :                        " %Lu heap allocs with %Lu outstanding\n",
     111             :                        this, allocs_from_heap_.load(),
     112             :                        allocs_from_heap_.load() - frees_to_heap_.load()));
     113             :       }
     114             : 
     115             :     } else {
     116           0 :       allocs_from_pool_++;
     117             : 
     118           0 :       if (DCPS_debug_level >= 6)
     119           0 :         if (allocs_from_pool_ % 500 == 0)
     120           0 :           ACE_DEBUG((LM_DEBUG,
     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(),
     124             :                      frees_to_pool_.load(),
     125             :                      available()));
     126             :     }
     127             : 
     128           0 :     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           0 :   virtual void *calloc(size_t /* nbytes */,
     138             :                        char /* initial_value */ = '\0') {
     139           0 :     ACE_NOTSUP_RETURN(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           0 :   virtual void *calloc(size_t /* n_elem */,
     145             :                        size_t /* elem_size */,
     146             :                        char /* initial_value */ = '\0') {
     147           0 :     ACE_NOTSUP_RETURN(0);
     148             :   }
     149             : 
     150             :   /// Return a chunk of memory back to free list cache.
     151           0 :   void free(void * ptr) {
     152           0 :     unsigned char* tmp = static_cast<unsigned char*> (ptr);
     153           0 :     if (tmp < begin_ ||
     154           0 :         tmp >= end_) {
     155           0 :       ACE_Allocator::instance()->free(tmp);
     156           0 :       frees_to_heap_ ++;
     157             : 
     158           0 :       if (frees_to_heap_ > allocs_from_heap_) {
     159           0 :         ACE_ERROR((LM_ERROR,
     160             :                    "(%P|%t) ERROR: Dynamic_Cached_Allocator_With_Overflow::free %x"
     161             :                    " more deletes %d than allocs %d to the heap\n",
     162             :                    this,
     163             :                    frees_to_heap_.load(),
     164             :                    allocs_from_heap_.load()));
     165             :       }
     166             : 
     167           0 :       if (DCPS_debug_level >= 6) {
     168           0 :         if (frees_to_heap_ % 500 == 0) {
     169           0 :           ACE_DEBUG((LM_DEBUG,
     170             :                      "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::free %@"
     171             :                      " %Lu heap allocs with %Lu outstanding\n",
     172             :                      this, allocs_from_heap_.load(),
     173             :                      allocs_from_heap_.load() - frees_to_heap_.load()));
     174             :         }
     175             :       }
     176             : 
     177           0 :       return;
     178             : 
     179           0 :     } else if (ptr != 0) {
     180           0 :       frees_to_pool_ ++;
     181             : 
     182           0 :       if (frees_to_pool_ > allocs_from_pool_) {
     183           0 :         ACE_ERROR((LM_ERROR,
     184             :                    "(%P|%t) ERROR: Dynamic_Cached_Allocator_With_Overflow::free %x"
     185             :                    " more deletes %d than allocs %d from the pool\n",
     186             :                    this,
     187             :                    frees_to_pool_.load(),
     188             :                    allocs_from_pool_.load()));
     189             :       }
     190             : 
     191           0 :       free_list_.add((ACE_Cached_Mem_Pool_Node<char> *) ptr) ;
     192             : 
     193           0 :       if (DCPS_debug_level >= 6)
     194           0 :         if (available() % 500 == 0)
     195           0 :           ACE_DEBUG((LM_DEBUG,
     196             :                      "(%P|%t) Dynamic_Cached_Allocator_With_Overflow::malloc %x"
     197             :                      " %d pool allocs %d pool frees with %d available\n",
     198             :                      this, allocs_from_pool_.load(), frees_to_pool_.load(),
     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           0 :   size_t available() {
     213           0 :     return free_list_.size();
     214             :   };
     215             : 
     216             :   /// number of allocations from the heap.
     217             :   Atomic<unsigned long> allocs_from_heap_;
     218             :   /// number of allocations from the pool.
     219             :   Atomic<unsigned long> allocs_from_pool_;
     220             :   /// number of frees returned to the heap
     221             :   Atomic<unsigned long> frees_to_heap_ ;
     222             :   /// number of frees returned to the pool
     223             :   Atomic<unsigned long> frees_to_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*).
     235             :   ACE_Locked_Free_List<ACE_Cached_Mem_Pool_Node<char>, ACE_LOCK> free_list_;
     236             : 
     237             :   /// Remember the size of our chunks.
     238             :   size_t chunk_size_;
     239             : };
     240             : 
     241             : } // namespace DCPS
     242             : } // namespace OpenDDS
     243             : 
     244             : OPENDDS_END_VERSIONED_NAMESPACE_DECL
     245             : 
     246             : #endif /* DYNAMIC_CACHED_ALLOCATOR_WITH_OVERFLOW_T_H */

Generated by: LCOV version 1.16