-
Notifications
You must be signed in to change notification settings - Fork 380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cascaded allocator #2113
base: master
Are you sure you want to change the base?
Cascaded allocator #2113
Changes from 35 commits
bd320b1
37dc8f4
c61b9be
6166f88
32d3d1f
e2a5511
6835890
150eb7b
089af45
ac87bd4
b71f82d
34abb6e
cf39680
78e7165
e5e55bc
00016bf
912d9db
833bc87
40675b0
148734d
6e9a17c
d065069
eba85a4
1e1862d
0f26ecb
4a7fcd0
cf686b3
4c27ab0
8210ac0
e8d9b51
63e543f
d4e7d14
874d9cb
92066a1
4a9c114
8450156
0c8067c
cc4053c
ac5f611
fec93be
42b409e
a837b5d
c9ead44
021ef62
92c3499
b84e0c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -177,6 +177,221 @@ ACE_Dynamic_Cached_Allocator<ACE_LOCK>::free (void * ptr) | |
this->free_list_.add ((ACE_Cached_Mem_Pool_Node<char> *) ptr); | ||
} | ||
|
||
template <class ACE_LOCK> | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::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_)); | ||
smithAchang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Consider the exception of vector push_back call | ||
this->hierarchy_.push_back(tmp); | ||
if (0 == this->hierarchy_.size()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. empty? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when push_back fails it will throw, maybe use std::unique_ptr? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These APIs have strong exception safety guarantee, so there is no need to use std::unique_ptr. Is it true ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By the way, I can see the result of these calls to judge whether potential exceptions have occurred There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See https://en.cppreference.com/w/cpp/language/exceptions, push_back can throw, when it does the state of the std::vector is guaranteed at that moment, but it will not just return. The exception should go back to the caller to my idea, your allocator should not leak when push_back (or any operation it uses) throws There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when using unique_ptr the check after the push_back seems invalid There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have used std::unique_ptr for idiom in new commit But for a vector of pointer, I think sdt::vector can keep strong exception guarantee.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, Your idea is right! The APIs provide strong exception guarantee, but not Nothrow I will delete the defence codes |
||
{ | ||
delete tmp; | ||
return; | ||
} | ||
|
||
// Increase the chunk sum if all points having potential risk of exception is passed. | ||
this->chunk_sum_ += tmp->pool_depth (); | ||
} | ||
|
||
template <class ACE_LOCK> | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::~ACE_Cascaded_Dynamic_Cached_Allocator () | ||
{ | ||
for (size_t h = 0; h < this->hierarchy_.size(); h++) | ||
{ | ||
delete this->hierarchy_[h]; | ||
} | ||
|
||
this->hierarchy_.clear(); | ||
} | ||
|
||
template <class ACE_LOCK> void * | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::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)); | ||
|
||
void * ptr = nullptr; | ||
|
||
for (size_t h = 0; h < this->hierarchy_.size(); h++) | ||
{ | ||
ptr = this->hierarchy_[h]->malloc(nbytes); | ||
if(ptr != nullptr) | ||
break; | ||
} | ||
|
||
if(ptr == nullptr) | ||
{ | ||
comb_alloc_ptr tmp; | ||
ACE_NEW_RETURN (tmp, comb_alloc_type(this->initial_n_chunks_ * (1 << this->hierarchy_.size()), | ||
this->chunk_size_), | ||
nullptr); | ||
|
||
// Consider the exception of vector push_back call | ||
const auto old_size = this->hierarchy_.size(); | ||
this->hierarchy_.push_back(tmp); | ||
if (old_size == this->hierarchy_.size()) | ||
{ | ||
delete tmp; | ||
return nullptr; | ||
} | ||
|
||
// Increase the chunk sum if all points having potential risk of exception is passed. | ||
this->chunk_sum_ += tmp->pool_depth (); | ||
ptr = tmp->malloc(nbytes); | ||
} | ||
|
||
return ptr; | ||
} | ||
|
||
template <class ACE_LOCK> void * | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::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 <class ACE_LOCK> void * | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::calloc (size_t, size_t, char) | ||
{ | ||
ACE_NOTSUP_RETURN (nullptr); | ||
} | ||
|
||
template <class ACE_LOCK> void | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::free (void * ptr) | ||
{ | ||
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 (ptr != nullptr && this->hierarchy_.size() > 0) | ||
this->hierarchy_[0]->free(ptr); | ||
} | ||
|
||
template <class ACE_LOCK> int | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::remove () | ||
{ | ||
ACE_NOTSUP_RETURN (-1); | ||
} | ||
|
||
template <class ACE_LOCK> int | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::bind (const char *, void *, int) | ||
{ | ||
ACE_NOTSUP_RETURN (-1); | ||
} | ||
|
||
template <class ACE_LOCK> int | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::trybind (const char *, void *&) | ||
{ | ||
ACE_NOTSUP_RETURN (-1); | ||
} | ||
|
||
template <class ACE_LOCK> int | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::find (const char *, void *&) | ||
{ | ||
ACE_NOTSUP_RETURN (-1); | ||
} | ||
|
||
template <class ACE_LOCK> int | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::find (const char *) | ||
{ | ||
ACE_NOTSUP_RETURN (-1); | ||
} | ||
|
||
template <class ACE_LOCK> int | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::unbind (const char *) | ||
{ | ||
ACE_NOTSUP_RETURN (-1); | ||
} | ||
|
||
template <class ACE_LOCK> int | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::unbind (const char *, void *&) | ||
{ | ||
ACE_NOTSUP_RETURN (-1); | ||
} | ||
|
||
template <class ACE_LOCK> int | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::sync (ssize_t, int) | ||
{ | ||
ACE_NOTSUP_RETURN (-1); | ||
} | ||
|
||
template <class ACE_LOCK> int | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::sync (void *, size_t, int) | ||
{ | ||
ACE_NOTSUP_RETURN (-1); | ||
} | ||
|
||
template <class ACE_LOCK> int | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::protect (ssize_t, int) | ||
{ | ||
ACE_NOTSUP_RETURN (-1); | ||
} | ||
|
||
template <class ACE_LOCK> int | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::protect (void *, size_t, int) | ||
{ | ||
ACE_NOTSUP_RETURN (-1); | ||
} | ||
|
||
template <class ACE_LOCK> size_t | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::pool_depth () | ||
{ | ||
ACE_MT (ACE_GUARD_RETURN (ACE_LOCK, ace_mon, this->mutex_, 0)); | ||
|
||
size_t pool_depth = 0; | ||
|
||
for (size_t h = 0; h < this->hierarchy_.size(); h++) | ||
{ | ||
pool_depth += this->hierarchy_[h]->pool_depth(); | ||
} | ||
|
||
return pool_depth; | ||
} | ||
|
||
template <class ACE_LOCK> void | ||
ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::dump () const | ||
{ | ||
#if defined (ACE_HAS_DUMP) | ||
ACE_TRACE ("ACE_Cascaded_Dynamic_Cached_Allocator<ACE_LOCK>::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 <class MALLOC> void * | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,8 @@ | |
* @file Malloc_T.h | ||
* | ||
* @author Douglas C. Schmidt <[email protected]> and | ||
* Irfan Pyarali <[email protected]> | ||
* Irfan Pyarali <[email protected]> and | ||
* smithAchang <[email protected]> | ||
*/ | ||
//========================================================================== | ||
|
||
|
@@ -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 <vector> | ||
|
||
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 <code> sizeof (void*) </code> 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 | ||
* <code> init_n_chunks* the sum of current allocators </code> 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_LOCK> | ||
class ACE_Cascaded_Dynamic_Cached_Allocator : public ACE_Allocator | ||
{ | ||
public: | ||
// Useful STL-style traits. | ||
using comb_alloc_type = ACE_Dynamic_Cached_Allocator<ACE_Null_Mutex>; | ||
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 (); | ||
smithAchang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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<comb_alloc_ptr> 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 | ||
* | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
initialize tmp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the temp var will be assigned or call flow is ended by
ACE_NEW
macroShould it be initialized?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the temp var will be assigned or call flow is ended by
ACE_NEW
macroShould it be initialized?