kerneltest/e32test/prime/t_semutx.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
child 28 5b5d147c7838
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\prime\t_semutx.cpp
// Tests the RSemaphore, RMutex and RCriticalSection classes
// Overview:
// Tests the RSemaphore, RMutex and RCriticalSection classes
// API Information:
// RSemaphore, RMutex, RCriticalSection
// Details:
// - Test RSemaphore and RMutex with the producer/consumer scenario.
// Create two threads, use signal and wait to coordinate the
// threads. Verify results are as expected.
// - Calculate the time required to create, resume and close a thread.
// - Test RSemaphore::Wait(timeout) in a variety ways and timeout 
// values. Verify results are as expected.
// - Test RMutex via two threads which write to an array. The writing
// and updating of the index is wrapped within a mutex pair. Verify 
// results are as expected.
// - Test RCriticalSection via two threads which write to an array. The 
// writing and updating of the index is wrapped within a critical section
// pair. Verify results are as expected.
// Platforms/Drives/Compatibility:
// All.
// Assumptions/Requirement/Pre-requisites:
// Failures and causes:
// Base Port information:
// 
//

#include <e32test.h>

const TInt KMaxBufferSize=10;
const TInt KMaxArraySize=10;
const TInt KNumProducerItems=100;

enum {EThread1ID=1,EThread2ID};

RTest test(_L("T_SEMUTX"));
RMutex mutex;
RCriticalSection criticalSn;	
TInt thread1Count,thread2Count;
TInt arrayIndex;
TInt array[KMaxArraySize];  
TInt consumerArray[KNumProducerItems];
RSemaphore slotAvailable,itemAvailable;  
			 
class CStack
	{
public:	   
	CStack() {iCount=0;};
	void Push(TInt aItem) {iStack[iCount++]=aItem;};
	TInt Pop(void) {return(iStack[--iCount]);};
private:
	TInt iStack[KMaxBufferSize];
	TInt iCount;
	};
CStack stack;


TInt Producer(TAny*)
	{
	for(TInt ii=0;ii<KNumProducerItems;ii++)
		{
		slotAvailable.Wait();
		mutex.Wait();
		stack.Push(ii);
		mutex.Signal();
		itemAvailable.Signal();
		}
	return(KErrNone);
	}

TInt Consumer(TAny*)
	{
	TInt item;
	for(TInt ii=0;ii<KNumProducerItems;ii++)
		{
		itemAvailable.Wait();
		mutex.Wait();
		item=stack.Pop();
		mutex.Signal();
		slotAvailable.Signal();
		consumerArray[item]=item;
		}
	return(KErrNone);
	}

void BusyWait(TInt aMicroseconds)
	{
	TTime begin;
	begin.HomeTime();
	FOREVER
		{
		TTime now;
		now.HomeTime();
		TTimeIntervalMicroSeconds iv=now.MicroSecondsFrom(begin);
		if (iv.Int64()>=TInt64(aMicroseconds))
			return;
		}
	}

TInt MutexThreadEntryPoint1(TAny*)
//
// Mutex test thread 1
//
	{	

	thread1Count=0;
	TBool running=ETrue;
	do
		{
		mutex.Wait();
		BusyWait(100000);
		if (arrayIndex<KMaxArraySize)
			{
			array[arrayIndex++]=EThread1ID;
			thread1Count++;
			}
		else
			running=EFalse;
		mutex.Signal();
		} while (running);
	return(KErrNone);
	}

TInt MutexThreadEntryPoint2(TAny*)
//
// Mutex test thread 2
//
	{

	thread2Count=0;
	TBool running=ETrue;
	do
		{
		mutex.Wait();
		BusyWait(200000);
		if (arrayIndex<KMaxArraySize)
			{
			array[arrayIndex++]=EThread2ID;
			thread2Count++;
			}
		else
			running=EFalse;
		mutex.Signal();
		} while (running);
	return(KErrNone);
	}

TInt CriticalSnThreadEntryPoint1(TAny*)
//
// Critical Section test thread 1
//
	{	

	thread1Count=0;
	TBool running=ETrue;
	do
		{
		criticalSn.Wait();
		User::After(100000);
		if (arrayIndex<KMaxArraySize)
			{
			array[arrayIndex++]=EThread1ID;
			thread1Count++;
			}
		else
			running=EFalse;
		criticalSn.Signal();
		} while (running);
	return(KErrNone);
	}

TInt CriticalSnThreadEntryPoint2(TAny*)
//
// Critical Section test thread 2
//
	{

	thread2Count=0;
	TBool running=ETrue;
	do
		{
		criticalSn.Wait();
		User::After(200000);
		if (arrayIndex<KMaxArraySize)
			{
			array[arrayIndex++]=EThread2ID;
			thread2Count++;
			}
		else
			running=EFalse;
		criticalSn.Signal();
		} while (running);
	return(KErrNone);
	}

struct SWaitSem
	{
	RSemaphore iSem;
	TInt iTimeout;
	};

TInt WaitSemThread(TAny* a)
	{
	SWaitSem& ws = *(SWaitSem*)a;
	return ws.iSem.Wait(ws.iTimeout);
	}

void StartWaitSemThread(RThread& aT, SWaitSem& aW, TThreadPriority aP=EPriorityLess)
	{
	TInt r = aT.Create(KNullDesC, &WaitSemThread, 0x1000, 0x1000, 0x1000, &aW);
	test(r==KErrNone);
	aT.SetPriority(aP);
	aT.Resume();
	}

void WaitForWaitSemThread(RThread& aT, TInt aResult)
	{
	TRequestStatus s;
	aT.Logon(s);
	User::WaitForRequest(s);
	test(aT.ExitType()==EExitKill);
	test(aT.ExitReason()==aResult);
	test(s.Int()==aResult);
	CLOSE_AND_WAIT(aT);
	}

TInt DummyThread(TAny*)
	{
	return 0;
	}

void TestSemaphore2()
	{
	test.Start(_L("Test semaphore wait with timeout"));
	SWaitSem ws;
	RThread t;
	TTime initial;
	TTime final;
	TInt elapsed=0;
	TInt r = ws.iSem.CreateLocal(0);
	test(r==KErrNone);

	RThread().SetPriority(EPriorityAbsoluteVeryLow);
	TInt threadcount=0;
	initial.HomeTime();
	while (elapsed<1000000)
		{
		r = t.Create(KNullDesC, &DummyThread, 0x1000, NULL, NULL);
		test(r==KErrNone);
		t.SetPriority(EPriorityMore);
		t.Resume();
		t.Close();
		++threadcount;
		final.HomeTime();
		elapsed = I64INT(final.Int64()-initial.Int64());
		}
	RThread().SetPriority(EPriorityNormal);
	test.Printf(_L("%d threads in 1 sec\n"),threadcount);
	TInt overhead = 1000000/threadcount;
	test.Printf(_L("overhead = %dus\n"),overhead);

	ws.iTimeout=1000000;
	initial.HomeTime();
	StartWaitSemThread(t, ws);
	WaitForWaitSemThread(t, KErrTimedOut);
	final.HomeTime();
	elapsed = I64INT(final.Int64()-initial.Int64());
	test.Printf(_L("Time taken = %dus\n"), elapsed);
	test(elapsed>=900000+overhead && elapsed<1500000+overhead);

	ws.iTimeout=-1;
	initial.HomeTime();
	StartWaitSemThread(t, ws);
	WaitForWaitSemThread(t, KErrArgument);
	final.HomeTime();
	elapsed = I64INT(final.Int64()-initial.Int64());
	test.Printf(_L("Time taken = %dus\n"), elapsed);

	ws.iTimeout=2000000;
	initial.HomeTime();
	StartWaitSemThread(t, ws);
	User::After(1000000);
	ws.iSem.Signal();
	WaitForWaitSemThread(t, KErrNone);
	final.HomeTime();
	elapsed = I64INT(final.Int64()-initial.Int64());
	test.Printf(_L("Time taken = %dus\n"), elapsed);
	test(elapsed>=900000+overhead && elapsed<1500000+overhead);

	ws.iTimeout=100000;
	StartWaitSemThread(t, ws, EPriorityMore);
	t.Suspend();
	ws.iSem.Signal();
	User::After(200000);
	t.Resume();
	WaitForWaitSemThread(t, KErrTimedOut);
	test(ws.iSem.Wait(1)==KErrNone);

	ws.iTimeout=100000;
	StartWaitSemThread(t, ws, EPriorityMore);
	t.Suspend();
	ws.iSem.Signal();
	User::After(50000);
	t.Resume();
	WaitForWaitSemThread(t, KErrNone);
	test(ws.iSem.Wait(1)==KErrTimedOut);

	RThread t2;
	ws.iTimeout=100000;
	StartWaitSemThread(t, ws, EPriorityMuchMore);
	StartWaitSemThread(t2, ws, EPriorityMore);
	t.Suspend();
	ws.iSem.Signal();
	test(t2.ExitType()==EExitKill);
	test(t.ExitType()==EExitPending);
	t.Resume();
	WaitForWaitSemThread(t, KErrTimedOut);
	WaitForWaitSemThread(t2, KErrNone);
	test(ws.iSem.Wait(1)==KErrTimedOut);

	ws.iTimeout=1000000;
	initial.HomeTime();
	StartWaitSemThread(t2, ws, EPriorityMore);
	StartWaitSemThread(t, ws, EPriorityMuchMore);
	ws.iSem.Signal();
	WaitForWaitSemThread(t, KErrNone);
	final.HomeTime();
	elapsed = I64INT(final.Int64()-initial.Int64());
	test.Printf(_L("Time taken = %dus\n"), elapsed);
	WaitForWaitSemThread(t2, KErrTimedOut);
	final.HomeTime();
	elapsed = I64INT(final.Int64()-initial.Int64());
	test.Printf(_L("Time taken = %dus\n"), elapsed);
	test(elapsed>=900000+2*overhead && elapsed<1500000+2*overhead);

	ws.iTimeout=1000000;
	initial.HomeTime();
	StartWaitSemThread(t2, ws, EPriorityMore);
	StartWaitSemThread(t, ws, EPriorityMuchMore);
	WaitForWaitSemThread(t, KErrTimedOut);
	final.HomeTime();
	elapsed = I64INT(final.Int64()-initial.Int64());
	test.Printf(_L("Time taken = %dus\n"), elapsed);
	WaitForWaitSemThread(t2, KErrTimedOut);
	final.HomeTime();
	elapsed = I64INT(final.Int64()-initial.Int64());
	test.Printf(_L("Time taken = %dus\n"), elapsed);
	test(elapsed>=900000+2*overhead && elapsed<1500000+2*overhead);

	ws.iTimeout=1000000;
	initial.HomeTime();
	StartWaitSemThread(t2, ws, EPriorityMore);
	StartWaitSemThread(t, ws, EPriorityMuchMore);
	t.Kill(299792458);
	WaitForWaitSemThread(t2, KErrTimedOut);
	WaitForWaitSemThread(t, 299792458);
	final.HomeTime();
	elapsed = I64INT(final.Int64()-initial.Int64());
	test.Printf(_L("Time taken = %dus\n"), elapsed);
	test(elapsed>=900000+2*overhead && elapsed<1500000+2*overhead);

	ws.iTimeout=1000000;
	initial.HomeTime();
	StartWaitSemThread(t, ws, EPriorityMore);
	StartWaitSemThread(t2, ws, EPriorityMuchMore);
	test(t.ExitType()==EExitPending);
	test(t2.ExitType()==EExitPending);
	ws.iSem.Close();
	test(t.ExitType()==EExitKill);
	test(t2.ExitType()==EExitKill);
	WaitForWaitSemThread(t2, KErrGeneral);
	WaitForWaitSemThread(t, KErrGeneral);
	final.HomeTime();
	elapsed = I64INT(final.Int64()-initial.Int64());
	test.Printf(_L("Time taken = %dus\n"), elapsed);
	test(elapsed<=50000+3*overhead);

	test.End();
	}

void TestSemaphore()
	{
/*********** TO DO ************/
// Check it panics if the count <0

	test.Start(_L("Create"));
	RSemaphore semaphore;
	RThread thread1, thread2;

	semaphore.CreateLocal(0); 	// creates a DPlatSemaphore but casts it to a pointer to a DSemaphore
								// sets semaphore count to the value of the parameter, 
								// adds object to the K::Semaphores container, sets iHandle
								// Local sets DSemaphore.iName to NULL & iOwner to Kern::CurrentProcess()
								// Global sets iName to that passed and iOwner to NULL
								// Adds a record into CObjectIx containing a pointer to the semaphore object
/*	test.Next(_L("Find"));
	fullName=semaphore.FullName();	
	find.Find(fullName);	// sets iMatch to fullName	(misleadingly named method as it doesn't find anything)
	test(find.Next(fullName)== KErrNone);	
*/
	test.Next(_L("Producer/Consumer scenario"));
	// Test Rsemaphore with the producer/consumer scenario	RThread thread1, thread2;
	TRequestStatus stat1, stat2;
	test(mutex.CreateLocal()==KErrNone);
	test(slotAvailable.CreateLocal(KMaxBufferSize)==KErrNone);
	test(itemAvailable.CreateLocal(0)==KErrNone);
	test(thread1.Create(_L("Thread1"),Producer,KDefaultStackSize,0x200,0x200,NULL)==KErrNone);
	test(thread2.Create(_L("Thread2"),Consumer,KDefaultStackSize,0x200,0x200,NULL)==KErrNone);
	thread1.Logon(stat1);
	thread2.Logon(stat2);
	test(stat1==KRequestPending);
	test(stat2==KRequestPending);
	thread1.Resume(); 
	thread2.Resume();
	User::WaitForRequest(stat1);
	User::WaitForRequest(stat2);
	test(stat1==KErrNone);
	test(stat2==KErrNone);
	for(TInt jj=0;jj<KNumProducerItems;jj++)
		test(consumerArray[jj]==jj);		
	
	test.Next(_L("Close"));
	mutex.Close();
	CLOSE_AND_WAIT(thread1);
	CLOSE_AND_WAIT(thread2);
	test.End();
	}

void TestMutex2()
	{
	RMutex m;
	test.Start(_L("Create"));
	test(m.CreateLocal()==KErrNone);

	// Test RMutex::IsHeld()
	test.Next(_L("IsHeld ?"));
	test(!m.IsHeld());
	test.Next(_L("Wait"));
	m.Wait();
	test.Next(_L("IsHeld ?"));
	test(m.IsHeld());
	test.Next(_L("Signal"));
	m.Signal();
	test.Next(_L("IsHeld ?"));
	test(!m.IsHeld());

	test.End();
	}

void TestMutex()
	{
	test.Start(_L("Create"));
	test(mutex.CreateLocal()==KErrNone);
	
	test.Next(_L("Threads writing to arrays test"));
//
// Create two threads which write to two arrays. The arrays and indexs
// are global and each thread writes an identifier to the arrays. For
// one array the writing and updating of the index is wrapped in a mutex
// pair. The other array is a control and is not wrapaped within mutex.
// Each thread records the number of instances it "thinks" it wrote to
// each array. For the mutex controlled array the actual instances
// written to the array should always be the same as the threads think.
//
	arrayIndex=0;
	RThread thread1,thread2;	
	test(thread1.Create(_L("Thread1"),MutexThreadEntryPoint1,KDefaultStackSize,0x2000,0x2000,NULL)==KErrNone);
	test(thread2.Create(_L("Thread2"),MutexThreadEntryPoint2,KDefaultStackSize,0x2000,0x2000,NULL)==KErrNone);			 
	TRequestStatus stat1,stat2;
	thread1.Logon(stat1);
	thread2.Logon(stat2);
	test(stat1==KRequestPending);
	test(stat2==KRequestPending);
	thread1.Resume(); 
	thread2.Resume();
	User::WaitForRequest(stat1);
	User::WaitForRequest(stat2);
	test(stat1==KErrNone);
	test(stat2==KErrNone); 
	TInt thread1ActualCount=0; 
	TInt thread2ActualCount=0;
	TInt ii=0;
	while(ii<KMaxArraySize)
		{
		if (array[ii]==EThread1ID)
			thread1ActualCount++;
		if (array[ii]==EThread2ID)
			thread2ActualCount++;
		ii++;
		}
	test.Printf(_L("T1 %d T1ACT %d T2 %d T2ACT %d"),thread1Count,thread1ActualCount,thread2Count,thread2ActualCount);
	test(thread1ActualCount==thread1Count);
	test(thread2ActualCount==thread2Count);
	test(thread1Count==thread2Count);
	test(thread1Count==(KMaxArraySize>>1));
	
	test.Next(_L("Close"));
	CLOSE_AND_WAIT(thread1);
	CLOSE_AND_WAIT(thread2);
	mutex.Close();
	test.End();
	}

void TestCriticalSection()
//
//As TestMutex, but for RCriticalSection
//
	{
	
	test.Start(_L("Create"));
	test(criticalSn.CreateLocal()==KErrNone);

/***************** TO DO ***********************

	test.Next(_L("Find"));
//
// Test finding the RCriticalSection
//
	TFindCriticalSection find;
	TFullName fullName;
	fullName=criticalSn.FullName();
	find.Find(fullName);
	test(find.Next(fullName)==KErrNone);
	test(fullName==criticalSn.FullName());

************************************************/

	test.Next(_L("Threads writing to arrays test"));
//
// Create two threads which write to two arrays. The arrays and indexs
// are global and each thread writes an identifier to the arrays. For
// one array the writing and updating of the index is wrapped in a critical
// section pair. The other array is a control and is not wrapaped within
// a critical section. Each thread records the number of instances it
// "thinks" it wrote to each array. For the mutex controlled array the
// actual instances written to the array should always be the same as the
// threads think.
//
	arrayIndex=0;
	RThread thread1,thread2;	
	test(thread1.Create(_L("Thread1"),CriticalSnThreadEntryPoint1,KDefaultStackSize,0x2000,0x2000,NULL)==KErrNone);
	test(thread2.Create(_L("Thread2"),CriticalSnThreadEntryPoint2,KDefaultStackSize,0x2000,0x2000,NULL)==KErrNone);			 
	TRequestStatus stat1,stat2;
	thread1.Logon(stat1);
	thread2.Logon(stat2);
	test(stat1==KRequestPending);
	test(stat2==KRequestPending);
	thread1.Resume(); 
	thread2.Resume();
	User::WaitForRequest(stat1);
	User::WaitForRequest(stat2);
	test(stat1==KErrNone);
	test(stat2==KErrNone); 
	TInt thread1ActualCount=0; 
	TInt thread2ActualCount=0;
	TInt ii=0;
	while(ii<KMaxArraySize)
		{
		if (array[ii]==EThread1ID)
			thread1ActualCount++;
		if (array[ii]==EThread2ID)
			thread2ActualCount++;
		ii++;
		}
	test(thread1ActualCount==thread1Count);
	test(thread2ActualCount==thread2Count);
	test(thread1Count==thread2Count);
	test(thread1Count==(KMaxArraySize>>1));

	test.Next(_L("Close"));
	CLOSE_AND_WAIT(thread1);
	CLOSE_AND_WAIT(thread2);
	criticalSn.Close();
	test.End();
	}


GLDEF_C TInt E32Main()
	{	

	test.Title();
 	__UHEAP_MARK;
	test.Start(_L("Test RSemaphore"));
	TestSemaphore();
	TestSemaphore2();
	test.Next(_L("Test RMutex"));
	TestMutex();
	TestMutex2();
	test.Next(_L("Test RCriticalSection"));
	TestCriticalSection();
	test.End();
	__UHEAP_MARKEND;
	return(KErrNone);
	}