RetroShare/libretroshare/src/util/smallobject.cc
Gioacchino Mazzurco 329050a9c2
Use safer rstime_t instead of time_t
Avoid problems to serialization on different platforms, without breaking
nested STL containers serialization.

The conversion have been made with sed, and checked with grep, plus
kdiff3 visual ispection, plus rutime tests, so it should be fine.
2018-10-07 13:07:06 +02:00

350 lines
9.6 KiB
C++

/*******************************************************************************
* libretroshare/src/util: smallobject.cc *
* *
* libretroshare: retroshare core library *
* *
* Copyright (c) 2011, Cyril Soler <csoler@users.sourceforge.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#include <iostream>
#include "smallobject.h"
#include "util/rsthreads.h"
#include "util/rsmemory.h"
using namespace RsMemoryManagement ;
RsMutex SmallObject::_mtx("SmallObject") ;
SmallObjectAllocator SmallObject::_allocator(RsMemoryManagement::MAX_SMALL_OBJECT_SIZE) ;
void Chunk::init(size_t blockSize,unsigned char blocks)
{
_data = new unsigned char[blockSize*blocks] ;
_firstAvailableBlock = 0 ;
_blocksAvailable = blocks ;
// Inits the first byte of each block to point to the next available block
//
unsigned char *p = _data ;
for(unsigned char i=0;i<blocks;p += blockSize)
*p = ++i ;
}
void Chunk::free()
{
delete[] _data ;
_data = NULL ;
}
void *Chunk::allocate(size_t blockSize)
{
if(!_blocksAvailable)
return NULL ;
assert(blockSize >= 2) ;
unsigned char *result = _data + _firstAvailableBlock*blockSize ;
// Grab id of next available block from the block being allocated.
// Always the case because the first available block is the first, so the next
// available block is given by *result.
//
_firstAvailableBlock = *result ;
--_blocksAvailable ;
return result ;
}
void Chunk::deallocate(void *p,size_t blockSize)
{
assert(p >= _data) ;
unsigned char *toRelease = static_cast<unsigned char *>(p) ;
// alignment check
assert( (toRelease - _data) % blockSize == 0 ) ;
*toRelease = _firstAvailableBlock ;
_firstAvailableBlock = static_cast<unsigned char>( (toRelease - _data)/blockSize) ;
// truncation check
assert(_firstAvailableBlock == (toRelease - _data)/blockSize);
++_blocksAvailable ;
}
void Chunk::printStatistics(int blockSize) const
{
std::cerr << " blocksAvailable : " << (int)_blocksAvailable << std::endl;
std::cerr << " firstBlockAvailable: " << (int)_firstAvailableBlock << std::endl;
std::cerr << " blocks : " << (void*)_data << " to " << (void*)(_data+BLOCKS_PER_CHUNK*blockSize) << std::endl;
}
FixedAllocator::FixedAllocator(size_t bytes)
{
_blockSize = bytes ;
_numBlocks = BLOCKS_PER_CHUNK ;
_allocChunk = -1 ;
_deallocChunk = -1 ;
}
FixedAllocator::~FixedAllocator()
{
for(uint32_t i=0;i<_chunks.size();++i)
{
_chunks[i]->free() ;
delete _chunks[i] ;
}
}
void *FixedAllocator::allocate()
{
if(_allocChunk < 0 || _chunks[_allocChunk]->_blocksAvailable == 0)
{
// find availabel memory in this chunk
//
uint32_t i ;
_allocChunk = -1 ;
for(i=0;i<_chunks.size();++i)
if(_chunks[i]->_blocksAvailable > 0) // found a chunk
{
_allocChunk = i ;
break ;
}
if( _allocChunk < 0 )
{
_chunks.reserve(_chunks.size()+1) ;
Chunk *newChunk = new Chunk;
if(newChunk == NULL)
{
std::cerr << "RsMemoryManagement: ran out of memory !" << std::endl;
exit(-1) ;
}
newChunk->init(_blockSize,_numBlocks) ;
_chunks.push_back(newChunk) ;
_allocChunk = _chunks.size()-1 ;
_deallocChunk = _chunks.size()-1 ;
}
}
assert(_chunks[_allocChunk] != NULL) ;
assert(_chunks[_allocChunk]->_blocksAvailable > 0) ;
return _chunks[_allocChunk]->allocate(_blockSize) ;
}
void FixedAllocator::deallocate(void *p)
{
if(_deallocChunk < 0 || !chunkOwnsPointer(*_chunks[_deallocChunk],p))
{
// find the chunk that contains this pointer. Perform a linear search.
_deallocChunk = -1 ;
for(uint32_t i=0;i<_chunks.size();++i)
if(chunkOwnsPointer(*_chunks[i],p))
{
_deallocChunk = i ;
break ;
}
}
assert(_chunks[_deallocChunk] != NULL) ;
_chunks[_deallocChunk]->deallocate(p,_blockSize) ;
if(_chunks[_deallocChunk]->_blocksAvailable == BLOCKS_PER_CHUNK)
{
_chunks[_deallocChunk]->free() ;
delete _chunks[_deallocChunk] ;
_chunks[_deallocChunk] = _chunks.back() ;
if(_allocChunk == _deallocChunk) _allocChunk = -1 ;
if(_allocChunk == ((int)_chunks.size())-1) _allocChunk = _deallocChunk ;
_deallocChunk = -1 ;
_chunks.pop_back();
}
}
uint32_t FixedAllocator::currentSize() const
{
uint32_t res = 0 ;
for(uint32_t i=0;i<_chunks.size();++i)
res += (_numBlocks - _chunks[i]->_blocksAvailable) * _blockSize ;
return res ;
}
void FixedAllocator::printStatistics() const
{
std::cerr << " numBLocks=" << (int)_numBlocks << std::endl;
std::cerr << " blockSize=" << (int)_blockSize << std::endl;
std::cerr << " Number of chunks: " << _chunks.size() << std::endl;
for(uint32_t i=0;i<_chunks.size();++i)
_chunks[i]->printStatistics(_blockSize) ;
}
SmallObjectAllocator::SmallObjectAllocator(size_t maxObjectSize)
: _maxObjectSize(maxObjectSize)
{
RsStackMutex m(SmallObject::_mtx) ;
_lastAlloc = NULL ;
_lastDealloc = NULL ;
_active = true ;
}
SmallObjectAllocator::~SmallObjectAllocator()
{
RsStackMutex m(SmallObject::_mtx) ;
//std::cerr << __PRETTY_FUNCTION__ << " not deleting. Leaving it to the system." << std::endl;
_active = false ;
uint32_t still_allocated = 0 ;
for(std::map<int,FixedAllocator*>::const_iterator it(_pool.begin());it!=_pool.end();++it)
still_allocated += it->second->currentSize() ;
//delete it->second ;
std::cerr << "Memory still in use at end of program: " << still_allocated << " bytes." << std::endl;
}
void *SmallObjectAllocator::allocate(size_t bytes)
{
if(bytes > _maxObjectSize)
return rs_malloc(bytes) ;
else if(_lastAlloc != NULL && _lastAlloc->blockSize() == bytes)
return _lastAlloc->allocate() ;
else
{
std::map<int,FixedAllocator*>::iterator it(_pool.find(bytes)) ;
if(it == _pool.end())
{
_pool[bytes] = new FixedAllocator(bytes) ;
it = _pool.find(bytes) ;
}
_lastAlloc = it->second ;
return it->second->allocate() ;
}
}
void SmallObjectAllocator::deallocate(void *p,size_t bytes)
{
if(bytes > _maxObjectSize)
free(p) ;
else if(_lastDealloc != NULL && _lastDealloc->blockSize() == bytes)
_lastDealloc->deallocate(p) ;
else
{
std::map<int,FixedAllocator*>::iterator it(_pool.find(bytes)) ;
if(it == _pool.end())
{
_pool[bytes] = new FixedAllocator(bytes) ;
it = _pool.find(bytes) ;
}
it->second->deallocate(p) ;
_lastDealloc = it->second ;
}
}
void SmallObjectAllocator::printStatistics() const
{
std::cerr << "RsMemoryManagement Statistics:" << std::endl;
std::cerr << " Total Fixed-size allocators: " << _pool.size() << std::endl;
std::cerr << "Pool" << std::endl;
for(std::map<int,FixedAllocator*>::const_iterator it(_pool.begin());it!=_pool.end();++it)
{
std::cerr << " Allocator for size " << it->first << " : " << std::endl;
std::cerr << " Last Alloc: " << _lastAlloc << std::endl;
std::cerr << " Last Dealloc: " << _lastDealloc << std::endl;
it->second->printStatistics() ;
}
}
void *SmallObject::operator new(size_t size)
{
#ifdef DEBUG_MEMORY
bool print=false ;
{
RsStackMutex m(_mtx) ;
static rstime_t last_time = 0 ;
rstime_t now = time(NULL) ;
if(now > last_time + 20)
{
last_time = now ;
print=true ;
}
}
if(print)
printStatistics() ;
#endif
RsStackMutex m(_mtx) ;
// This should normally not happen. But that prevents a crash when quitting, since we cannot prevent the constructor
// of an object to call operator new(), nor to handle the case where it returns NULL.
// The memory will therefore not be deleted if that happens. We thus print a warning.
if(_allocator._active)
return _allocator.allocate(size) ;
else
{
std::cerr << "(EE) allocating " << size << " bytes of memory that cannot be deleted. This is a bug, except if it happens when closing Retroshare" << std::endl;
return malloc(size) ;
}
#ifdef DEBUG_MEMORY
std::cerr << "new RsItem: " << p << ", size=" << size << std::endl;
#endif
}
void SmallObject::operator delete(void *p,size_t size)
{
RsStackMutex m(_mtx) ;
if(!_allocator._active)
return ;
_allocator.deallocate(p,size) ;
#ifdef DEBUG_MEMORY
std::cerr << "del RsItem: " << p << ", size=" << size << std::endl;
#endif
}
void SmallObject::printStatistics()
{
RsStackMutex m(_mtx) ;
if(!_allocator._active)
return ;
_allocator.printStatistics() ;
}
void RsMemoryManagement::printStatistics()
{
SmallObject::printStatistics();
}