⛏️ index : haiku.git

/*
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
 * Distributed under the terms of the MIT License.
 */


#include <package/hpkg/BlockBufferPoolImpl.h>

#include <algorithm>
#include <new>

#include <AutoLocker.h>

#include <package/hpkg/PoolBuffer.h>


namespace BPackageKit {

namespace BHPKG {

namespace BPrivate {


// #pragma mark - BlockBufferPoolImpl


BlockBufferPoolImpl::BlockBufferPoolImpl(size_t blockSize,
	uint32 maxCachedBlocks, BBufferPoolLockable* lockable)
	:
	fBlockSize(blockSize),
	fMaxCachedBlocks(maxCachedBlocks),
	fAllocatedBlocks(0),
	fLockable(lockable)
{
}


BlockBufferPoolImpl::~BlockBufferPoolImpl()
{
	// delete all cached blocks
	while (PoolBuffer* block = fCachedBuffers.RemoveHead())
		delete block;

	while (PoolBuffer* block = fUnusedBuffers.RemoveHead())
		delete block;
}


status_t
BlockBufferPoolImpl::Init()
{
	return B_OK;
}


PoolBuffer*
BlockBufferPoolImpl::GetBuffer(size_t size, PoolBuffer** owner, bool* _newBuffer)
{
	// for sizes greater than the block size, we always allocate a new buffer
	if (size > fBlockSize)
		return _AllocateBuffer(size, owner, _newBuffer);

	AutoLocker<BBufferPoolLockable> locker(fLockable);

	// if an owner is given and the buffer is still cached, return it
	if (owner != NULL && *owner != NULL) {
		PoolBuffer* buffer = *owner;
		fCachedBuffers.Remove(buffer);

		if (_newBuffer != NULL)
			*_newBuffer = false;
		return buffer;
	}

	// we need a new buffer -- try unused ones first
	PoolBuffer* buffer = fUnusedBuffers.RemoveHead();
	if (buffer != NULL) {
		buffer->SetOwner(owner);

		if (owner != NULL)
			*owner = buffer;
		if (_newBuffer != NULL)
			*_newBuffer = true;
		return buffer;
	}

	// if we have already hit the max block limit, steal a cached block
	if (fAllocatedBlocks >= fMaxCachedBlocks) {
		buffer = fCachedBuffers.RemoveHead();
		if (buffer != NULL) {
			buffer->SetCached(false);
			*buffer->Owner() = NULL;
			buffer->SetOwner(owner);

			if (owner != NULL)
				*owner = buffer;
			if (_newBuffer != NULL)
				*_newBuffer = true;
			return buffer;
		}
	}

	// allocate a new buffer
	locker.Unlock();
	return _AllocateBuffer(size, owner, _newBuffer);
}


void
BlockBufferPoolImpl::PutBufferAndCache(PoolBuffer** owner)
{
	PoolBuffer* buffer = *owner;

	// always delete buffers with non-standard size
	if (buffer->Size() != fBlockSize) {
		*owner = NULL;
		delete buffer;
		return;
	}

	AutoLocker<BBufferPoolLockable> locker(fLockable);

	// queue the cached buffer
	buffer->SetOwner(owner);
	fCachedBuffers.Add(buffer);
	buffer->SetCached(true);

	if (fAllocatedBlocks > fMaxCachedBlocks) {
		// We have exceeded the limit -- we need to free a buffer.
		PoolBuffer* otherBuffer = fUnusedBuffers.RemoveHead();
		if (otherBuffer == NULL) {
			otherBuffer = fCachedBuffers.RemoveHead();
			*otherBuffer->Owner() = NULL;
			otherBuffer->SetCached(false);
		}

		delete otherBuffer;
	}
}


void
BlockBufferPoolImpl::PutBuffer(PoolBuffer** owner)
{
	AutoLocker<BBufferPoolLockable> locker(fLockable);

	PoolBuffer* buffer = *owner;

	if (buffer == NULL)
		return;

	if (buffer->IsCached()) {
		fCachedBuffers.Remove(buffer);
		buffer->SetCached(false);
	}

	buffer->SetOwner(NULL);
	*owner = NULL;

	if (buffer->Size() == fBlockSize && fAllocatedBlocks < fMaxCachedBlocks)
		fUnusedBuffers.Add(buffer);
	else
		delete buffer;
}


PoolBuffer*
BlockBufferPoolImpl::_AllocateBuffer(size_t size, PoolBuffer** owner,
	bool* _newBuffer)
{
	PoolBuffer* buffer = new(std::nothrow) PoolBuffer(
		std::max(size, fBlockSize));
	if (buffer == NULL || buffer->Buffer() == NULL) {
		delete buffer;
		return NULL;
	}

	buffer->SetOwner(owner);

	if (_newBuffer != NULL)
		*_newBuffer = true;

	AutoLocker<BBufferPoolLockable> locker(fLockable);
	fAllocatedBlocks++;

	if (owner != NULL)
		*owner = buffer;

	return buffer;
}


}	// namespace BPrivate

}	// namespace BHPKG

}	// namespace BPackageKit