kerneltest/e32test/smp_demo/smp_demo.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 6 0173bcd7697c
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) 2006-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\smp_demo\smp_demo.cpp
// Demonstration for SMP
// 
//

#include <e32test.h>
#include <u32hal.h>
#include <e32svr.h>
#include <f32file.h>
#include <hal.h>
#include <e32math.h>

RTest test(_L("SMP_DEMO"));

#define DEBUG_PRINT(__args)		test.Printf __args ;

TBool   TestThreadsExit = EFalse;
_LIT(KTestBlank, "");


//#define LOCK_TYPE	RMutex
//#define LOCK_TYPE_CREATE_PARAM	
#define LOCK_TYPE	RFastLock
#define LOCK_TYPE_CREATE_PARAM	
//#define LOCK_TYPE	RSemaphore
//#define LOCK_TYPE_CREATE_PARAM	0

#define MAX_THREADS		8		
#define MAX_CHAPTERS	28

TUint8		TestGuess[MAX_THREADS];
TInt		TestGuessReady[MAX_THREADS];
LOCK_TYPE	TestGuessLock[MAX_THREADS];

TInt		TestGuessChanged[MAX_THREADS];
LOCK_TYPE	TestChangedLock[MAX_THREADS];
TUint8		TestNext[MAX_THREADS];

TUint		TestGuessMisses[MAX_THREADS];
TUint		TestGuessCorrect[MAX_THREADS];
TUint		TestGuessIncorrect[MAX_THREADS];
TUint		TestGuessCollision[MAX_THREADS];
TInt		TestCpuCount = 0;

TBool		TestUseMathRandom = ETrue;
TBool		TestSingleCpu = EFalse;
TBool		TestDualCpu = EFalse;
TBool		TestNoPrint = EFalse;
TBool		TestSingleThread = EFalse;
TBool		TestUseAffinity = ETrue;

TInt LoadChapter(TInt chapterIndex, HBufC8 **aChapterPtrPtr)
	{
	RFile file;
	RFs fs;
	if (KErrNone != fs.Connect())
		{
		DEBUG_PRINT(_L("LoadChapter : Can't connect to the FS\n"));
		return KErrGeneral;
		}

	TBuf<32>	filename;
	filename.Format(_L("z:\\Test\\war_and_peace_ch%d.txt"), chapterIndex);

	TInt ret = file.Open(fs,filename,EFileRead);
	if (ret == KErrNone)
		{
		TInt fileSize = 0;
		ret = file.Size(fileSize);
		if (ret == KErrNone)
			{
			HBufC8 *theBuf = HBufC8::New(fileSize + 10);
			if (theBuf != NULL)
				{
				TPtr8 des2=theBuf->Des();
				ret = file.Read((TInt)0, des2,fileSize);
				if (ret == KErrNone)
					{
					*aChapterPtrPtr = theBuf;
					}
				else
					{
					DEBUG_PRINT((_L("LoadChapter : Read Failed for %S of %d\n"), &filename, fileSize));
					}
				}
			else
				{
				DEBUG_PRINT((_L("LoadChapter : Buffer Alloc Failed for %S\n"), &filename));
				}
			}
		else
			{
			DEBUG_PRINT((_L("LoadChapter : Size Failed for %S\n"), &filename));
			}
		file.Close();
		}
	else
		{
		DEBUG_PRINT((_L("LoadChapter : Open Failed for %S\n"), &filename));
		}
	
	return ret;
	}	

TInt SetCpuAffinity(TInt aThreadId)
	{
	if (TestUseAffinity)
		{
		TUint32 cpu;

		if (TestCpuCount == 4)
			cpu = (TUint32)(aThreadId % 3) + 1;
		else if (TestCpuCount == 2)
			cpu = (TUint32)1;
		else
			cpu = 0;

		TInt r = UserSvr::HalFunction(EHalGroupKernel, EKernelHalLockThreadToCpu, (TAny *)cpu, 0);
		test(r==KErrNone);	
		return r;
		}
	return KErrNone;
	}


LOCAL_C TInt DemoThread(TAny* aUseTb)
	{
	TInt	threadId = (TInt)aUseTb;
	
	SetCpuAffinity(threadId);
	User::After(100);

	TestGuessChanged[threadId] = EFalse;
	TestGuessReady[threadId] = EFalse;
	TestGuessMisses[threadId] = 0;
	TestGuessCorrect[threadId] = 0;
	TestGuessIncorrect[threadId] = 0;
	TestGuessCollision[threadId] = 0;

	TUint8	guess = 0;
	TUint8	nextChar = TestNext[threadId];
	TBool	correct = EFalse;

	while (!TestThreadsExit)
		{
		correct = EFalse;

		if (TestUseMathRandom)
			guess = (TUint8)Math::Random();
		else
			guess ++;

		if (TestGuessChanged[threadId])
			{
			TestChangedLock[threadId].Wait();
			nextChar = TestNext[threadId];
			TestGuessChanged[threadId] = EFalse;
			TestChangedLock[threadId].Signal();			
			}
		correct = (nextChar == guess);

		if (correct)
			{
			if (TestGuessReady[threadId] == EFalse)
				{
				TestGuessLock[threadId].Wait();
				TestGuess[threadId] = guess;
				TestGuessReady[threadId] = ETrue;
				TestGuessLock[threadId].Signal();
				TestGuessCorrect[threadId] ++;
				}
			else
				{
				TestGuessMisses[threadId] ++;
				}
			}
		else
			{
			TestGuessIncorrect[threadId] ++;
			}
		if (TestCpuCount == 1)
			{
			User::After(0);
			}
		}
	return KErrNone;
	}

TInt NumberOfCpus()
	{
	TInt r = UserSvr::HalFunction(EHalGroupKernel, EKernelHalNumLogicalCpus, 0, 0);
	test(r>0);
	return r;
	}

TInt ParseArgs(void)
	{
	TBuf<256> args;
	User::CommandLine(args);
	TLex	lex(args);

	FOREVER
		{
		TPtrC  token=lex.NextToken();
		if(token.Length()!=0)
			{
			if (token == _L("unbound"))
				{
				TestUseMathRandom = EFalse;
				}
			if (token == _L("single"))
				{
				TestSingleCpu = ETrue;
				}
			if (token == _L("dual"))
				{
				TestDualCpu = ETrue;
				}
			if (token == _L("silent"))
				{
				TestNoPrint = ETrue;
				}
			if (token == _L("onethread"))
				{
				TestSingleCpu = ETrue;
				TestSingleThread = ETrue;
				}
			if (token == _L("help"))
				{
				test.Printf(_L("smp_demo: unbound | single | onethread | silent | dual | noaffinity | help \n"));
				return -1;
				}
			if (token == _L("noaffinity"))
				{
				TestUseAffinity = EFalse;
				}
			}
		else
			{
			break;
			}
		}
		return KErrNone;
	}

TInt E32Main()
	{
	test.Title();
	test.Start(_L("SMP Demonstration guessing War and Peace...."));
	
	if (ParseArgs() != KErrNone)
		{
		test.Getch();
		test.End();
		return KErrNone;
		}

	TUint   start = User::TickCount();
	TInt	tickPeriod = 0;
	HAL::Get(HAL::ESystemTickPeriod, tickPeriod);

	if (TestSingleCpu)
		{
		TestCpuCount = 1;
		}
	else if (TestDualCpu)
		{
		TestCpuCount = 2;
		}
	else
		{
		TestCpuCount = NumberOfCpus();
		}
	
	DEBUG_PRINT((_L("CPU Count %d\n"), TestCpuCount));

	TRequestStatus	theStatus[MAX_THREADS];
	RThread			theThreads[MAX_THREADS];
	TBool			threadInUse[MAX_THREADS];

	TInt	index;
	TInt	maxChapters = MAX_CHAPTERS;

	if (TestUseMathRandom)
		{
		maxChapters = 2;
		}

	TInt	maxIndex = TestCpuCount - 1;
	if (maxIndex == 0)
		{
		maxChapters = 2;
		maxIndex = 1;
		}
	else if ((maxIndex == 1) && (TestUseMathRandom))
		{
		maxChapters = 4;
		}

	TInt	ret;
	TUint32 cpu = 0;

	if (TestUseAffinity)
		{
		UserSvr::HalFunction(EHalGroupKernel, EKernelHalLockThreadToCpu, (TAny *)cpu, 0);
		}

	if (TestSingleThread)
		{
		TInt	chapterIndex;
		TUint8	guess = 0;
		
		maxChapters = MAX_CHAPTERS;

		TRequestStatus keyStatus;
		CConsoleBase*  console=test.Console();
		
		console->Read(keyStatus);

		for (chapterIndex = 0; chapterIndex < maxChapters; chapterIndex ++)
			{
			HBufC8 *chapterPtr = NULL;
			ret = LoadChapter(chapterIndex + 1, &chapterPtr);
			if ((ret != KErrNone) || (chapterPtr == NULL))
				{
				DEBUG_PRINT((_L("E32Main: LoadChapter failed %d\n"), ret));
				}
			else
				{
				TPtr8			theDes = chapterPtr->Des();
				TUint8		   *pData = (TUint8 *)theDes.Ptr();
				TInt			dataLength = chapterPtr->Length();


				while (dataLength > 0)
					{
					if (TestUseMathRandom)
						guess = (TUint8)Math::Random();
					else
						guess ++;

 					if (*pData == guess)
						{
						pData ++;
						dataLength --;
						if (!TestNoPrint)
							{
							test.Printf(_L("%c"), (TUint8)guess);
							}
						}
					if (keyStatus != KRequestPending)
						{
						if (console->KeyCode() == EKeyEscape)
							{
							TestThreadsExit = ETrue;
							break;
							}
						console->Read(keyStatus);
						}
					}
				// clean up
				delete chapterPtr;
				test.Printf(_L("\n\n"));
				if (TestThreadsExit)
					{
					break;
					}
				}
			}
		console->ReadCancel();
		test.Printf(_L("Finished after %d chapters!\n"),chapterIndex);
		}
	else
		{
		for (index = 0; index < maxIndex; index ++)
			{
			TestGuessLock[index].CreateLocal(LOCK_TYPE_CREATE_PARAM);
			TestChangedLock[index].CreateLocal(LOCK_TYPE_CREATE_PARAM);
			ret = theThreads[index].Create(KTestBlank,DemoThread,KDefaultStackSize,NULL,(TAny*) index);
			if (ret == KErrNone)
				{
				theThreads[index].Logon(theStatus[index]);
				if (theStatus[index] != KRequestPending)
					{
					DEBUG_PRINT((_L("E32Main: !KRequestPending %d\n"), theStatus[index].Int() ));
					}	
				theThreads[index].Resume();
				threadInUse[index] = ETrue;
				DEBUG_PRINT((_L("E32Main: starting thread %d %d\n"), index, index % TestCpuCount));
				}
			else
				{
				DEBUG_PRINT((_L("E32Main: Create thread failed %d\n"), ret));
				return KErrGeneral;
				}
			}

		TInt	chapterIndex;
		TInt    index2;

		TRequestStatus keyStatus;
		CConsoleBase*  console=test.Console();
		
		console->Read(keyStatus);

		for (chapterIndex = 0; chapterIndex < maxChapters; chapterIndex ++)
			{
			HBufC8 *chapterPtr = NULL;
			ret = LoadChapter(chapterIndex + 1, &chapterPtr);
			if ((ret != KErrNone) || (chapterPtr == NULL))
				{
				DEBUG_PRINT((_L("E32Main: LoadChapter failed %d\n"), ret));
				}
			else
				{
				TPtr8			theDes = chapterPtr->Des();
				TUint8		   *pData = (TUint8 *)theDes.Ptr();
				TInt			dataLength = chapterPtr->Length();
				for (index2 = 0; index2 < maxIndex; index2 ++)
					{
					TestChangedLock[index2].Wait();
					TestGuessChanged[index2] = ETrue;
					TestNext[index2] = (TUint8)*pData;
					TestChangedLock[index2].Signal();
					}
				// where the real code goes!!
				TUint8	guess = 0;
				TBool	wasReady = EFalse;
				while (dataLength > 0)
					{
					for (index = 0; index < maxIndex; index ++)
						{
						wasReady = EFalse;
						if (TestGuessReady[index])
							{
							wasReady = ETrue;
							TestGuessLock[index].Wait();
							guess = (TUint8)TestGuess[index];
							TestGuessReady[index] = EFalse;
							TestGuessLock[index].Signal();
							}
						if (wasReady)
							{
							if (*pData == guess)
								{
								pData ++;
								dataLength --;
								for (index2 = 0; index2 < maxIndex; index2 ++)
									{
									TestChangedLock[index2].Wait();
									TestNext[index2] = (TUint8)*pData;
									TestGuessChanged[index2] = ETrue;
									TestChangedLock[index2].Signal();
									}
								if (!TestNoPrint)
									{
									test.Printf(_L("%c"), (TUint8)guess);
									}
								}
							else
								{
								TestGuessCollision[index] ++;
								}
							}
						if (TestCpuCount == 1)
							{
							User::After(0);
							}
						}
					if (keyStatus != KRequestPending)
						{
						if (console->KeyCode() == EKeyEscape)
							{
							TestThreadsExit = ETrue;
							break;
							}
						console->Read(keyStatus);
						}
					}
				// clean up
				delete chapterPtr;
				test.Printf(_L("\n\n"));
				if (TestThreadsExit)
					{
					break;
					}
				}
			}

		console->ReadCancel();
		
		test.Printf(_L("Finished after %d chapters!\n"),chapterIndex);
		for (index = 0; index < maxIndex; index ++)
			{
			test.Printf(_L("Thread %d stalls %u correct %u incorrect %u collision %u\n"), index, TestGuessMisses[index],TestGuessCorrect[index],TestGuessIncorrect[index], TestGuessCollision[index]);
			}
			
		// real code ends!!
		TestThreadsExit = ETrue;
		
		TBool		anyUsed = ETrue;

		while(anyUsed)
			{
			anyUsed = EFalse;

			for (index = 0; index < maxIndex; index++)
				{
				if (threadInUse[index])
					{
					if (theThreads[index].ExitType() != EExitPending)
						{
						threadInUse[index] = EFalse;
						TestGuessLock[index].Close();
						TestChangedLock[index].Close();
						}
					else
						{
						anyUsed = ETrue;
						}
					}
				}
			}
		}		
	TUint time = TUint((TUint64)(User::TickCount()-start)*(TUint64)tickPeriod/(TUint64)1000000);
	test.Printf(_L("Complete in %u seconds\n"), time);	
	test.Getch();
	test.End();
	return KErrNone;
	}