commsfwutils/commsbufs/version1/mbufmgr/src/MBufPoolChain.cpp
author hgs
Mon, 24 May 2010 18:38:45 +0100
changeset 31 f5b12b673c07
parent 0 dfb7c4ff071f
permissions -rw-r--r--
201019_02

// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// Maintains a free queue of single size RMBuf's on behalf of CMBufPoolManager
// 
//

/**
 @file
 @internalComponent
*/

#include "MBufPoolChain.h"
#include "MBufPool.h"
#include "MBufPoolManager.h"

RMBufPoolChain::RMBufPoolChain(TInt aMBufSize, CMBufPoolManager& aMBufPoolManager)
	: iMBufSize(aMBufSize), iInitialAllocation(0), iMBufPoolManager(aMBufPoolManager)
	{
	}

RMBufPoolChain::RMBufPoolChain(TInt aMBufSize, TInt aInitialAllocation, TInt aMinGrowth, TInt aGrowthThreshold, CMBufPoolManager& aMBufPoolManager)
    : iMBufSize(aMBufSize), iInitialAllocation(aInitialAllocation), iMinGrowth(aMinGrowth), iGrowthThreshold(aGrowthThreshold), iMBufPoolManager(aMBufPoolManager)
	{
	iLenFree = 0;
	iLenTotal = 0;
    iFreeMBufs.Init();
    iPools.Reset();

	iSignalled = EFalse;
	iNumMBufsToAdd = 0;
	iNoMoreSpace = 0;
	iLastGrowth = 0;
	iMaxGrowthAttempt = 0;

	iDbgPoolLimit = 0;

	iPools.SetOffset(_FOFF(CMBufPool, iLink));
	}

void RMBufPoolChain::OpenL()
	{
	User::LeaveIfError(iLockFreeList.CreateLocal());
	iPools.Reset();
	iPools.SetOffset(_FOFF(CMBufPool, iLink));
	}

void RMBufPoolChain::Close()
	{
	iLockFreeList.Close();
	}

// get the growth amount
TInt RMBufPoolChain::GrowthSize(TInt aSize)
{
	// small request so grow by minimum size
	if (aSize < iMinGrowth)
		{
		return iMinGrowth;
		}

	// Have we tried as big as this in the past?
	if ( (iMaxGrowthAttempt > 0) && (aSize >= iMaxGrowthAttempt) )
		{
		// try smaller size again
		aSize = iMaxGrowthAttempt;
		}

	// Attempt to grow the pool by requested amount
	if (!iNoMoreSpace)
		{
		return aSize;
		}

	// Last growth attempt failed so check if its worth
	// trying again
	if ( iLastGrowth < (iMinGrowth + 2))
		{
		return 0;
		}

	// Last growth attempt failed so lets try a smaller
	iMaxGrowthAttempt = iMinGrowth + (iLastGrowth - iMinGrowth) / 2;

	return iMaxGrowthAttempt;
}

// merge new pool into the pool chain
void RMBufPoolChain::ExtendFreeList(CMBufPool* aPool)
	{
	// The caller must already have acquired the freelist lock
	// - justification;
	//   a. limitation in RWorkerLock's RFastLock which does not support nested requests by the same thread without hanging the thread
	//   b. performance is adversely affected by continuoulsy locking & releasing the lock by the caller, refer ::Alloc
	// - assumption is checked for debug builds, but not for release builds as it is too expensive & indicates a serous coding error that
	//   could/should of been fixed within a debug build anyhow
	__ASSERT_DEBUG(iLockFreeList.IsOwned(), CMBufManager::Panic(EMBuf_FreeLockNotOwned));

	iFreeMBufs.Append(aPool->MBufQ());
    iLenFree += aPool->Count() * iMBufSize;
    iLenTotal += aPool->Count() * iMBufSize;
	}

// allocate a chain of MBufs from the pool chain
TInt RMBufPoolChain::Alloc(RMBufQ& aList, TInt aSize, TBool aIsAllocPool)
	{

	// Lock the freelist so that the transfer of mbufs and statistics update is an atomic operation
	iLockFreeList.Wait();

	TInt transfered = iFreeMBufs.Transfer(aList, aSize);
	iLenFree -= transfered;

	// If necessary gather data from the poolChain within the freelist lock to ensure integrity of statistics used
	TInt preAllocationLength = 0;
	if (aIsAllocPool)
		{
		preAllocationLength = iGrowthThreshold - (iLenFree/iMBufSize); // determine in terms of number of mbufs.
		}

	// adjust the growth if necessary
	TInt growth = 0;
	if (preAllocationLength >= 0)
		{
		growth = GrowthSize(preAllocationLength); // determine in terms of number of mbufs.
		}

	// Now free the freelist lock
	iLockFreeList.Signal();

	// check if the growth threshold trigger has been exceeded, and if so perform a background pool (pre)allocation
	if (aIsAllocPool)
		{
		if (preAllocationLength >= 0)
			{
			// a failed background (pre)allocation shall not error the user since they shouldn't know (and hence care), thus
			// the return code is only used to avoid a perpetual loop
			if (growth > 0)
				{
				if (KErrNoMBufs == iMBufPoolManager.AllocPool(*this, growth, EFalse))
					{
					iMaxGrowthAttempt = growth;
					}
				}
			}
		}
	return transfered;
	}

// compare function to find the suitable pool chain
TInt RMBufPoolChain::Compare(const TInt* aMBufSize, const RMBufPoolChain& aRHSPoolChain)
	{
	if (*aMBufSize < aRHSPoolChain.iMBufSize)
		{
		return -1;
		}
	else if (*aMBufSize > aRHSPoolChain.iMBufSize)
		{
		return 1;
		}
	return 0;
	}

TInt RMBufPoolChain::Compare(const RMBufPoolChain& aLHSPoolChain, const RMBufPoolChain& aRHSPoolChain)
	{
	return Compare((TInt*)&(aLHSPoolChain.iMBufSize), aRHSPoolChain);
	}

CMBufPoolManager& RMBufPoolChain::MBufPoolManager()
	{
	return iMBufPoolManager;
	}