kerneltest/e32test/system/t_condvar2.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "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:
// e32test\misc\t_condvar2.cpp
// 
//

#define __E32TEST_EXTENSION__

#include <e32base.h>
#include <e32base_private.h>
#include <e32test.h>
#include <e32svr.h>
#include "u32std.h"
#include "../misc/prbs.h"
#include "../mmu/freeram.h"

const TInt KStackSize=0x1000;

RTest test(_L("T_CONDVAR2"));

const TInt KPacketSize = 1024;
const TInt KCrcSize = KPacketSize;
struct SMessage
	{
	TDblQueLink iLink;
	TUint	iSeq;
	TUint	iData[KPacketSize/4];
	TUint	iCrc;
	};

TDblQue<SMessage> Queue(0);
TUint PSeq;
TUint CSeq;
RHeap* SharedHeap;
RMutex Mutex;
RCondVar CV;
TInt NThreads;

class CThread : public CActive
	{
public:
	CThread(TInt aPriority);
	~CThread();
	virtual void RunL();
	virtual void DoCancel();
	virtual void DisplayStats()=0;
	TInt Start();
public:
	static TInt ThreadFunc(TAny* aPtr);
public:
	virtual TInt StartThread()=0;
	virtual TInt RunThread()=0;
public:
	RThread iThread;
	TInt iInnerCount;
	TInt iOuterCount;
	TInt iSuspendCount;
	TInt iExitCount;
	TInt iTerminateCount;
	TInt iCvCloseCount;
	TBool iProducer;
	TInt iId;
	TBool iCritical;
	TAny* iTempAlloc;
	};

class CProducerThread : public CThread
	{
public:
	static void NewL(TInt aId);
	CProducerThread(TInt aId);
	virtual TInt StartThread();
	virtual TInt RunThread();
	virtual void DisplayStats();
	};

class CConsumerThread : public CThread
	{
public:
	static void NewL(TInt aId);
	CConsumerThread(TInt aId);
	virtual TInt StartThread();
	virtual TInt RunThread();
	virtual void DisplayStats();
	};

class CRandomTimer : public CActive
	{
public:
	static void NewL();
	CRandomTimer(TInt aPriority);
	~CRandomTimer();
	virtual void RunL();
	virtual void DoCancel();
	void Start();
public:
	RTimer iTimer;
	TUint iSeed[2];
	TInt iCount;
	TInt iBackOff;
	TInt iZeroHandle;
	CThread* iSuspended;
	};

class CStatsTimer : public CActive
	{
public:
	static void NewL();
	CStatsTimer(TInt aPriority);
	~CStatsTimer();
	virtual void RunL();
	virtual void DoCancel();
	void Start();
public:
	RTimer iTimer;
	TInt iInitFreeRam;
	TInt iMaxDelta;
	TInt iCount;
	};

const TInt KNumProducers=4;
CProducerThread* TheProducers[KNumProducers];
const TInt KNumConsumers=4;
CConsumerThread* TheConsumers[KNumConsumers];
CRandomTimer* TheRandomTimer;

CThread::CThread(TInt aPriority)
	: CActive(aPriority)
	{
	}

CThread::~CThread()
	{
	Cancel();
	iThread.Kill(0);
	iThread.Close();
	}

TInt StartAllThreads()
	{
	TInt i;
	TInt r = CV.CreateLocal();
	if (r!=KErrNone)
		return r;
	for (i=0; i<KNumConsumers; ++i)
		{
		r = TheConsumers[i]->Start();
		if (r!=KErrNone)
			return r;
		}
	for (i=0; i<KNumProducers; ++i)
		{
		r = TheProducers[i]->Start();
		if (r!=KErrNone)
			return r;
		}
	return KErrNone;
	}

void CThread::RunL()
	{
	TExitType exitType = iThread.ExitType();
	TInt exitReason = iThread.ExitReason();
	const TDesC& exitCat = iThread.ExitCategory();
	TBool bad=EFalse;
	if (exitType==EExitKill)
		{
		if (exitReason!=KErrNone && exitReason!=KErrGeneral)
			bad=ETrue;
		}
	else if (exitType==EExitPanic)
		{
		if (exitCat!=_L("KERN-EXEC") || exitReason!=0 || CV.Handle()!=0)
			bad=ETrue;
		else
			++iCvCloseCount;
		}
	if (bad)
		{
		TFullName n(iThread.FullName());
		if (iProducer)
			test.Printf(_L("Thread %S (P%1d) exited %d,%d,%S\n"),&n,iId,exitType,exitReason,&exitCat);
		else
			test.Printf(_L("Thread %S (C%1d) exited %d,%d,%S\n"),&n,iId,exitType,exitReason,&exitCat);
		CActiveScheduler::Stop();
		return;
		}
	CLOSE_AND_WAIT(iThread);
	if (exitType==EExitTerminate)
		++iTerminateCount;
	else if (exitType==EExitKill && exitReason==KErrNone)
		++iExitCount;
	else if (exitType==EExitKill && exitReason==KErrGeneral)
		++iCvCloseCount;
	--NThreads;
	if (iTempAlloc)
		{
		SharedHeap->Free(iTempAlloc);
		iTempAlloc = NULL;
		}
	TInt r;
	if (CV.Handle()==0)
		{
		if (NThreads==0)
			r = StartAllThreads();
		else
			return;
		}
	else
		r=Start();
	if (r!=KErrNone)
		{
		test.Printf(_L("Start thread error %d\n"),r);
		CActiveScheduler::Stop();
		}
	}

void CThread::DoCancel()
	{
	iThread.LogonCancel(iStatus);
	}

TInt CThread::Start()
	{
	TInt r;
	FOREVER
		{
		r=StartThread();
		if (r==KErrNone)
			break;
		if (r!=KErrAlreadyExists)
			break;
		User::After(100000);
		}
	if (r==KErrNone)
		{
		iThread.Logon(iStatus);
		SetActive();
		}
	++NThreads;
	return r;
	}

TInt CThread::ThreadFunc(TAny* aPtr)
	{
	return ((CThread*)aPtr)->RunThread();
	}

CConsumerThread::CConsumerThread(TInt aId)
	: CThread(0)
	{
	iId = aId;
	}

void CConsumerThread::NewL(TInt aId)
	{
	CConsumerThread* pT=new (ELeave) CConsumerThread(aId);
	TheConsumers[aId] = pT;
	CActiveScheduler::Add(pT);
	User::LeaveIfError(pT->Start());
	}

TInt CConsumerThread::StartThread()
	{
	TInt r=iThread.Create(KNullDesC(), &ThreadFunc, KStackSize, SharedHeap, this);	// use unnamed thread
	if (r!=KErrNone)
		return r;
	iThread.Resume();
	return KErrNone;
	}

void CConsumerThread::DisplayStats()
	{
	test.Printf(_L("C%1d: I:%9d O:%9d S:%9d T:%9d C:%9d\n"), iId, iInnerCount, iOuterCount, iSuspendCount, iTerminateCount, iCvCloseCount);
	}

TInt CConsumerThread::RunThread()
	{
	Mutex.Wait();
	TInt r = KErrNone;
	FOREVER
		{
		while (Queue.IsEmpty())
			{
			r = CV.Wait(Mutex);
			++iInnerCount;
			if (r!=KErrNone)
				return r;
			}
		++iOuterCount;
		iCritical = ETrue;
		SMessage* m = Queue.First();
		m->iLink.Deque();
		iTempAlloc = m;
		TBool seq_ok = (m->iSeq == CSeq++);
		iCritical = EFalse;
		Mutex.Signal();
		if (!seq_ok)
			return KErrCorrupt;
		TUint16 crc = 0;
		Mem::Crc(crc, m->iData, KCrcSize);
		if (crc != m->iCrc)
			return KErrCorrupt;
		iCritical = ETrue;
		iTempAlloc = NULL;
		User::Free(m);
		iCritical = EFalse;
		Mutex.Wait();
		}
	}

CProducerThread::CProducerThread(TInt aId)
	: CThread(0)
	{
	iId = aId;
	}

void CProducerThread::NewL(TInt aId)
	{
	CProducerThread* pT=new (ELeave) CProducerThread(aId);
	TheProducers[aId] = pT;
	CActiveScheduler::Add(pT);
	User::LeaveIfError(pT->Start());
	}

TInt CProducerThread::StartThread()
	{
	TInt r=iThread.Create(KNullDesC(), &ThreadFunc, KStackSize, SharedHeap, this);	// use unnamed thread
	if (r!=KErrNone)
		return r;
	iThread.Resume();
	return KErrNone;
	}

void CProducerThread::DisplayStats()
	{
	test.Printf(_L("P%1d: I:%9d O:%9d S:%9d T:%9d C:%9d\n"), iId, iInnerCount, iOuterCount, iSuspendCount, iTerminateCount, iCvCloseCount);
	}

TInt CProducerThread::RunThread()
	{
	TUint seed[2];
	seed[0] = User::TickCount();
	seed[1] = 0;
	FOREVER
		{
		iCritical = ETrue;
		SMessage* m = new SMessage;
		iTempAlloc = m;
		iCritical = EFalse;
		TInt i = 0;
		for (; i<KPacketSize/4; ++i)
			m->iData[i] = Random(seed);
		TUint16 crc = 0;
		Mem::Crc(crc, m->iData, KCrcSize);
		m->iCrc = crc;
		Mutex.Wait();
		iCritical = ETrue;
		m->iSeq = PSeq++;
		Queue.AddLast(*m);
		iTempAlloc = NULL;
		iCritical = EFalse;
		CV.Signal();
		Mutex.Signal();
		++iOuterCount;
		if (!(Random(seed)&1))
			User::AfterHighRes(1000);
		}
	}

void CRandomTimer::NewL()
	{
	CRandomTimer* pR=new (ELeave) CRandomTimer(20);
	User::LeaveIfError(pR->iTimer.CreateLocal());
	CActiveScheduler::Add(pR);
	TheRandomTimer=pR;
	pR->Start();
	}

CRandomTimer::CRandomTimer(TInt aPriority)
	: CActive(aPriority)
	{
	iSeed[0]=User::TickCount();
	}

CRandomTimer::~CRandomTimer()
	{
	Cancel();
	iTimer.Close();
	}

void CRandomTimer::RunL()
	{
	++iCount;
	FOREVER
		{
		TUint x=Random(iSeed)&511;
		TInt tn=(TInt)(Random(iSeed) % (KNumConsumers + KNumProducers));
		CThread* pT = (tn>=KNumConsumers) ? (CThread*)TheProducers[tn-KNumConsumers] : (CThread*)TheConsumers[tn];
		if (x==511)
			{
			CV.Close();
			if (iSuspended)
				{
				if (iSuspended->iThread.Handle())
					iSuspended->iThread.Resume();
				iSuspended = NULL;
				}
			break;
			}
		if (pT->iThread.Handle()==0)
			{
			++iZeroHandle;
			continue;
			}
		if (x>=500)
			{
			if (pT->iCritical)
				{
				++iBackOff;
				continue;
				}
			pT->iThread.Terminate(0);
			if (iSuspended == pT)
				iSuspended = NULL;
			break;
			}
		if (iSuspended && (x&1))
			{
			if (iSuspended->iThread.Handle())
				iSuspended->iThread.Resume();
			iSuspended = NULL;
			}
		if (!iSuspended && !(x&15))
			{
			iSuspended = pT;
			pT->iThread.Suspend();
			++pT->iSuspendCount;
			}
		TThreadPriority tp;
		if (x>=400)
			tp = EPriorityMuchMore;
		else if (x>=300)
			tp = EPriorityMore;
		else if (x>=200)
			tp = EPriorityNormal;
		else if (x>=100)
			tp = EPriorityLess;
		else
			tp = EPriorityMuchLess;
		pT->iThread.SetPriority(tp);
		break;
		}
	Start();
	}

void CRandomTimer::Start()
	{
	TUint x=Random(iSeed)&15;
	x+=1;
	iTimer.HighRes(iStatus, x*1000);
	SetActive();
	}

void CRandomTimer::DoCancel()
	{
	iTimer.Cancel();
	}

void CStatsTimer::NewL()
	{
	CStatsTimer* pT=new (ELeave) CStatsTimer(-10);
	User::LeaveIfError(pT->iTimer.CreateLocal());
	CActiveScheduler::Add(pT);
	pT->Start();
	}

CStatsTimer::CStatsTimer(TInt aPriority)
	: CActive(aPriority)
	{
	iInitFreeRam = FreeRam();
	}

CStatsTimer::~CStatsTimer()
	{
	Cancel();
	iTimer.Close();
	}

void CStatsTimer::RunL()
	{
	TInt i;
	for (i=0; i<KNumProducers; i++)
		TheProducers[i]->DisplayStats();
	for (i=0; i<KNumConsumers; i++)
		TheConsumers[i]->DisplayStats();
	test.Printf(_L("RndTm: %9d BO: %9d ZH: %9d\n"), TheRandomTimer->iCount, TheRandomTimer->iBackOff, TheRandomTimer->iZeroHandle);
	TInt free_ram = FreeRam();
	TInt delta_ram = iInitFreeRam - free_ram;
	if (delta_ram > iMaxDelta)
		iMaxDelta = delta_ram;
	if (++iCount==10)
		{
		test.Printf(_L("Max RAM delta %dK Free RAM %08x\n"), iMaxDelta/1024, free_ram);
		iCount=0;
		}
	Start();
	}

void CStatsTimer::Start()
	{
	iTimer.After(iStatus, 1000000);
	SetActive();
	}

void CStatsTimer::DoCancel()
	{
	iTimer.Cancel();
	}

_LIT(KSharedHeap, "SharedHeap");
void InitialiseL()
	{
	PSeq = 0;
	CSeq = 0;
	User::LeaveIfError(Mutex.CreateLocal());
	User::LeaveIfError(CV.CreateLocal());
	User::LeaveIfNull(SharedHeap = UserHeap::ChunkHeap(&KSharedHeap, 0x1000, 0x01000000));
	CActiveScheduler* pA=new (ELeave) CActiveScheduler;
	CActiveScheduler::Install(pA);
	TInt id;
	for (id=0; id<KNumConsumers; ++id)
		CConsumerThread::NewL(id);
	for (id=0; id<KNumProducers; ++id)
		CProducerThread::NewL(id);
	CRandomTimer::NewL();
	CStatsTimer::NewL();
	}

GLDEF_C TInt E32Main()
//
// Test timers.
//
	{

	test.Title();

	RThread().SetPriority(EPriorityAbsoluteHigh);

	TRAPD(r,InitialiseL());
	test(r==KErrNone);

	User::SetJustInTime(EFalse);

	CActiveScheduler::Start();

	test(0);

	return(0);
	}