00001
00002
00003
00004
00005
00006
00007
00008 #include "DCPS/DdsDcps_pch.h"
00009 #include "MemoryPool.h"
00010 #include "PoolAllocator.h"
00011 #include "ace/Log_Msg.h"
00012 #include "ace/OS_NS_stdio.h"
00013 #include <stdexcept>
00014 #include <limits>
00015 #include <map>
00016 #include <cstring>
00017
00018 #if defined(WITH_VALGRIND)
00019 #include "valgrind/memcheck.h"
00020 #endif
00021
00022 #define TEST_CHECK(COND) \
00023 if (!( COND )) { \
00024 char msg[1024]; \
00025 ACE_OS::snprintf(msg, 1024, "%s: FAILED at %s:%d", #COND, __FILE__, __LINE__); \
00026 ACE_OS::printf("%s\n", msg); \
00027 throw std::runtime_error(msg); \
00028 return; \
00029 }
00030
00031 OPENDDS_BEGIN_VERSIONED_NAMESPACE_DECL
00032
00033 namespace OpenDDS { namespace DCPS {
00034
00035 AllocHeader::AllocHeader()
00036 : alloc_size_(0)
00037 , prev_size_(0)
00038 {
00039 }
00040
00041 unsigned char*
00042 AllocHeader::ptr() const
00043 {
00044 const unsigned char* buff = reinterpret_cast<const unsigned char*>(this + 1);
00045 return const_cast<unsigned char*>(buff);
00046 }
00047
00048 AllocHeader*
00049 AllocHeader::next_adjacent() {
00050 unsigned char* past_buffer_end = ptr() + size();
00051 return reinterpret_cast<AllocHeader*>(past_buffer_end);
00052 }
00053
00054 AllocHeader*
00055 AllocHeader::prev_adjacent() {
00056 AllocHeader* result = NULL;
00057 if (prev_size_) {
00058 unsigned char* self = reinterpret_cast<unsigned char*>(this);
00059 unsigned char* prev_buffer_start = self - prev_size_;
00060 unsigned char* past_alloc = prev_buffer_start - sizeof(AllocHeader);
00061 result = reinterpret_cast<AllocHeader*>(past_alloc);
00062 }
00063 return result;
00064 }
00065
00066 void
00067 AllocHeader::allocate(size_t size) {
00068 set_allocated();
00069 set_size(size);
00070 }
00071
00072 void
00073 AllocHeader::set_size(size_t size)
00074 {
00075 if (is_free()) {
00076 size *= -1;
00077 }
00078 alloc_size_ = (int)size;
00079 }
00080
00081 void
00082 AllocHeader::join_next() {
00083
00084 size_t next_size = this->next_adjacent()->size();
00085 size_t joined_size = this->size() + next_size + sizeof(AllocHeader);
00086 this->set_size(joined_size);
00087 }
00088
00089 void
00090 FreeHeader::init_free_block(unsigned int pool_size)
00091 {
00092 alloc_size_ = static_cast<int>(pool_size - sizeof(AllocHeader));
00093 prev_size_ = 0;
00094 set_free();
00095 }
00096
00097 void
00098 FreeHeader::set_free()
00099 {
00100
00101 if (!is_free()) {
00102 alloc_size_ *= -1;
00103 set_smaller_free(NULL, NULL);
00104 set_larger_free(NULL, NULL);
00105 }
00106 }
00107 FreeHeader*
00108 FreeHeader::smaller_free(unsigned char* pool_base) const
00109 {
00110 FreeHeader* result = NULL;
00111 if (offset_smaller_free_ != std::numeric_limits<size_t>::max()) {
00112 result = reinterpret_cast<FreeHeader*>(pool_base + offset_smaller_free_);
00113 }
00114 return result;
00115 }
00116
00117 FreeHeader*
00118 FreeHeader::larger_free(unsigned char* pool_base) const
00119 {
00120 FreeHeader* result = NULL;
00121 if (offset_larger_free_ != std::numeric_limits<size_t>::max()) {
00122 result = reinterpret_cast<FreeHeader*>(pool_base + offset_larger_free_);
00123 }
00124 return result;
00125 }
00126
00127 void
00128 FreeHeader::set_smaller_free(FreeHeader* next, unsigned char* pool_base)
00129 {
00130 if (next) {
00131 offset_smaller_free_ = reinterpret_cast<unsigned char*>(next) - pool_base;
00132 } else {
00133 offset_smaller_free_ = std::numeric_limits<size_t>::max();
00134 }
00135 }
00136
00137 void
00138 FreeHeader::set_larger_free(FreeHeader* prev, unsigned char* pool_base)
00139 {
00140 if (prev) {
00141 offset_larger_free_ = reinterpret_cast<unsigned char*>(prev) - pool_base;
00142 } else {
00143 offset_larger_free_ = std::numeric_limits<size_t>::max();
00144 }
00145 }
00146
00147 FreeIndexNode::FreeIndexNode()
00148 : size_(0)
00149 , limit_(0)
00150 , ptr_(0)
00151 {
00152 }
00153
00154 void
00155 FreeIndexNode::set_sizes(size_t size, size_t limit)
00156 {
00157 size_ = size;
00158 limit_ = limit;
00159 }
00160
00161 FreeIndex::FreeIndex(FreeHeader*& largest_free)
00162 : size_(0)
00163 , largest_free_(largest_free)
00164 {
00165 }
00166
00167 void
00168 FreeIndex::add(FreeHeader* freed)
00169 {
00170 unsigned int index = node_index(freed->size());
00171 FreeIndexNode* node = nodes_ + index;
00172
00173
00174 if ((node->ptr() == NULL) || (node->ptr()->size() >= freed->size())) {
00175
00176 node->set_ptr(freed);
00177 }
00178 }
00179
00180 void
00181 FreeIndex::remove(FreeHeader* free_block, FreeHeader* larger)
00182 {
00183 unsigned int index = node_index(free_block->size());
00184 FreeIndexNode* node = nodes_ + index;
00185
00186
00187 if (node->ptr() == free_block) {
00188
00189 if (larger && node->contains(larger->size())) {
00190 node->set_ptr(larger);
00191 } else {
00192 node->set_ptr(NULL);
00193 }
00194 }
00195 }
00196
00197 void
00198 FreeIndex::init(FreeHeader* init_free_block)
00199 {
00200 size_t max = std::numeric_limits<size_t>::max();
00201 for (size_t size = min_index; size <= max_index; size *= 2) {
00202 nodes_[size_].set_sizes(size, (size == max_index) ? max : size*2);
00203 ++size_;
00204 }
00205 add(init_free_block);
00206 }
00207
00208 FreeHeader*
00209 FreeIndex::find(size_t search_size, unsigned char* pool_base)
00210 {
00211 unsigned int index = node_index(search_size);
00212 FreeIndexNode* index_node = nodes_ + index;
00213
00214
00215 FreeHeader* result = NULL;
00216 if (largest_free_ && (largest_free_->size() >= search_size)) {
00217 result = largest_free_;
00218
00219
00220 while (index_node < nodes_ + size_) {
00221 if (index_node->ptr() && index_node->ptr()->size() >= search_size) {
00222 result = index_node->ptr();
00223 break;
00224 }
00225 ++index_node;
00226 }
00227 }
00228
00229
00230 while (result) {
00231 FreeHeader* smaller = result->smaller_free(pool_base);
00232 if (smaller && smaller->size() >= search_size) {
00233 result = smaller;
00234 } else {
00235 break;
00236 }
00237 }
00238
00239 return result;
00240 }
00241
00242 unsigned int
00243 FreeIndex::node_index(size_t size)
00244 {
00245
00246
00247
00248 size_t size_copy = size >> (min_index_pow + 1);
00249 unsigned int index = 0;
00250 unsigned int max_idx = max_index_pow - min_index_pow;
00251 while (size_copy && (index < max_idx)) {
00252 ++index;
00253 size_copy = size_copy >> 1;
00254 }
00255 return index;
00256 }
00257
00258 #ifdef VALIDATE_MEMORY_POOL
00259 void
00260 FreeIndex::validate_index(FreeIndex& index, unsigned char* base, bool log)
00261 {
00262 if (log) {
00263 FreeIndexNode* node = index.nodes_;
00264 while (node < index.nodes_ + index.size_) {
00265 if (node->ptr()) {
00266 ACE_OS::printf(" IND[%4d] -> %4d\n", node->size(), node->ptr()->size());
00267 } else {
00268 ACE_OS::printf(" IND[%4d] -> NULL\n", node->size());
00269 }
00270 ++node;
00271 }
00272 }
00273
00274
00275 for (size_t size = min_index; size <= max_index; size *= 2) {
00276
00277 FreeHeader* size_or_larger = index.find(size, base);
00278 if (size_or_larger) {
00279 TEST_CHECK(size_or_larger->size() >= size);
00280 }
00281 }
00282
00283
00284 for (FreeIndexNode* node = index.nodes_; node < index.nodes_ + index.size_; ++node) {
00285 FreeHeader* block = node->ptr();
00286 if (block) {
00287
00288 TEST_CHECK(node->contains(block->size()));
00289
00290 FreeHeader* smaller = block;
00291 while ((smaller = smaller->smaller_free(base))) {
00292
00293 TEST_CHECK(smaller->size() < node->size());
00294 }
00295 }
00296 }
00297 }
00298 #endif
00299
00300 MemoryPool::MemoryPool(unsigned int pool_size, size_t granularity)
00301 : granularity_(align(granularity, 8))
00302 , min_alloc_size_(align(min_free_size - sizeof(AllocHeader), granularity_))
00303 , pool_size_(align(pool_size, granularity_))
00304 , pool_ptr_(new unsigned char[pool_size_])
00305 , largest_free_(NULL)
00306 , free_index_(largest_free_)
00307 {
00308 AllocHeader* the_pool = new (pool_ptr_) AllocHeader();
00309 FreeHeader* first_free = reinterpret_cast<FreeHeader*>(the_pool);
00310 first_free->init_free_block(static_cast<unsigned int>(pool_size_));
00311 largest_free_ = first_free;
00312 free_index_.init(first_free);
00313 lwm_free_bytes_ = largest_free_->size();
00314 #if defined(WITH_VALGRIND)
00315 VALGRIND_MAKE_MEM_NOACCESS(pool_ptr_, pool_size_);
00316 VALGRIND_CREATE_MEMPOOL(pool_ptr_, 0, false);
00317 #endif
00318 }
00319
00320 MemoryPool::~MemoryPool()
00321 {
00322 #ifndef OPENDDS_SAFETY_PROFILE
00323 delete [] pool_ptr_;
00324 #endif
00325 }
00326
00327 size_t
00328 MemoryPool::lwm_free_bytes() const
00329 {
00330 return lwm_free_bytes_;
00331 }
00332
00333 void*
00334 MemoryPool::pool_alloc(size_t size)
00335 {
00336 #if defined(WITH_VALGRIND)
00337 VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(pool_ptr_, pool_size_);
00338 #endif
00339
00340
00341 unsigned char* block = NULL;
00342
00343
00344 size_t aligned_size = align(size, granularity_);
00345
00346 if (aligned_size < min_alloc_size_) {
00347 aligned_size = min_alloc_size_;
00348 }
00349
00350
00351 FreeHeader* block_to_alloc = free_index_.find(aligned_size, pool_ptr_);
00352
00353 if (block_to_alloc) {
00354 block = allocate(block_to_alloc, aligned_size);
00355 }
00356
00357
00358 size_t largest_free_bytes = largest_free_ ? largest_free_->size() : 0;
00359 if (largest_free_bytes < lwm_free_bytes_) {
00360 lwm_free_bytes_ = largest_free_bytes;
00361 }
00362
00363 #ifdef VALIDATE_MEMORY_POOL
00364 validate_pool(*this, false);
00365 #endif
00366
00367 #if defined(WITH_VALGRIND)
00368 VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(pool_ptr_, pool_size_);
00369 VALGRIND_MEMPOOL_ALLOC(pool_ptr_, block, size);
00370 #endif
00371
00372 return block;
00373 }
00374
00375 bool
00376 MemoryPool::pool_free(void* ptr)
00377 {
00378 bool freed = false;
00379 if (ptr && includes(ptr)) {
00380 #if defined(WITH_VALGRIND)
00381 VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(pool_ptr_, pool_size_);
00382 #endif
00383
00384 FreeHeader* header = reinterpret_cast<FreeHeader*>(
00385 reinterpret_cast<AllocHeader*>(ptr) - 1);
00386
00387
00388 header->set_free();
00389
00390 join_free_allocs(header);
00391
00392 #ifdef VALIDATE_MEMORY_POOL
00393 validate_pool(*this, false);
00394 #endif
00395
00396 freed = true;
00397
00398 #if defined(WITH_VALGRIND)
00399 VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(pool_ptr_, pool_size_);
00400 VALGRIND_MEMPOOL_FREE(pool_ptr_, ptr);
00401 #endif
00402 }
00403
00404 return freed;
00405 }
00406
00407 void
00408 MemoryPool::join_free_allocs(FreeHeader* freed)
00409 {
00410
00411 if (joinable_next(freed)) {
00412 FreeHeader* next_free = reinterpret_cast<FreeHeader*>(freed->next_adjacent());
00413 remove_free_alloc(next_free);
00414 freed->join_next();
00415
00416 AllocHeader* next = freed->next_adjacent();
00417 if (includes(next)) {
00418 next->set_prev_size(freed->size());
00419 }
00420 }
00421 if (joinable_prev(freed)) {
00422 FreeHeader* prev_free = reinterpret_cast<FreeHeader*>(freed->prev_adjacent());
00423 remove_free_alloc(prev_free);
00424
00425 prev_free->join_next();
00426 insert_free_alloc(prev_free);
00427
00428 AllocHeader* next = prev_free->next_adjacent();
00429 if (includes(next)) {
00430 next->set_prev_size(prev_free->size());
00431 }
00432 } else {
00433 insert_free_alloc(freed);
00434 }
00435 }
00436
00437 void
00438 MemoryPool::remove_free_alloc(FreeHeader* block_to_alloc)
00439 {
00440 FreeHeader* smaller = block_to_alloc->smaller_free(pool_ptr_);
00441 FreeHeader* larger = block_to_alloc->larger_free(pool_ptr_);
00442
00443 block_to_alloc->set_smaller_free(NULL, NULL);
00444 block_to_alloc->set_larger_free(NULL, NULL);
00445
00446
00447 if (block_to_alloc == largest_free_) {
00448
00449 largest_free_ = smaller;
00450 }
00451
00452 if (larger) {
00453 larger->set_smaller_free(smaller, pool_ptr_);
00454 }
00455
00456 if (smaller) {
00457 smaller->set_larger_free(larger, pool_ptr_);
00458 }
00459
00460
00461 free_index_.remove(block_to_alloc, larger);
00462 }
00463
00464 void
00465 MemoryPool::insert_free_alloc(FreeHeader* freed)
00466 {
00467
00468 FreeHeader* alloc = free_index_.find(freed->size(), pool_ptr_);
00469
00470 if (alloc) {
00471 FreeHeader* smaller = alloc->smaller_free(pool_ptr_);
00472
00473
00474 freed->set_larger_free(alloc, pool_ptr_);
00475 alloc->set_smaller_free(freed, pool_ptr_);
00476 if (smaller) {
00477 smaller->set_larger_free(freed, pool_ptr_);
00478 freed->set_smaller_free(smaller, pool_ptr_);
00479 }
00480
00481 } else {
00482 if (freed != largest_free_) {
00483 freed->set_smaller_free(largest_free_, pool_ptr_);
00484 if (largest_free_) {
00485 largest_free_->set_larger_free(freed, pool_ptr_);
00486 }
00487 largest_free_ = freed;
00488 }
00489 }
00490
00491
00492 free_index_.add(freed);
00493 }
00494
00495 unsigned char*
00496 MemoryPool::allocate(FreeHeader* free_block, size_t alloc_size)
00497 {
00498 size_t free_block_size = free_block->size();
00499 size_t remainder = free_block_size - alloc_size;
00500
00501
00502 if (remainder < min_free_size) {
00503 alloc_size = free_block_size;
00504 remainder = 0;
00505 }
00506
00507
00508 if (remainder) {
00509
00510 remainder -= sizeof(AllocHeader);
00511
00512
00513 AllocHeader* next_adjacent = free_block->next_adjacent();
00514 if (includes(next_adjacent)) {
00515 next_adjacent->set_prev_size(static_cast<int>(alloc_size));
00516 }
00517
00518
00519
00520 remove_free_alloc(free_block);
00521 free_block->set_size(remainder);
00522 insert_free_alloc(free_block);
00523
00524
00525
00526
00527 AllocHeader* alloc_block = new(free_block->next_adjacent()) AllocHeader();
00528
00529
00530 alloc_block->set_size(alloc_size);
00531 alloc_block->set_allocated();
00532 alloc_block->set_prev_size(static_cast<int>(remainder));
00533 return alloc_block->ptr();
00534
00535 } else {
00536 free_block->set_allocated();
00537
00538 remove_free_alloc(free_block);
00539 return free_block->ptr();
00540 }
00541 }
00542
00543 bool
00544 MemoryPool::joinable_next(FreeHeader* freed)
00545 {
00546 AllocHeader* next_alloc = freed->next_adjacent();
00547 return freed->is_free() &&
00548 includes(next_alloc) &&
00549 next_alloc->is_free();
00550 }
00551
00552 bool
00553 MemoryPool::joinable_prev(FreeHeader* freed)
00554 {
00555 AllocHeader* prev_alloc = freed->prev_adjacent();
00556 return freed->is_free() &&
00557 includes(prev_alloc) &&
00558 prev_alloc->is_free();
00559 }
00560
00561 #ifdef VALIDATE_MEMORY_POOL
00562 void
00563 MemoryPool::validate_pool(MemoryPool& pool, bool log) {
00564 AllocHeader* prev = 0;
00565 size_t allocated_bytes = 0;
00566 size_t free_bytes = 0;
00567 size_t oh_bytes = 0;
00568 size_t free_count = 0;
00569 unsigned char* pool_end = pool.pool_ptr_ + pool.pool_size_;
00570 bool prev_was_free;
00571 size_t index = 0;
00572
00573 typedef std::map<FreeHeader*, int> FreeMap;
00574 FreeMap free_map;
00575
00576 AllocHeader* alloc = reinterpret_cast<AllocHeader*>(pool.pool_ptr_);
00577 while (pool.includes(alloc)) {
00578 FreeHeader* free_header = alloc->is_free() ?
00579 reinterpret_cast<FreeHeader*>(alloc) : NULL;
00580 if (free_header) {
00581 free_map[free_header] = index;
00582 }
00583 alloc = alloc->next_adjacent();
00584 ++index;
00585 }
00586
00587 index = 0;
00588 if (log) {
00589 ACE_OS::printf("Pool ptr %zx end %zx\n", (unsigned long)pool.pool_ptr_,
00590 (unsigned long)pool_end);
00591 }
00592
00593
00594 alloc = reinterpret_cast<AllocHeader*>(pool.pool_ptr_);
00595 while (pool.includes(alloc)) {
00596 if (log) {
00597
00598 int smlr_index = -1;
00599 int lrgr_index = -1;
00600 char lrgr_buff[32];
00601 char smlr_buff[32];
00602
00603 FreeHeader* free_header = alloc->is_free() ?
00604 reinterpret_cast<FreeHeader*>(alloc) : NULL;
00605 if (free_header) {
00606 FreeMap::const_iterator found;
00607 found = free_map.find(free_header->smaller_free(pool.pool_ptr_));
00608 if (found != free_map.end()) {
00609 smlr_index = found->second;
00610 snprintf(smlr_buff, 32, "[%2d]", smlr_index);
00611 }
00612 found = free_map.find(free_header->larger_free(pool.pool_ptr_));
00613 if (found != free_map.end()) {
00614 lrgr_index = found->second;
00615 snprintf(lrgr_buff, 32, "[%2d]", lrgr_index);
00616 }
00617 }
00618 ACE_OS::printf(
00619 "Alloc[%zu] %s at %zx ptr %zx lg %s sm %s size %d psize %d\n",
00620 index++,
00621 alloc->is_free() ?
00622 (alloc == pool.largest_free_ ? "FREE!" : "free ") : " ",
00623 (unsigned long)alloc,
00624 (unsigned long)alloc->ptr(),
00625 lrgr_index >= 0 ? lrgr_buff : "[ ]",
00626 smlr_index >= 0 ? smlr_buff : "[ ]",
00627 alloc->size(),
00628 alloc->prev_size()
00629 );
00630 }
00631
00632 TEST_CHECK(alloc->size());
00633 if (prev) {
00634 TEST_CHECK(prev->next_adjacent() == alloc);
00635 TEST_CHECK(alloc->prev_adjacent() == prev);
00636
00637 TEST_CHECK(!(prev_was_free && alloc->is_free()));
00638 }
00639
00640 if (!alloc->is_free()) {
00641 allocated_bytes += alloc->size();
00642 prev_was_free = false;
00643 } else {
00644 free_bytes += alloc->size();
00645 prev_was_free = true;
00646 }
00647 oh_bytes += sizeof(AllocHeader);
00648 prev = alloc;
00649 alloc = alloc->next_adjacent();
00650 }
00651 TEST_CHECK((unsigned char*)alloc == pool_end);
00652
00653 TEST_CHECK(allocated_bytes + free_bytes + oh_bytes == pool.pool_size_);
00654
00655 FreeIndex::validate_index(pool.free_index_, pool.pool_ptr_, log);
00656
00657 size_t prev_size = 0;
00658 size_t free_bytes_in_list = 0;
00659 FreeHeader* free_alloc = NULL;
00660 FreeHeader* prev_free = NULL;
00661
00662
00663 for (free_alloc = pool.largest_free_;
00664 free_alloc;
00665 free_alloc = free_alloc->smaller_free(pool.pool_ptr_)) {
00666
00667 TEST_CHECK(free_alloc->is_free());
00668
00669 TEST_CHECK(++free_count < 10000);
00670
00671
00672 free_bytes_in_list += free_alloc->size();
00673
00674
00675 if (prev_size) {
00676 TEST_CHECK(free_alloc->size() <= prev_size);
00677 TEST_CHECK(free_alloc->size() > 0);
00678 }
00679 prev_size = free_alloc->size();
00680 prev_free = free_alloc;
00681 }
00682
00683 TEST_CHECK(free_bytes == free_bytes_in_list);
00684
00685
00686 if (prev_free) {
00687 free_bytes_in_list = 0;
00688
00689 for (free_alloc = prev_free;
00690 free_alloc;
00691 free_alloc = free_alloc->larger_free(pool.pool_ptr_)) {
00692
00693 TEST_CHECK(free_alloc->is_free());
00694
00695
00696 free_bytes_in_list += free_alloc->size();
00697
00698
00699 if (free_alloc != prev_free) {
00700 TEST_CHECK(free_alloc->size() >= prev_size);
00701 TEST_CHECK(free_alloc->size() > 0);
00702 }
00703 prev_size = free_alloc->size();
00704 }
00705 TEST_CHECK(free_bytes == free_bytes_in_list);
00706 }
00707 }
00708 #endif
00709
00710 }}
00711
00712 OPENDDS_END_VERSIONED_NAMESPACE_DECL