diff --git a/ACE/MPC b/ACE/MPC new file mode 160000 index 0000000000000..307f1fc30267d --- /dev/null +++ b/ACE/MPC @@ -0,0 +1 @@ +Subproject commit 307f1fc30267dde08c28d5665716530ce7ff0abd diff --git a/ACE/ace/Malloc_T.cpp b/ACE/ace/Malloc_T.cpp index 8f1467423601a..31ccae938ae2a 100644 --- a/ACE/ace/Malloc_T.cpp +++ b/ACE/ace/Malloc_T.cpp @@ -14,6 +14,7 @@ #include "ace/ACE.h" #include "ace/OS_NS_string.h" #include +#include ACE_BEGIN_VERSIONED_NAMESPACE_DECL @@ -177,6 +178,230 @@ ACE_Dynamic_Cached_Allocator::free (void * ptr) this->free_list_.add ((ACE_Cached_Mem_Pool_Node *) ptr); } +template +ACE_Cascaded_Dynamic_Cached_Allocator::ACE_Cascaded_Dynamic_Cached_Allocator + (size_t initial_n_chunks, size_t chunk_size) + : initial_n_chunks_ (initial_n_chunks), + chunk_size_ (chunk_size), + chunk_sum_ (0) +{ + ACE_ASSERT (this->chunk_size_ > 0); + + comb_alloc_ptr tmp; + // If ACE_NEW fails, the hierarchy_ will be reconstructed when malloc API is called. + ACE_NEW (tmp, comb_alloc_type (this->initial_n_chunks_, this->chunk_size_)); + + // Consider the exception of vector push_back call. + std::unique_ptr smart_ptr (tmp); + // Has strong exception safety guarantee for call of push_back. + this->hierarchy_.push_back (smart_ptr.get ()); + + // Increase the chunk sum if all points having potential risk of exception is passed. + this->chunk_sum_ += smart_ptr->pool_depth (); + + smart_ptr.release (); +} + +template +ACE_Cascaded_Dynamic_Cached_Allocator::~ACE_Cascaded_Dynamic_Cached_Allocator () +{ + for (size_t h = 0; h < this->hierarchy_.size (); h++) + { + delete this->hierarchy_[h]; + } + + this->hierarchy_.clear (); +} + +template void * +ACE_Cascaded_Dynamic_Cached_Allocator::malloc (size_t nbytes) +{ + // Check if size requested fits within pre-determined size. + if (nbytes > this->chunk_size_) + return nullptr; + + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, nullptr)); + + size_t const size = this->hierarchy_.size (); + if (size == 0) + return nullptr; + + // Only the first and last child allocator maybe has free chunks, the others are empty. + void* ptr = this->hierarchy_[0]->malloc (nbytes); + if (ptr != nullptr) + return ptr; + + // The code will possibly be optimized by compiler when using const var. + size_t const lastAllocatorPos = size - 1; + if (lastAllocatorPos > 0) + { + ptr = this->hierarchy_[lastAllocatorPos]->malloc (nbytes); + if (ptr != nullptr) + return ptr; + } + + // Need alloc a new child allocator. + comb_alloc_ptr tmp; + ACE_NEW_RETURN (tmp, comb_alloc_type (this->initial_n_chunks_ * (1 << size), + this->chunk_size_), + nullptr); + + // Consider the exception of vector push_back call. + std::unique_ptr smart_ptr (tmp); + // Has strong exception safety guarantee for call of push_back. + this->hierarchy_.push_back (smart_ptr.get ()); + + // Increase the chunk sum if all points having potential risk of exception is passed. + this->chunk_sum_ += smart_ptr->pool_depth (); + ptr = smart_ptr->malloc (nbytes); + smart_ptr.release (); + + return ptr; +} + +template void * +ACE_Cascaded_Dynamic_Cached_Allocator::calloc (size_t nbytes, + char initial_value) +{ + // Check if size requested fits within pre-determined size. + if (nbytes > this->chunk_size_) + return nullptr; + + // No need any lock. + void *ptr = malloc(nbytes); + if (ptr != nullptr) + ACE_OS::memset (ptr, initial_value, this->chunk_size_); + + return ptr; +} + +template void * +ACE_Cascaded_Dynamic_Cached_Allocator::calloc (size_t, size_t, char) +{ + ACE_NOTSUP_RETURN (nullptr); +} + +template void +ACE_Cascaded_Dynamic_Cached_Allocator::free (void * ptr) +{ + if (ptr == nullptr) + return; + + ACE_MT (ACE_GUARD (ACE_LOCK, ace_mon, this->mutex_)); + + ACE_ASSERT (this->hierarchy_.size () > 0); + + // Use first allocator as a free chunk manager for all allocators when chunk freed. + if (this->hierarchy_.size () != 0) + this->hierarchy_[0]->free (ptr); +} + +template int +ACE_Cascaded_Dynamic_Cached_Allocator::remove () +{ + ACE_NOTSUP_RETURN (-1); +} + +template int +ACE_Cascaded_Dynamic_Cached_Allocator::bind (const char *, void *, int) +{ + ACE_NOTSUP_RETURN (-1); +} + +template int +ACE_Cascaded_Dynamic_Cached_Allocator::trybind (const char *, void *&) +{ + ACE_NOTSUP_RETURN (-1); +} + +template int +ACE_Cascaded_Dynamic_Cached_Allocator::find (const char *, void *&) +{ + ACE_NOTSUP_RETURN (-1); +} + +template int +ACE_Cascaded_Dynamic_Cached_Allocator::find (const char *) +{ + ACE_NOTSUP_RETURN (-1); +} + +template int +ACE_Cascaded_Dynamic_Cached_Allocator::unbind (const char *) +{ + ACE_NOTSUP_RETURN (-1); +} + +template int +ACE_Cascaded_Dynamic_Cached_Allocator::unbind (const char *, void *&) +{ + ACE_NOTSUP_RETURN (-1); +} + +template int +ACE_Cascaded_Dynamic_Cached_Allocator::sync (ssize_t, int) +{ + ACE_NOTSUP_RETURN (-1); +} + +template int +ACE_Cascaded_Dynamic_Cached_Allocator::sync (void *, size_t, int) +{ + ACE_NOTSUP_RETURN (-1); +} + +template int +ACE_Cascaded_Dynamic_Cached_Allocator::protect (ssize_t, int) +{ + ACE_NOTSUP_RETURN (-1); +} + +template int +ACE_Cascaded_Dynamic_Cached_Allocator::protect (void *, size_t, int) +{ + ACE_NOTSUP_RETURN (-1); +} + +template size_t +ACE_Cascaded_Dynamic_Cached_Allocator::pool_depth () +{ + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, 0)); + + size_t const size = this->hierarchy_.size (); + if (size == 0) + return 0; + + // Only the first and last child allocator maybe has non-zero value, the others have zero value. + size_t pool_depth = this->hierarchy_[0]->pool_depth (); + + if (size > 1) + pool_depth += this->hierarchy_[size - 1]->pool_depth (); + + return pool_depth; +} + +template void +ACE_Cascaded_Dynamic_Cached_Allocator::dump () const +{ +#if defined (ACE_HAS_DUMP) + ACE_TRACE ("ACE_Cascaded_Dynamic_Cached_Allocator::dump"); + + ACELIB_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this)); + ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("initial_n_chunks_ = %u\n"), this->initial_n_chunks_)); + ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("chunk_size_ = %u\n"), this->chunk_size_)); + ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("chunk_sum_ = %u\n"), this->chunk_sum_)); + ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("hierarchy_ size = %u\n"), this->hierarchy_.size ())); + + for (size_t h = 0; h < this->hierarchy_.size (); h++) + { + this->hierarchy_[h]->dump (); + ACELIB_DEBUG ((LM_DEBUG, ACE_TEXT ("\n"))); + } + + ACELIB_DEBUG ((LM_DEBUG, ACE_END_DUMP)); +#endif /* ACE_HAS_DUMP */ +} + ACE_ALLOC_HOOK_DEFINE_Tmcc (ACE_Malloc_T) template void * diff --git a/ACE/ace/Malloc_T.h b/ACE/ace/Malloc_T.h index 62775b798cbdb..44fca744c3604 100644 --- a/ACE/ace/Malloc_T.h +++ b/ACE/ace/Malloc_T.h @@ -5,7 +5,8 @@ * @file Malloc_T.h * * @author Douglas C. Schmidt and - * Irfan Pyarali + * Irfan Pyarali and + * smithAchang */ //========================================================================== @@ -15,6 +16,7 @@ #include "ace/Malloc.h" /* Need ACE_Control_Block */ #include "ace/Malloc_Base.h" /* Need ACE_Allocator */ +#include "ace/Null_Mutex.h" /* Need ACE_Null_Mutex */ #if !defined (ACE_LACKS_PRAGMA_ONCE) # pragma once @@ -24,6 +26,8 @@ #include "ace/Free_List.h" #include "ace/Guard_T.h" +#include + ACE_BEGIN_VERSIONED_NAMESPACE_DECL /** @@ -203,6 +207,109 @@ class ACE_Dynamic_Cached_Allocator : public ACE_New_Allocator size_t chunk_size_; }; +/** + * @class ACE_Cascaded_Dynamic_Cached_Allocator + * + * @brief A size-based allocator that caches blocks for quicker access, + * but with a hierarchy of cascaded, nested allocators + * + * This class enables caching of dynamically allocated, + * fixed-size chunks. Notice that the @a chunk_size + * must be greater than or equal to sizeof (void*) for + * this to work properly. + * + * Notice that when the latest allocator is empty, the allocator will create a fresh + * @a ACE_Dynamic_Cached_Allocator allocator again with + * init_n_chunks * ( 1 << the sum of current allocators ) as it's constructor parameter, + * so all the allocators will form a cascaded hierarchy. + + * This class can be configured flexibly with different types of + * ACE_LOCK strategies that support the @a ACE_Thread_Mutex and + * @a ACE_Process_Mutex constructor API. + * + * @sa ACE_Dynamic_Cached_Allocator + */ +template +class ACE_Cascaded_Dynamic_Cached_Allocator : public ACE_Allocator +{ +public: + // Useful STL-style traits. + using comb_alloc_type = ACE_Dynamic_Cached_Allocator; + using comb_alloc_ptr = comb_alloc_type*; + + /// Create a cached memory pool with @a initial_n_chunks chunks + /// each with @a chunk_size size. + ACE_Cascaded_Dynamic_Cached_Allocator (size_t initial_n_chunks, size_t chunk_size); + + /// Clear things up. + ~ACE_Cascaded_Dynamic_Cached_Allocator (); + + /** + * Get a chunk of memory from free list cache. Note that @a nbytes is + * only checked to make sure that it's less or equal to @a chunk_size, + * and is otherwise ignored since malloc() always returns a pointer to an + * item of @a chunk_size size. + */ + virtual void *malloc (size_t nbytes); + + /** + * Get a chunk of memory from free list cache, giving them + * @a initial_value. Note that @a nbytes is only checked to make sure + * that it's less or equal to @a chunk_size, and is otherwise ignored + * since calloc() always returns a pointer to an item of @a chunk_size. + */ + virtual void *calloc (size_t nbytes, char initial_value = '\0'); + + /// This method is a no-op and just returns 0 since the free list + /// only works with fixed sized entities. + virtual void *calloc (size_t n_elem, size_t elem_size, char initial_value = '\0'); + + /// Return a chunk of memory back to free list cache. + virtual void free (void *ptr); + + /// Dump the state of this object. + virtual void dump () const; + + /// These methods are no-ops. + virtual int remove (); + virtual int bind (const char *name, void *pointer, int duplicates = 0); + virtual int trybind (const char *name, void *&pointer); + virtual int find (const char *name, void *&pointer); + virtual int find (const char *name); + virtual int unbind (const char *name); + virtual int unbind (const char *name, void *&pointer); + virtual int sync (ssize_t len = -1, int flags = MS_SYNC); + virtual int sync (void *addr, size_t len, int flags = MS_SYNC); + virtual int protect (ssize_t len = -1, int prot = PROT_RDWR); + virtual int protect (void *addr, size_t len, int prot = PROT_RDWR); + + /// Return the number of chunks available in the hierarchy. + size_t pool_depth (); + + /// Return the sum of chunks including used and freed in the hierarchy. + size_t pool_sum (); + + /// Returns a reference to the lock used to provide mutual exclusion to + /// the allocator hierarchy. + ACE_LOCK &mutex (); + +private: + /// Synchronization variable for API. + ACE_LOCK mutex_; + + /// Remember how we allocate the memory so we can clear things up later. + std::vector hierarchy_; + + /// Remember the size of initial n_chunks for creating fresh allocator in future. + const size_t initial_n_chunks_; + + /// Remember the size of our chunks for creating fresh allocator in future. + const size_t chunk_size_; + + /// Remember the sum of our chunks including used and freed + size_t chunk_sum_; +}; + /** * @class ACE_Allocator_Adapter * diff --git a/ACE/ace/Malloc_T.inl b/ACE/ace/Malloc_T.inl index c66d0edfc69c5..e9e4c49054f10 100644 --- a/ACE/ace/Malloc_T.inl +++ b/ACE/ace/Malloc_T.inl @@ -38,6 +38,20 @@ ACE_Dynamic_Cached_Allocator::pool_depth () return this->free_list_.size (); } +template ACE_INLINE size_t +ACE_Cascaded_Dynamic_Cached_Allocator::pool_sum () +{ + ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, 0)); + + return this->chunk_sum_; +} + +template ACE_INLINE ACE_LOCK & +ACE_Cascaded_Dynamic_Cached_Allocator::mutex () +{ + return this->mutex_; +} + template ACE_INLINE int ACE_Malloc_T::ref_counter () { diff --git a/ACE/tests/Allocator_Cascaded_Test.cpp b/ACE/tests/Allocator_Cascaded_Test.cpp new file mode 100644 index 0000000000000..4c06174136d09 --- /dev/null +++ b/ACE/tests/Allocator_Cascaded_Test.cpp @@ -0,0 +1,167 @@ + +//============================================================================ +/** + * @file Allocator_Cascaded_Test.cpp + * + * This program tests the basic APIs supported in and demonstrates how to use it. + * + * @author Smith Achang + */ +//============================================================================ + + +#include "ace/Malloc_T.h" +#include "test_config.h" +#include + +#define ACE_TEST_EXCEPTION_RETURN(expression, message) \ +do \ +{ \ + if (expression) \ + { \ + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT_CHAR_TO_TCHAR (message)), 1);\ + } \ +} \ +while (0) + +static int +run_cascaded_allocator_test () +{ + ACE_DEBUG ((LM_INFO, "%C begin to run ...\n", __func__)); + + size_t const initial_n_chunks = 1; + size_t const chunk_size = sizeof(void*) + 7; + + void *ptr, *ptr1, *ptr2; + size_t nbytes = chunk_size; + size_t pool_sum, old_pool_sum, pool_depth, old_pool_depth; + char initial_value = '\0'; + + ACE_Cascaded_Dynamic_Cached_Allocator alloc (initial_n_chunks, chunk_size); + pool_sum = alloc.pool_sum (); + ACE_TEST_EXCEPTION_RETURN (pool_sum != initial_n_chunks, " initial pool sum must be initial_n_chunks\n"); + + ACE_DEBUG ((LM_INFO, "%C will test unsupported API ...\n", __func__)); + ptr = alloc.calloc (1, chunk_size, initial_value); + ACE_TEST_EXCEPTION_RETURN (ptr != nullptr, + " pool must return nullptr for calloc(size_t n_elem, size_t elem_size, char initial_value) call\n"); + ACE_TEST_EXCEPTION_RETURN(alloc.pool_depth() != initial_n_chunks, + " initial pool depth must keep unchanged for call of unsupported API\n"); + + ACE_DEBUG ((LM_INFO, "%C will test supported calloc API ...\n", __func__)); + ptr = alloc.calloc (nbytes + 1, initial_value); + ACE_TEST_EXCEPTION_RETURN (ptr != nullptr, + " pool must return nullptr for calloc call with bigger nbytes parameter\n"); + ACE_TEST_EXCEPTION_RETURN (alloc.pool_depth () != initial_n_chunks, + " initial pool depth must keep unchanged for call of unsupported API\n"); + + ptr = alloc.calloc (nbytes); + ACE_TEST_EXCEPTION_RETURN (ptr == nullptr, " pool must return valid ptr for calloc call with normal nbytes\n"); + ACE_TEST_EXCEPTION_RETURN (*static_cast (ptr) != 0, " calloc call will clear the memory to zero\n"); + alloc.free (ptr); + + ACE_DEBUG ((LM_INFO, "%C will test supported malloc API ...\n", __func__)); + ptr = alloc.malloc (nbytes + 1); + ACE_TEST_EXCEPTION_RETURN (ptr != nullptr, + " pool must return nullptr for malloc call with bigger nbytes parameter\n"); + + ptr = alloc.malloc(nbytes); + ACE_TEST_EXCEPTION_RETURN (ptr == nullptr, + " pool must return valid ptr for malloc call with normal nbytes\n"); + ACE_TEST_EXCEPTION_RETURN (alloc.pool_depth () != 0, " initial pool depth must be zero\n"); + alloc.free(ptr); + ACE_TEST_EXCEPTION_RETURN (alloc.pool_depth () != initial_n_chunks, + " initial pool depth must be initial_n_chunks after free\n"); + + ACE_DEBUG ((LM_INFO, "%C will test cascaded allocator ...\n", __func__)); + ptr = alloc.malloc (nbytes); + ACE_TEST_EXCEPTION_RETURN (ptr == nullptr, + " pool must return valid ptr, cascaded pool must support to alloc more times firstly\n"); + + ptr1 = alloc.malloc (nbytes); + ACE_TEST_EXCEPTION_RETURN (ptr1 == nullptr, + " pool must return valid ptr, cascaded pool must support to alloc more times secondly\n"); + pool_depth = alloc.pool_depth (); + ACE_TEST_EXCEPTION_RETURN (pool_depth != 1, + " cascaded pool depth must support to alloc twice\n"); + + old_pool_depth = alloc.pool_depth(); + ptr2 = alloc.malloc (nbytes); + ACE_TEST_EXCEPTION_RETURN (ptr2 == nullptr, + " pool must return valid ptr, cascaded pool must support to alloc more times thirdly\n"); + pool_depth = alloc.pool_depth(); + ACE_TEST_EXCEPTION_RETURN (pool_depth != ((2*initial_n_chunks) - old_pool_depth - 1), + " cascaded pool depth must support to alloc three times\n"); + + old_pool_sum = pool_sum; + pool_sum = alloc.pool_sum (); + ACE_TEST_EXCEPTION_RETURN (pool_sum < old_pool_sum, + " cascaded pool sum must be bigger than that of initial pool\n"); + ACE_TEST_EXCEPTION_RETURN (pool_sum != (initial_n_chunks + (2 * initial_n_chunks)), + " cascaded pool sum must be as expected, pool has been enlarged\n"); + + alloc.free (ptr); + alloc.free (ptr1); + alloc.free (ptr2); + + ACE_TEST_EXCEPTION_RETURN (alloc.pool_depth () != (initial_n_chunks + (2 * initial_n_chunks)), + " cascaded pool depth must be three after having freed all malloc ptrs\n"); + + ACE_DEBUG ((LM_INFO, "%C will test cascaded allocator deeply ...\n", __func__)); + old_pool_sum = alloc.pool_sum (); + size_t const totalAllocSum = 8 * 1024; + char cmpvalues[chunk_size]; + char initial_cmp_value = initial_value; + std::vector ptrs; + for (long i = 0; i < totalAllocSum; ++i, ++initial_cmp_value) + { + ACE_OS::memset (cmpvalues, initial_cmp_value, chunk_size); + ptr = alloc.calloc (nbytes, initial_cmp_value); + ACE_TEST_EXCEPTION_RETURN (ptr == nullptr, + " pool must return valid ptr for deeply calloc api test with normal nbytes\n"); + ACE_TEST_EXCEPTION_RETURN (ACE_OS::memcmp (ptr, cmpvalues, chunk_size) != 0, + " calloc call must set the memory content as expected\n"); + ptrs.push_back (ptr); + } + + pool_sum = alloc.pool_sum(); + ACE_TEST_EXCEPTION_RETURN (pool_sum <= old_pool_sum, + " pool sum must greater than old pool sum after alloc many chunks for deeply test\n"); + + for (size_t i = 0; i < ptrs.size (); ++i) + { + alloc.free (ptrs[i]); + } + + pool_depth = alloc.pool_depth(); + ACE_TEST_EXCEPTION_RETURN (pool_depth != pool_sum, + " pool depth must equal to pool sum after all chunks has been freed for deeply test\n"); + + for (long i = 0; i < totalAllocSum; ++i, ++initial_cmp_value) + { + ACE_OS::memset (cmpvalues, initial_cmp_value, chunk_size); + ptr = alloc.calloc (nbytes, initial_cmp_value); + ACE_TEST_EXCEPTION_RETURN (ptr == nullptr, + " pool must return valid ptr for deeply calloc api test2 with normal nbytes\n"); + ACE_TEST_EXCEPTION_RETURN (ACE_OS::memcmp (ptr, cmpvalues, chunk_size) != 0, + " deeply calloc api test2 must set the memory content as expected\n"); + alloc.free (ptr); + } + + return 0; +} + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Allocator_Cascaded_Test")); + + int retval = 0; + + // Run the tests for each type of ordering. + retval = run_cascaded_allocator_test (); + + ACE_END_TEST; + + return retval; +} diff --git a/ACE/tests/run_test.lst b/ACE/tests/run_test.lst index 2075bdbc33b79..19d5caf2e9f2b 100644 --- a/ACE/tests/run_test.lst +++ b/ACE/tests/run_test.lst @@ -16,6 +16,7 @@ ACE_Init_Test: MFC ACE_Test Aio_Platform_Test +Allocator_Cascaded_Test Arg_Shifter_Test ARGV_Test Array_Map_Test diff --git a/ACE/tests/tests.mpc b/ACE/tests/tests.mpc index e60cd3eca87df..2fe3a6fa27437 100644 --- a/ACE/tests/tests.mpc +++ b/ACE/tests/tests.mpc @@ -145,6 +145,14 @@ project(Aio Platform Test) : acetest { } } +project(Allocator Cascaded Test) : acetest { + avoids += ace_for_tao + exename = Allocator_Cascaded_Test + Source_Files { + Allocator_Cascaded_Test.cpp + } +} + project(Arg Shifter Test) : acetest { exename = Arg_Shifter_Test Source_Files {