179 lines
4.5 KiB
C++
179 lines
4.5 KiB
C++
/**
|
|
* PANDA 3D SOFTWARE
|
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
*
|
|
* All use of this software is subject to the terms of the revised BSD
|
|
* license. You should have received a copy of this license along
|
|
* with this source code in a file named "LICENSE."
|
|
*
|
|
* @file simpleAllocator.cxx
|
|
* @author drose
|
|
* @date 2007-05-12
|
|
*/
|
|
|
|
#include "simpleAllocator.h"
|
|
|
|
/**
|
|
*
|
|
*/
|
|
SimpleAllocator::
|
|
~SimpleAllocator() {
|
|
// We're shutting down. Force-free everything remaining.
|
|
if (_next != (LinkedListNode *)this) {
|
|
MutexHolder holder(_lock);
|
|
while (_next != (LinkedListNode *)this) {
|
|
nassertv(_next != (LinkedListNode *)NULL);
|
|
((SimpleAllocatorBlock *)_next)->do_free();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
void SimpleAllocator::
|
|
output(ostream &out) const {
|
|
MutexHolder holder(_lock);
|
|
out << "SimpleAllocator, " << _total_size << " of " << _max_size
|
|
<< " allocated";
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
void SimpleAllocator::
|
|
write(ostream &out) const {
|
|
MutexHolder holder(_lock);
|
|
out << "SimpleAllocator, " << _total_size << " of " << _max_size
|
|
<< " allocated";
|
|
|
|
SimpleAllocatorBlock *block = (SimpleAllocatorBlock *)_next;
|
|
while (block->_next != this) {
|
|
SimpleAllocatorBlock *next = (SimpleAllocatorBlock *)block->_next;
|
|
|
|
out << " " << *block << "\n";
|
|
block = next;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Allocates a new block. Returns NULL if a block of the requested size
|
|
* cannot be allocated.
|
|
*
|
|
* To free the allocated block, call block->free(), or simply delete the block
|
|
* pointer.
|
|
*
|
|
* Assumes the lock is already held.
|
|
*/
|
|
SimpleAllocatorBlock *SimpleAllocator::
|
|
do_alloc(size_t size) {
|
|
if (size > _contiguous) {
|
|
// Don't even bother.
|
|
return NULL;
|
|
}
|
|
|
|
// First fit algorithm: walk through all the empty blocks until we find one
|
|
// that has enough room.
|
|
|
|
SimpleAllocatorBlock *block = NULL;
|
|
size_t end = 0;
|
|
size_t best = 0;
|
|
if (_next != this) {
|
|
// We have at least one allocated block.
|
|
block = (SimpleAllocatorBlock *)_next;
|
|
end = block->_start + block->_size;
|
|
|
|
// Scan until we have reached the last allocated block.
|
|
while (block->_next != this) {
|
|
SimpleAllocatorBlock *next = (SimpleAllocatorBlock *)block->_next;
|
|
size_t free_size = next->_start - end;
|
|
if (size <= free_size) {
|
|
SimpleAllocatorBlock *new_block = make_block(end, size);
|
|
nassertr(new_block->get_allocator() == this, NULL);
|
|
|
|
new_block->insert_before(next);
|
|
_total_size += size;
|
|
|
|
if (_max_size - _total_size < _contiguous) {
|
|
// Since we only have (_max_size - _total_size) bytes remaining, it
|
|
// follows that our largest contiguous block must be no larger than
|
|
// this.
|
|
_contiguous = _max_size - _total_size;
|
|
changed_contiguous();
|
|
}
|
|
return new_block;
|
|
}
|
|
if (free_size > best) {
|
|
best = free_size;
|
|
}
|
|
|
|
block = next;
|
|
end = block->_start + block->_size;
|
|
}
|
|
}
|
|
|
|
// No free blocks; check for room at the end.
|
|
size_t free_size = _max_size - end;
|
|
if (size <= free_size) {
|
|
SimpleAllocatorBlock *new_block = make_block(end, size);
|
|
nassertr(new_block->get_allocator() == this, NULL);
|
|
|
|
new_block->insert_before(this);
|
|
_total_size += size;
|
|
|
|
if (_max_size - _total_size < _contiguous) {
|
|
// Since we only have (_max_size - _total_size) bytes remaining, it
|
|
// follows that our largest contiguous block must be no larger than
|
|
// this.
|
|
_contiguous = _max_size - _total_size;
|
|
changed_contiguous();
|
|
}
|
|
return new_block;
|
|
}
|
|
|
|
if (free_size > best) {
|
|
best = free_size;
|
|
}
|
|
|
|
// Now that we've walked through the entire list of blocks, we really do
|
|
// know accurately what the largest contiguous block is.
|
|
if (_contiguous != best) {
|
|
_contiguous = best;
|
|
changed_contiguous();
|
|
}
|
|
|
|
// No room for this block.
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Creates a new SimpleAllocatorBlock object. Override this function to
|
|
* specialize the block type returned.
|
|
*/
|
|
SimpleAllocatorBlock *SimpleAllocator::
|
|
make_block(size_t start, size_t size) {
|
|
return new SimpleAllocatorBlock(this, start, size);
|
|
}
|
|
|
|
/**
|
|
* This callback function is made whenever the estimate of contiguous
|
|
* available space changes, either through an alloc or free. The lock will be
|
|
* held.
|
|
*/
|
|
void SimpleAllocator::
|
|
changed_contiguous() {
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
void SimpleAllocatorBlock::
|
|
output(ostream &out) const {
|
|
if (_allocator == (SimpleAllocator *)NULL) {
|
|
out << "free block\n";
|
|
} else {
|
|
MutexHolder holder(_allocator->_lock);
|
|
out << "block of size " << _size << " at " << _start;
|
|
}
|
|
}
|