kerneltest/e32test/prime/t_semutx2.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 29 743008598095
parent 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_semutx2.cpp
// Test RSemaphore and RMutex
// Overview:
// Tests the RSemaphore and RMutex
// API Information:
// RSemaphore, RMutex
// Details:
// - Test and verify that thread priorities work as expected.
// - Test and verify that signalling an RMutex from the wrong
// thread fails as expected.
// - Test and verify that mutex priority inheritance works as
// expected.
// - Perform an exhaustive state transition test using mutexs
// and up to ten threads. Verify priorities, order of execution, 
// mutex signalling, suspend, resume, kill and close. Verify 
// results are as expected.
// - Test semaphore speed by counting how many Wait/Signal 
// operations can be completed in one second.
// Platforms/Drives/Compatibility:
// All.
// Assumptions/Requirement/Pre-requisites:
// Failures and causes:
// Base Port information:
// 
//

#define __E32TEST_EXTENSION__
#include <e32test.h>
#include <u32std.h>
#include <e32svr.h>

RMutex M1;
RMutex M2;
RSemaphore S;

const TInt KBufferSize=4096;
TUint ThreadId[KBufferSize];
TInt PutIx;
TInt GetIx;
TInt Count;
RThread Main;


RTest test(_L("T_SEMUTX2"));

/*****************************************************************************
 * Utility functions / macros
 *****************************************************************************/
#define TRACE_ON	User::SetDebugMask(0xffdfffff);
#define TRACE_OFF	User::SetDebugMask(0x80000000);

//#define MCOUNT(m,c)	test((m).Count() ==(c))
// mutex count value is not visible for user any more
#define MCOUNT(m,c) (void)(1)
#define IDCHECK(x) test_Equal((x), GetNextId())
#define NUMCHECK(x)	test_Equal((x), NumIdsPending())

#define id0		id[0]
#define id1		id[1]
#define id2		id[2]
#define id3		id[3]
#define id4		id[4]
#define id5		id[5]
#define id6		id[6]
#define id7		id[7]
#define id8		id[8]
#define id9		id[9]
#define id10	id[10]

TBool Exists(const TDesC& aName)
	{
	TFullName n(RProcess().Name());
	n+=_L("::");
	n+=aName;
	TFindThread ft(n);
	TFullName fn;
	return ft.Next(fn)==KErrNone;
	}

TBool Exists(TInt aNum)
	{
	TBuf<4> b;
	b.Num(aNum);
	return Exists(b);
	}

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

void Kick(RThread& t)
	{
	TRequestStatus s;
	TRequestStatus* pS=&s;
	t.RequestComplete(pS,0);
	}

TUint GetNextId()
	{
	if (GetIx<PutIx)
		return ThreadId[GetIx++];
	return 0;
	}

TInt NumIdsPending()
	{
	return PutIx-GetIx;
	}

/*****************************************************************************
 * General tests
 *****************************************************************************/
TInt Test0Thread(TAny* aPtr)
	{
	TInt& count=*(TInt*)aPtr;
	++count;
	Main.SetPriority(EPriorityMuchMore);
	++count;
	Main.SetPriority(EPriorityMuchMore);
	++count;
	RThread().SetPriority(EPriorityNormal);
	++count;
	return 0;
	}

void Test0()
	{
	User::After(100000);	// Test fails intermittently on hardware unless we pause here 
	
	test.Start(_L("Test thread priorities work"));
	test.Next(_L("Create thread"));
	RThread t;
	TInt count=0;
	TRequestStatus s;
	TInt r=t.Create(_L("Test0"),Test0Thread,0x1000,NULL,&count);
	test_KErrNone(r);
	t.Logon(s);
	test_KErrNone(r);
	User::After(10000);		// make sure we have a full timeslice
	t.Resume();

	test_Equal(0, count);			// t shouldn't have run yet
	RThread().SetPriority(EPriorityMuchMore);	// shouldn't reschedule (priority unchanged)
	test_Equal(0, count);
	RThread().SetPriority(EPriorityMore);	// shouldn't reschedule (priority decreasing, but not enough)
	test_Equal(0, count);
	RThread().SetPriority(EPriorityMuchMore);	// shouldn't reschedule (priority increasing)
	test_Equal(0, count);
	RThread().SetPriority(EPriorityNormal);	// should reschedule (we go behind t)
	test_Equal(1, count);
	RThread().SetPriority(EPriorityLess);	// should reschedule (priority decreasing to below t)
	test_Equal(2, count);
	t.SetPriority(EPriorityMuchMore);		// shouldn't reschedule (round-robin, timeslice not expired)
	test_Equal(2, count);
	t.SetPriority(EPriorityNormal);			// shouldn't reschedule (t's priority decreasing)
	test_Equal(2, count);
	t.SetPriority(EPriorityNormal);			// shouldn't reschedule (t's priority unchanged)
	test_Equal(2, count);
	BusyWait(100000);		// use up our timeslice
	t.SetPriority(EPriorityMuchMore);		// should reschedule (round-robin, timeslice expired)
	test_Equal(3, count);
	test_Equal(KRequestPending, s.Int());
	test_Equal(EExitPending, t.ExitType());
	t.SetPriority(EPriorityRealTime);		// should reschedule (t increases above current)
	test_Equal(4, count);
	test_KErrNone(s.Int());						// t should have exited
	test_Equal(EExitKill, t.ExitType());
	User::WaitForRequest(s);
	RThread().SetPriority(EPriorityMuchMore);
	t.Close();
	test.End();
	}

TInt Test1Thread(TAny*)
	{
	M1.Signal();
	return 0;
	}

void Test1()
	{
	test.Start(_L("Test signalling from wrong thread"));
	TInt r=M1.CreateLocal();
	test_KErrNone(r);
	M1.Wait();
	RThread t;
	r=t.Create(_L("Test1"),Test1Thread,0x1000,NULL,NULL);
	test_KErrNone(r);
	TRequestStatus s;
	t.Logon(s);
	t.Resume();
	TBool jit = User::JustInTime();
	User::SetJustInTime(EFalse);
	User::WaitForRequest(s);
	User::SetJustInTime(jit);
	test_Equal(EAccessDenied, s.Int());
	test_Equal(EExitPanic, t.ExitType());
	test_Equal(EAccessDenied, t.ExitReason());
	test(t.ExitCategory()==_L("KERN-EXEC"));
	t.Close();
	M1.Close();
	test.End();
	}

/*****************************************************************************
 * Mutex priority inheritance
 *****************************************************************************/

const TInt KTestDelay = 1000000;

TInt LowThread(TAny* aPtr)
	{
	TInt& count=*(TInt*)aPtr;
	FOREVER
		{
		M1.Wait();
		++count;
		BusyWait(KTestDelay);
		M1.Signal();
		User::WaitForAnyRequest();
		}
	}

TInt MedThread(TAny* aPtr)
	{
	TInt& count=*(TInt*)aPtr;
	FOREVER
		{
		++count;
		User::WaitForAnyRequest();
		}
	}

TInt HighThread(TAny* aPtr)
	{
	TInt& count=*(TInt*)aPtr;
	FOREVER
		{
		M2.Wait();
		++count;
		M1.Wait();
		++count;
		BusyWait(KTestDelay);
		M1.Signal();
		M2.Signal();
		User::WaitForAnyRequest();
		}
	}

void TestMutex1()
	{
	test.Start(_L("Test mutex priority inheritance"));

	test.Next(_L("Create mutex"));
	TInt r=M1.CreateLocal();
	test_KErrNone(r);

	test.Next(_L("Create low priority thread"));
	TInt lowcount=0;
	RThread low;
	r=low.Create(_L("low"),LowThread,0x1000,NULL,&lowcount);
	test_KErrNone(r);
	low.SetPriority(EPriorityMuchLess);
	test(Exists(_L("low")));

	test.Next(_L("Create medium priority thread"));
	TInt medcount=0;
	RThread med;
	r=med.Create(_L("med"),MedThread,0x1000,NULL,&medcount);
	test_KErrNone(r);
	med.SetPriority(EPriorityNormal);
	test(Exists(_L("med")));

	test.Next(_L("Start low priority thread"));
	low.Resume();
	User::AfterHighRes(KTestDelay/10);
	test_Equal(1, lowcount);
//	MCOUNT(M1,0);

	test.Next(_L("Start medium priority thread"));
	med.Resume();
	User::AfterHighRes(KTestDelay/10);
	test_Equal(1, medcount);
	Kick(med);
	User::AfterHighRes(KTestDelay/10);
	test_Equal(2, medcount);
	Kick(med);

	M1.Wait();
	test_Equal(1, lowcount);
	test_Equal(2, medcount);
	test.Next(_L("Wait, check medium runs"));
	User::AfterHighRes(KTestDelay/10);
	test_Equal(3, medcount);
	M1.Signal();

	test.Next(_L("Create mutex 2"));
	r=M2.CreateLocal();
	test_KErrNone(r);

	test.Next(_L("Create high priority thread"));
	TInt highcount=0;
	RThread high;
	r=high.Create(_L("high"),HighThread,0x1000,NULL,&highcount);
	test_KErrNone(r);
	high.SetPriority(EPriorityMore);
	test(Exists(_L("high")));

	Kick(low);
	User::AfterHighRes(KTestDelay/10);
//	MCOUNT(M1,0);
	Kick(med);

//	MCOUNT(M2,1);
	high.Resume();
	User::AfterHighRes(KTestDelay/10);
//	MCOUNT(M2,0);
//	MCOUNT(M1,-1);
	test_Equal(1, highcount);

	M2.Wait();
	test_Equal(2, lowcount);
	test_Equal(3, medcount);
	test_Equal(2, highcount);
	test.Next(_L("Wait, check medium runs"));
	User::AfterHighRes(KTestDelay/10);
	test_Equal(4, medcount);
	M2.Signal();

	test.Next(_L("Kill threads"));
	low.Kill(0);
	med.Kill(0);
	high.Kill(0);
	low.Close();
	med.Close();
	high.Close();
	test(!Exists(_L("low")));
	test(!Exists(_L("med")));
	test(!Exists(_L("high")));

	M1.Close();
	test.End();
	}

/*****************************************************************************
 * Utilities for mutex exhaustive state transition test
 *****************************************************************************/
void MutexWait()
	{
	M1.Wait();
	++Count;
	ThreadId[PutIx++]=(TUint)RThread().Id();
	}

void MutexSignal()
	{
	M1.Signal();
	}

typedef void (*PFV)(void);
TInt ThreadFunction(TAny* aPtr)
	{
	PFV& f=*(PFV*)aPtr;
	FOREVER
		{
		MutexWait();
		if (f)
			f();
		MutexSignal();
		User::WaitForAnyRequest();
		}
	}

void Exit()
	{
	User::Exit(0);
	}

TUint CreateThread(RThread& t, TInt n, TAny* aPtr)
	{
	TBuf<4> b;
	b.Num(n);
	TInt r=t.Create(b,ThreadFunction,0x1000,NULL,aPtr);
	test_KErrNone(r);
	t.Resume();
	TUint id=t.Id();
	test.Printf(_L("id=%d\n"),id);
	return id;
	}

/*
Possible thread relationships with mutex:
	Holding
	Waiting
	Waiting + suspended
	Hold Pending

Need to verify correct behaviour when the following actions occur for each of these states:
	Suspend thread
	Resume thread
	Change thread priority
	Thread exits
	Thread is killed
	Mutex deleted
*/

PFV HoldExtra;
void KickMain()
	{
	RThread me;
	Kick(Main);
	User::WaitForAnyRequest();
	me.SetPriority(EPriorityMuchMore);
	MutexSignal();						// this should wake up t8
	MutexWait();
	MutexSignal();						// this should wake up t9
	MutexWait();
	Kick(Main);
	User::WaitForAnyRequest();
	if (HoldExtra)
		HoldExtra();
	}

void RackEmUp(RThread* t, PFV* f, TUint* id)
	{
	// set up t4 holding
	// t1, t2, t5, t10 waiting
	// t3, t6, t7 waiting+suspended
	// t8, t9 pending
	MCOUNT(M1,1);			// check mutex free
	Kick(t[4]);
	f[4]=&KickMain;
	User::WaitForAnyRequest();
	MCOUNT(M1,0);			// check mutex now held
	TInt i;
	for (i=1; i<=10; ++i)
		if (i!=4)
			Kick(t[i]);		// wake up threads
	User::After(50000);		// let threads wait
	MCOUNT(M1,-9);			// check 9 threads waiting
	Kick(t[4]);
	User::WaitForAnyRequest();
	MCOUNT(M1,-7);			// check 7 threads waiting
	NUMCHECK(3);
	IDCHECK(id4);			// from the initial wait
	IDCHECK(id4);			// now have t8, t9 pending, t4 holding, rest waiting
	IDCHECK(id4);			// now have t8, t9 pending, t4 holding, rest waiting
	t[4].SetPriority(EPriorityNormal);
	t[7].Resume();			// test resume when not suspended
	MCOUNT(M1,-7);			// check 7 threads waiting
	t[3].Suspend();
	t[6].Suspend();
	t[7].Suspend();			// now have required state
	t[3].Suspend();			// suspend and resume t3 again for good measure
	t[3].Resume();
	MCOUNT(M1,-7);			// check 7 threads waiting
	HoldExtra=NULL;
	}

void SimpleCheck(TInt n, const TUint* id, ...)
	{
	VA_LIST list;
	VA_START(list,id);
	User::After(50000);		// let stuff happen
	NUMCHECK(n);
	TInt i;
	for (i=0; i<n; ++i)
		{
		TInt tn=VA_ARG(list,TInt);
		IDCHECK(id[tn]);
		}
	}

void Resurrect(TInt n, TThreadPriority aPriority, RThread* t, PFV* f, TUint* id)
	{
	f[n]=NULL;
	id[n]=CreateThread(t[n],n,f+n);
	t[n].SetPriority(EPriorityRealTime);
	t[n].SetPriority(aPriority);
	NUMCHECK(1);
	IDCHECK(id[n]);
	}

/*****************************************************************************
 * Mutex exhaustive state transition test
 *****************************************************************************/
void TestMutex2()
	{
	test.Start(_L("Test mutex state transitions"));
	RThread t[11];
	TUint id[11];
	PFV f[11]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
	id[0]=(TUint)RThread().Id();
	PutIx=0;
	GetIx=0;
	Count=0;
	test.Next(_L("Create mutex"));
	TInt r=M1.CreateLocal();
	test_KErrNone(r);
	MCOUNT(M1,1);
	MutexWait();
	MCOUNT(M1,0);
	IDCHECK(id[0]);
	test.Next(_L("Create threads"));
	TInt i;
	for (i=1; i<=5; ++i)
		id[i]=CreateThread(t[i],i,f+i);
	User::After(50000);		// let threads wait on mutex
	MCOUNT(M1,-5);			// check 5 threads waiting
	for (i=-4; i<=0; ++i)
		{
		MutexSignal(); // wake up next thread
		MutexWait();
		MCOUNT(M1,i);		// check right number of threads waiting
		IDCHECK(id0);		// check we got mutex back straight away
		}
	MutexSignal();
	User::After(50000);		// let threads claim mutex
	MutexWait();
	MCOUNT(M1,0);			// check no threads waiting
	for (i=1; i<=5; ++i)
		{
		IDCHECK(id[i]);		// check they ran in order t1...t5
		Kick(t[i]);			// wake up thread
		}
	IDCHECK(id0);			// check we got it back last
	t[4].SetPriority(EPriorityMore);	// make t4 higher priority
	User::After(50000);		// let threads wait on mutex
	MCOUNT(M1,-5);			// check 5 threads waiting
//	temp = M1.Count();
	MutexSignal();
//	temp = M1.Count();
	User::After(50000);		// let threads claim mutex
	MutexWait();
//	temp = M1.Count();
	MCOUNT(M1,0);			// check no threads waiting
	IDCHECK(id4);			// check they ran in order t4,t1,t2,t3,t5
	IDCHECK(id1);
	IDCHECK(id2);
	IDCHECK(id3);
	IDCHECK(id5);
	IDCHECK(id0);
	t[4].SetPriority(EPriorityNormal);	// make t4 normal priority
	for (i=1; i<=5; ++i)
		Kick(t[i]);			// wake up thread
	User::After(50000);		// let threads wait on mutex
	MCOUNT(M1,-5);			// check 5 threads waiting

	t[3].SetPriority(EPriorityMore);	// make t3 higher priority
//	temp = M1.Count();
	MutexSignal();
//	temp = M1.Count();
	User::After(50000);		// let threads claim mutex
	MutexWait();
//	temp = M1.Count();
	MCOUNT(M1,0);			// check no threads waiting
	IDCHECK(id3);			// check they ran in order t3,t1,t2,t4,t5
	IDCHECK(id1);
	IDCHECK(id2);
	IDCHECK(id4);
	IDCHECK(id5);
	IDCHECK(id0);
	t[3].SetPriority(EPriorityNormal);	// make t4 normal priority
	for (i=1; i<=5; ++i)
		Kick(t[i]);			// wake up threads
	User::After(50000);		// let threads wait on mutex
	MCOUNT(M1,-5);			// check 5 threads waiting

	t[2].SetPriority(EPriorityMore);	// make t2 higher priority
	t[1].SetPriority(EPriorityLess);	// make t1 lower priority
	MutexSignal();
	User::After(50000);		// let threads claim mutex
	MutexWait();
	MCOUNT(M1,0);			// check no threads waiting
	IDCHECK(id2);			// check they ran in order t2,t3,t4,t5,t1
	IDCHECK(id3);
	IDCHECK(id4);
	IDCHECK(id5);
	IDCHECK(id1);
	IDCHECK(id0);

	for (i=1; i<=5; ++i)
		Kick(t[i]);			// wake up threads
	User::After(50000);		// let threads wait on mutex
	MCOUNT(M1,-5);			// check 5 threads waiting
	MutexSignal();
	User::After(50000);		// let threads claim mutex
	MutexWait();
	MCOUNT(M1,0);			// check no threads waiting
	IDCHECK(id2);			// check they ran in order t2,t3,t4,t5,t1
	IDCHECK(id3);
	IDCHECK(id4);
	IDCHECK(id5);
	IDCHECK(id1);
	IDCHECK(id0);

	test(Exists(2));
	for (i=1; i<=5; ++i)
		Kick(t[i]);			// wake up threads
	User::After(50000);		// let threads wait on mutex
	MCOUNT(M1,-5);			// check 5 threads waiting
	f[2]=&Exit;				// make t2 exit while holding the mutex
	MutexSignal();
	User::After(50000);		// let threads claim mutex
	MutexWait();
	MCOUNT(M1,0);			// check no threads waiting
	test_Equal(EExitKill, t[2].ExitType());	// check t2 has exited
	t[2].Close();
	test(!Exists(2));
	IDCHECK(id2);			// check they ran in order t2,t3,t4,t5,t1
	IDCHECK(id3);
	IDCHECK(id4);
	IDCHECK(id5);
	IDCHECK(id1);
	IDCHECK(id0);
	f[2]=NULL;
	id[2]=CreateThread(t[2],2,f+2);	// recreate t2
	User::After(50000);		// let new t2 wait on mutex
	MCOUNT(M1,-1);			// check 1 thread waiting
	MutexSignal();
	User::After(50000);		// let t2 claim mutex
	MutexWait();
	MCOUNT(M1,0);			// check no threads waiting
	IDCHECK(id2);
	IDCHECK(id0);

	t[2].SetPriority(EPriorityLess);	// make t2 lower priority
	for (i=1; i<=5; ++i)
		Kick(t[i]);			// wake up threads
	User::After(50000);		// let threads wait on mutex
	MCOUNT(M1,-5);			// check 5 threads waiting
	MutexSignal();			// t3 now pending
	MCOUNT(M1,-3);			// check 4 threads waiting, mutex free
	t[3].Suspend();			// this should wake up t4
	MCOUNT(M1,-2);			// check 3 threads waiting, mutex free
	User::After(50000);		// let threads claim mutex
	MutexWait();
	MCOUNT(M1,0);			// check no threads still waiting
	IDCHECK(id4);			// check they ran in order t4,t5,t1,t2
	IDCHECK(id5);
	IDCHECK(id1);
	IDCHECK(id2);
	IDCHECK(id0);
	Kick(t[1]);				// wake up t1
	User::After(50000);		// let thread wait on mutex
	MCOUNT(M1,-1);			// check 1 thread waiting
	t[3].Resume();			// resume pending t3
	MutexSignal();
	User::After(50000);		// let t2 claim mutex
	MutexWait();
	MCOUNT(M1,0);			// check no threads waiting
	IDCHECK(id3);			// check order t3,t1
	IDCHECK(id1);
	IDCHECK(id0);

	for (i=1; i<=5; ++i)
		Kick(t[i]);			// wake up threads
	User::After(50000);		// let threads wait on mutex
	MCOUNT(M1,-5);			// check 5 threads waiting
	t[4].Suspend();			// suspend t4
	MCOUNT(M1,-5);			// check 5 threads waiting
	t[4].Suspend();			// suspend t4 again
	MCOUNT(M1,-5);			// check 5 threads waiting
	MutexSignal();
	User::After(50000);		// let threads claim mutex
	MutexWait();
	MCOUNT(M1,-1);			// check 1 thread still waiting
	IDCHECK(id3);			// check they ran in order t3,t5,t1,t2
	IDCHECK(id5);
	IDCHECK(id1);
	IDCHECK(id2);
	IDCHECK(id0);
	MutexSignal();
	t[4].Resume();
	User::After(50000);		// let threads claim mutex
	MutexWait();
	IDCHECK(id0);			// check thread didn't get mutex (still suspended)
	MutexSignal();
	t[4].Resume();
	User::After(50000);		// let threads claim mutex
	MutexWait();
	IDCHECK(id4);			// check order t4 then this
	IDCHECK(id0);

	for (i=1; i<=5; ++i)
		Kick(t[i]);			// wake up threads
	User::After(50000);		// let threads wait on mutex
	MCOUNT(M1,-5);			// check 5 threads waiting
	MutexWait();			// wait on mutex again
	IDCHECK(id0);
	MutexSignal();			// signal once
	MCOUNT(M1,-5);			// check 5 threads still waiting
	MutexSignal();			// signal again
	MCOUNT(M1,-3);			// check one thread has been woken up and mutex is now free
	User::After(50000);		// let threads claim mutex
	MutexWait();
	MCOUNT(M1,0);			// check no threads still waiting
	IDCHECK(id3);			// check they ran in order t3,t4,t5,t1,t2
	IDCHECK(id4);
	IDCHECK(id5);
	IDCHECK(id1);
	IDCHECK(id2);
	IDCHECK(id0);

	test.Next(_L("Create more threads"));
	for (i=6; i<=10; ++i)
		id[i]=CreateThread(t[i],i,f+i);
	User::After(50000);		// let threads wait on mutex
	MCOUNT(M1,-5);			// check 5 threads waiting
	MutexSignal();
	User::After(50000);		// let threads claim mutex
	MCOUNT(M1,1);			// check no threads still waiting and mutex free
	IDCHECK(id6);			// check they ran in order t6,t7,t8,t9,t10
	IDCHECK(id7);
	IDCHECK(id8);
	IDCHECK(id9);
	IDCHECK(id10);
	t[8].SetPriority(EPriorityMore);	// t1-t3=less, t4-t7=normal, t8-t10 more, t0 much more
	t[9].SetPriority(EPriorityMore);
	t[10].SetPriority(EPriorityMore);
	t[2].SetPriority(EPriorityLess);
	t[3].SetPriority(EPriorityLess);

	RackEmUp(t,f,id);
	SimpleCheck(0,NULL,NULL);	// holding thread still blocked
	Kick(t[4]);
	SimpleCheck(6,id,10,8,9,5,1,2);	// 3,6,7 suspended
	t[3].Resume();
	t[6].Resume();
	t[7].Resume();
	SimpleCheck(3,id,6,7,3);	// 3,6,7 resumed

	RackEmUp(t,f,id);
	SimpleCheck(0,NULL,NULL);	// holding thread still blocked
	Kick(t[4]);
	t[4].Suspend();
	SimpleCheck(0,NULL,NULL);	// holding thread suspended
	t[4].Resume();
	SimpleCheck(6,id,10,8,9,5,1,2);	// 3,6,7 suspended
	t[3].Resume();
	t[6].Resume();
	t[7].Resume();
	SimpleCheck(3,id,6,7,3);	// 3,6,7 resumed

	RackEmUp(t,f,id);
	Kick(t[4]);
	t[4].SetPriority(EPriorityRealTime);
	MCOUNT(M1,-5);			// should be 6 waiting, mutex free
	t[4].SetPriority(EPriorityNormal);
	t[8].SetPriority(EPriorityRealTime);	// change pending thread priority
	MCOUNT(M1,-4);			// should be 5 waiting, mutex free
	t[8].SetPriority(EPriorityMore);
	NUMCHECK(1);
	IDCHECK(id8);
	t[3].SetPriority(EPriorityRealTime);	// change suspended thread priority
	SimpleCheck(5,id,9,10,5,1,2);	// 3,6,7 suspended
	t[6].Resume();
	t[7].Resume();
	t[3].Resume();			// this should run right away
	NUMCHECK(1);
	IDCHECK(id3);
	SimpleCheck(2,id,6,7);	// 6,7 resumed
	t[3].SetPriority(EPriorityLess);

	RackEmUp(t,f,id);
	Kick(t[4]);
	t[1].SetPriority(EPriorityRealTime);	// change waiting thread priority
											// this should run right away
	NUMCHECK(1);
	IDCHECK(id1);
	t[1].SetPriority(EPriorityLess);
	// t8,t9,t10 should now be pending
	MCOUNT(M1,1-5);
	t[8].Suspend();			// this should wake up t5
	t[9].Suspend();			// this should wake up t2
	MCOUNT(M1,1-3);
	t[8].Suspend();			// this should have no further effect
	t[8].Resume();			// this should have no further effect
	MCOUNT(M1,1-3);
	SimpleCheck(3,id,10,5,2);
	MCOUNT(M1,1-3);
	t[3].Resume();
	t[6].Resume();
	t[7].Resume();
	t[8].Resume();
	t[9].Resume();
	SimpleCheck(5,id,8,9,6,7,3);

	RackEmUp(t,f,id);
	MCOUNT(M1,-7);
	t[8].Suspend();			// this shouldn't wake anything up
	t[9].Suspend();			// this shouldn't wake anything up
	MCOUNT(M1,-7);
	Kick(t[4]);
	t[4].SetPriority(EPriorityRealTime);
	MCOUNT(M1,1-6);				// should be 6 waiting, mutex free, t10 pending
	t[4].SetPriority(EPriorityNormal);
	t[10].SetPriority(EPriorityLess);	// this should wake up t5
	MCOUNT(M1,1-5);				// should be 5 waiting, mutex free, t10, t5 pending
	SimpleCheck(4,id,5,10,1,2);
	t[3].SetPriority(EPriorityRealTime);	// boost suspended+waiting thread
	MCOUNT(M1,1-3);			// should be 3 waiting+suspended, mutex free, t8, t9 pending+suspended
	t[6].Resume();
	t[7].Resume();
	t[8].Resume();
	t[9].Resume();
	t[3].Resume();			// this should run immediately
	MCOUNT(M1,1);			// t8,t9,t6,t7 pending, mutex free
	NUMCHECK(1);
	IDCHECK(id3);			// t3 should have run
	t[3].SetPriority(EPriorityLess);
	t[9].SetPriority(EPriorityMuchLess);	// lower pending thread priority
	SimpleCheck(4,id,8,6,7,9);
	t[9].SetPriority(EPriorityMore);
	t[10].SetPriority(EPriorityMore);

	RackEmUp(t,f,id);
	MCOUNT(M1,-7);
	t[8].Suspend();			// this shouldn't wake anything up
	t[9].Suspend();			// this shouldn't wake anything up
	MCOUNT(M1,-7);
	Kick(t[4]);
	MCOUNT(M1,-7);
	t[4].SetPriority(EPriorityRealTime);
	MCOUNT(M1,1-6);			// should be 6 waiting, mutex free, t10 pending, t8,t9 pending+suspended
	t[4].SetPriority(EPriorityNormal);
	t[10].SetPriority(EPriorityMuchLess);	// lower pending thread priority
	MCOUNT(M1,1-5);			// should now be 5 waiting, mutex free, t10,t5 pending, t8,t9 pending+suspended
	t[6].Resume();
	t[7].Resume();
	t[3].Resume();			// this gets made READY straight away
	SimpleCheck(7,id,5,6,7,3,1,2,10);
	t[8].Resume();
	t[9].Resume();
	SimpleCheck(2,id,8,9);
	t[10].SetPriority(EPriorityMore);

	RackEmUp(t,f,id);
	MCOUNT(M1,-7);
	Kick(t[4]);
	t[9].Kill(0);			// kill pending thread
	t[9].Close();
	test(!Exists(9));
	t[1].Kill(0);			// kill waiting thread
	t[1].Close();
	test(!Exists(1));
	t[6].Kill(0);			// kill suspended+waiting thread
	t[6].Close();
	t[7].Resume();
	t[3].Resume();
	test(!Exists(6));
	SimpleCheck(6,id,10,8,5,7,2,3);	// 8 runs first and gets blocked behind 10
	Resurrect(9,EPriorityMore,t,f,id);
	Resurrect(1,EPriorityLess,t,f,id);
	Resurrect(6,EPriorityNormal,t,f,id);

	RackEmUp(t,f,id);
	MCOUNT(M1,-7);
	t[8].Suspend();			// this shouldn't wake anything up
	t[9].Suspend();			// this shouldn't wake anything up
	MCOUNT(M1,-7);
	Kick(t[4]);
	MCOUNT(M1,-7);
	t[4].SetPriority(EPriorityRealTime);
	MCOUNT(M1,1-6);			// should be 6 waiting, mutex free, t10 pending, t8,t9 pending+suspended
	t[4].SetPriority(EPriorityNormal);
	t[10].Kill(0);			// kill pending thread - this should wake up t5
	t[10].Close();
	test(!Exists(10));
	MCOUNT(M1,1-5);			// should be 5 waiting, mutex free, t5 pending, t8,t9 pending+suspended
	t[5].SetPriority(EPriorityRealTime);	// this should make t5 run
	MCOUNT(M1,1-4);			// should be 4 waiting, mutex free, t1 pending, t8,t9 pending+suspended
	t[5].SetPriority(EPriorityNormal);
	NUMCHECK(1);
	IDCHECK(id5);
	t[8].SetPriority(EPriorityRealTime);	// this shouldn't make anything happen
	MCOUNT(M1,1-4);			// mutex free, t1 pending, t8,t9 pending+suspended, t3,t6,t7 wait+susp, t2 waiting
	NUMCHECK(0);
	t[8].Resume();
	MCOUNT(M1,1-3);			// mutex free, t1,t2 pending, t9 pending+suspended, t3,t6,t7 wait+susp
	NUMCHECK(1);
	IDCHECK(id8);
	t[8].SetPriority(EPriorityMore);
	t[3].Resume();
	t[6].Resume();
	t[7].Resume();
	t[9].Resume();
	SimpleCheck(6,id,9,6,7,1,2,3);
	Resurrect(10,EPriorityMore,t,f,id);

	RackEmUp(t,f,id);
	MCOUNT(M1,-7);
	t[8].Suspend();			// this shouldn't wake anything up
	t[9].Suspend();			// this shouldn't wake anything up
	MCOUNT(M1,-7);
	Kick(t[4]);
	MCOUNT(M1,-7);
	t[4].SetPriority(EPriorityRealTime);
	MCOUNT(M1,1-6);			// mutex free, t10 pending, t8,t9 pending+susp, t3,t6,t7 wait+susp, t1,t2,t5 wait
	t[4].SetPriority(EPriorityNormal);
	t[1].SetPriority(EPriorityRealTime);	// this should be able to run and claim the mutex
	NUMCHECK(1);
	IDCHECK(id1);
	MCOUNT(M1,1-4);			// mutex free, t10,t5 pending, t8,t9 pending+susp, t3,t6,t7 wait+susp, t2 wait
	t[1].SetPriority(EPriorityLess);
	t[3].Resume();
	t[6].Resume();
	t[7].Resume();
	t[9].Resume();
	t[8].Resume();
	SimpleCheck(8,id,10,9,8,5,6,7,3,2);

	RackEmUp(t,f,id);
	MCOUNT(M1,-7);
	Kick(t[4]);
	M1.Close();				// close the mutex - non-suspended threads should all panic with KERN-EXEC 0
	TBool jit = User::JustInTime();
	User::SetJustInTime(EFalse);
	User::After(1000000);
	User::SetJustInTime(jit);
	for (i=1; i<=10; ++i)
		{
		if (i==3 || i==6 || i==7)
			{
			test_Equal(EExitPending, t[i].ExitType());
			}
		else
			{
			test_Equal(EExitPanic, t[i].ExitType());
			test_Equal(EBadHandle, t[i].ExitReason());
			test(t[i].ExitCategory()==_L("KERN-EXEC"));
			t[i].Close();
			test(!Exists(i));
			}
		}
	t[3].Resume();
	t[6].Resume();
	t[7].Resume();
	User::SetJustInTime(EFalse);
	User::After(1000000);
	User::SetJustInTime(jit);
	for (i=1; i<=10; ++i)
		{
		if (i==3 || i==6 || i==7)
			{
			test_Equal(EExitPanic, t[i].ExitType());
			test_Equal(EBadHandle, t[i].ExitReason());
			test(t[i].ExitCategory()==_L("KERN-EXEC"));
			t[i].Close();
			test(!Exists(i));
			}
		}

	test.End();
	}

/*****************************************************************************
 * Mutex benchmarks
 *****************************************************************************/
TInt MutexSpeed(TAny* aPtr)
	{
	TInt& count=*(TInt*)aPtr;
	RThread().SetPriority(EPriorityMore);
	FOREVER
		{
		M1.Wait();
		M1.Signal();
		++count;
		}
	}

TInt MutexSpeed2(TAny* aPtr)
	{
	TInt& count=*(TInt*)aPtr;
	RThread().SetPriority(EPriorityMore);
	FOREVER
		{
		M1.Wait();
		M1.Wait();
		M1.Signal();
		M1.Signal();
		++count;
		}
	}

void TestMutexSpeed()
	{
	test.Start(_L("Test mutex speed"));
	TInt count=0;
	TInt r=M1.CreateLocal();
	test_KErrNone(r);

	RThread t;
	r=t.Create(_L("Speed"),MutexSpeed,0x1000,NULL,&count);
	test_KErrNone(r);
	t.SetPriority(EPriorityRealTime);
	t.Resume();
	User::AfterHighRes(1000000);
	t.Kill(0);
	t.Close();
	test(!Exists(_L("Speed")));
	test.Printf(_L("%d wait/signal in 1 second\n"),count);

	TInt count2=0;
	r=t.Create(_L("Speed2"),MutexSpeed2,0x1000,NULL,&count2);
	test_KErrNone(r);
	t.SetPriority(EPriorityRealTime);
	t.Resume();
	User::AfterHighRes(1000000);
	t.Kill(0);
	t.Close();
	test(!Exists(_L("Speed2")));
	test.Printf(_L("%d double wait/signal in 1 second\n"),count2);

	M1.Close();
	test.End();
	}

/*****************************************************************************
 * Utilities for semaphore test
 *****************************************************************************/
void SemWait()
	{
	S.Wait();
	++Count;
	ThreadId[PutIx++]=(TUint)RThread().Id();
	}

void SemSignal()
	{
	S.Signal();
	}

TInt SemThreadFunction(TAny* aPtr)
	{
	PFV& f=*(PFV*)aPtr;
	FOREVER
		{
		SemWait();
		if (f)
			f();
		SemSignal();
		User::WaitForAnyRequest();
		}
	}

void Wait()
	{
	User::WaitForAnyRequest();
	}

TUint CreateSemThread(RThread& t, TInt n, TAny* aPtr)
	{
	TBuf<4> b;
	b.Num(n);
	TInt r=t.Create(b,SemThreadFunction,0x1000,NULL,aPtr);
	test_KErrNone(r);
	t.Resume();
	TUint id=t.Id();
	return id;
	}

/*
Possible thread relationships with semaphore:
	Waiting
	Waiting + suspended

Need to verify correct behaviour when the following actions occur for each of these states:
	Suspend thread
	Resume thread
	Change thread priority
	Thread exits
	Thread is killed
	Semaphore deleted
*/

void RackEmUp2(RThread* t, PFV* f, TUint* id)
	{
	// set up
	// t1, t2, t4, t5, t6, t8, t9 waiting
	// t3, t7, t10 waiting+suspended
	(void)f;
	MCOUNT(S,2);			// check semaphore level = 2
	SemWait();
	SemWait();
	MCOUNT(S,0);			// check semaphore level = 0
	NUMCHECK(2);
	IDCHECK(id0);
	IDCHECK(id0);
	TInt i;
	for (i=1; i<=10; ++i)
		Kick(t[i]);			// wake up threads
	User::After(50000);		// let threads wait
	MCOUNT(S,-10);			// check 10 threads waiting
	t[7].Resume();			// test resume when not suspended
	MCOUNT(S,-10);			// check 7 threads waiting
	t[3].Suspend();
	t[7].Suspend();
	t[10].Suspend();		// now have required state
	t[3].Suspend();			// suspend and resume t3 again for good measure
	t[3].Resume();
	MCOUNT(S,-7);			// check 7 threads waiting
	}

void Resurrect2(TInt n, TThreadPriority aPriority, RThread* t, PFV* f, TUint* id)
	{
	f[n]=NULL;
	id[n]=CreateSemThread(t[n],n,f+n);
	t[n].SetPriority(EPriorityRealTime);
	t[n].SetPriority(aPriority);
	NUMCHECK(1);
	IDCHECK(id[n]);
	}

/*****************************************************************************
 * Semaphore exhaustive state transition test
 *****************************************************************************/
void TestSemaphore()
	{
	test.Start(_L("Test semaphore state transitions"));
	RThread t[11];
	TUint id[11];
	PFV f[11]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
	id[0]=(TUint)RThread().Id();
	PutIx=0;
	GetIx=0;
	Count=0;
	test.Next(_L("Create semaphore"));
	TInt r=S.CreateLocal(2);
	test_KErrNone(r);
	MCOUNT(S,2);
	SemWait();
	MCOUNT(S,1);
	SemSignal();
	MCOUNT(S,2);
	SemWait();
	SemWait();
	MCOUNT(S,0);
	S.Signal(2);
	MCOUNT(S,2);
	NUMCHECK(3);
	IDCHECK(id0);
	IDCHECK(id0);
	IDCHECK(id0);
	test.Next(_L("Create threads"));
	TInt i;
	for (i=1; i<=10; ++i)
		{
		id[i]=CreateSemThread(t[i],i,f+i);
		f[i]=&Wait;
		}
	t[8].SetPriority(EPriorityMore);	// t1-t3=less, t4-t7=normal, t8-t10 more, t0 much more
	t[9].SetPriority(EPriorityMore);
	t[10].SetPriority(EPriorityMore);
	t[1].SetPriority(EPriorityLess);
	t[2].SetPriority(EPriorityLess);
	t[3].SetPriority(EPriorityLess);
	User::After(50000);
	MCOUNT(S,-8);			// check 8 waiting
	NUMCHECK(2);
	IDCHECK(id8);
	IDCHECK(id9);			// check t8,t9 got through
	t[8].SetPriority(EPriorityRealTime);
	Kick(t[8]);				// let t8 run and signal
	t[8].SetPriority(EPriorityMore);
	MCOUNT(S,-7);			// check 7 waiting
	User::After(50000);		// let next thread obtain semaphore
	MCOUNT(S,-7);			// check 7 waiting
	NUMCHECK(1);
	IDCHECK(id10);			// check t10 got it
	Kick(t[10]);			// let t10 run and signal
	User::After(50000);		// let next thread obtain semaphore
	MCOUNT(S,-6);			// check 6 waiting
	NUMCHECK(1);
	IDCHECK(id4);			// check t4 got it
	t[1].SetPriority(EPriorityRealTime);	// boost t1
	MCOUNT(S,-6);			// check 6 still waiting
	User::After(50000);		// let next thread obtain semaphore
	MCOUNT(S,-6);			// check 6 still waiting
	NUMCHECK(0);
	Kick(t[9]);				// make t9 ready to run and signal
	MCOUNT(S,-6);			// check 6 still waiting
	User::After(50000);		// let next thread obtain semaphore
	MCOUNT(S,-5);			// check 5 waiting
	NUMCHECK(1);
	IDCHECK(id1);			// check t1 got it
	t[1].SetPriority(EPriorityLess);
	Kick(t[1]);				// kick all remaining threads
	Kick(t[2]);
	Kick(t[3]);
	Kick(t[4]);
	Kick(t[5]);
	Kick(t[6]);
	Kick(t[7]);
	User::After(50000);		// let them run and obtain/signal the semaphore
	MCOUNT(S,2);			// check semaphore now back to initial level
	SimpleCheck(5,id,5,6,7,2,3);

	for (i=1; i<=10; ++i)
		f[i]=NULL;
	RackEmUp2(t,f,id);		// set up threads waiting on semaphore again
	S.Signal();
	SimpleCheck(7,id,8,9,4,5,6,1,2);	// let them go
	MCOUNT(S,1);
	S.Wait();
	t[3].SetPriority(EPriorityRealTime);	// change suspended thread priority
	t[7].Resume();
	SimpleCheck(0,id);		// t7 should wait for signal
	S.Signal();
	SimpleCheck(1,id,7);
	MCOUNT(S,1);
	t[3].Resume();
	t[10].Resume();
	NUMCHECK(1);
	IDCHECK(id3);			// t3 should have grabbed semaphore as soon as we resumed it
	SimpleCheck(1,id,10);
	t[3].SetPriority(EPriorityLess);
	S.Signal();				// put level back to 2

	RackEmUp2(t,f,id);		// set up threads waiting on semaphore again
	S.Signal();
	SimpleCheck(7,id,8,9,4,5,6,1,2);	// let them go
	MCOUNT(S,1);
	S.Wait();
	t[3].SetPriority(EPriorityRealTime);	// change suspended thread priority
	t[7].Resume();
	SimpleCheck(0,id);		// t7 should wait for signal
	S.Signal();
	SimpleCheck(1,id,7);
	MCOUNT(S,1);
	t[10].Resume();
	t[3].Resume();			// t3 not woken up here since t10 has already been given the semaphore
	NUMCHECK(0);
	SimpleCheck(2,id,10,3);
	t[3].SetPriority(EPriorityLess);
	S.Signal();				// put level back to 2

	RackEmUp2(t,f,id);		// set up threads waiting on semaphore again
	S.Signal();
	SimpleCheck(7,id,8,9,4,5,6,1,2);	// let them go
	MCOUNT(S,1);
	S.Wait();
	t[3].SetPriority(EPriorityRealTime);	// change suspended thread priority
	t[7].Resume();
	SimpleCheck(0,id);		// t7 should wait for signal
	S.Signal();
	S.Signal();				// put level back to 2
	SimpleCheck(1,id,7);
	MCOUNT(S,2);
	t[10].Resume();
	t[3].Resume();			// t3 and t10 both woken up here, t3 should run and signal
	MCOUNT(S,1);
	NUMCHECK(1);
	IDCHECK(id3);
	SimpleCheck(1,id,10);
	t[3].SetPriority(EPriorityLess);

	RackEmUp2(t,f,id);		// set up threads waiting on semaphore again
	t[9].Kill(0);			// kill waiting thread
	t[9].Close();
	test(!Exists(9));
	t[10].Kill(0);			// kill suspended thread
	t[10].Close();
	test(!Exists(10));
	MCOUNT(S,-6);
	f[5]=&Exit;				// get t5 to exit after acquiring semaphore
	S.Signal();
	SimpleCheck(3,id,8,4,5);	// let them go
	MCOUNT(S,-3);			// check one signal has been lost due to t5 exiting
	t[5].Close();
	test(!Exists(5));
	t[3].Resume();
	t[7].Resume();
	MCOUNT(S,-5);
	S.Signal();
	SimpleCheck(5,id,6,7,1,2,3);	// let them go
	MCOUNT(S,1);
	Resurrect2(9,EPriorityMore,t,f,id);
	Resurrect2(10,EPriorityMore,t,f,id);
	Resurrect2(5,EPriorityNormal,t,f,id);
	S.Signal();

	RackEmUp2(t,f,id);		// set up threads waiting on semaphore again
	f[5]=&Exit;				// get t5 to exit after acquiring semaphore
	S.Close();				// close semaphore - threads should panic except for 5

	TBool jit = User::JustInTime();
	User::SetJustInTime(EFalse);
	User::After(1000000);
	User::SetJustInTime(jit);
	for (i=1; i<=10; ++i)
		{
		if (i==3 || i==7 || i==10)
			{
			test_Equal(EExitPending, t[i].ExitType());
			}
		else if (i!=5)
			{
			test_Equal(EExitPanic, t[i].ExitType());
			test_Equal(EBadHandle, t[i].ExitReason());
			test(t[i].ExitCategory()==_L("KERN-EXEC"));
			t[i].Close();
			test(!Exists(i));
			}
		else
			{
			test_Equal(EExitKill, t[i].ExitType());
			test_Equal(0, t[i].ExitReason());
			t[i].Close();
			test(!Exists(i));
			}
		}
	t[3].Resume();
	t[7].Resume();
	t[10].Resume();
	User::SetJustInTime(EFalse);
	User::After(1000000);
	User::SetJustInTime(jit);
	for (i=1; i<=10; ++i)
		{
		if (i==3 || i==7 || i==10)
			{
			test_Equal(EExitPanic, t[i].ExitType());
			test_Equal(EBadHandle, t[i].ExitReason());
			test(t[i].ExitCategory()==_L("KERN-EXEC"));
			t[i].Close();
			test(!Exists(i));
			}
		}

	test.End();
	}

/*****************************************************************************
 * Semaphore benchmarks
 *****************************************************************************/
TInt SemSpeed(TAny* aPtr)
	{
	TInt& count=*(TInt*)aPtr;
	RThread().SetPriority(EPriorityMore);
	FOREVER
		{
		S.Wait();
		S.Signal();
		++count;
		}
	}

void TestSemSpeed()
	{
	test.Start(_L("Test semaphore speed"));
	TInt count=0;
	TInt r=S.CreateLocal(1);
	test_KErrNone(r);

	RThread t;
	r=t.Create(_L("SemSpeed"),SemSpeed,0x1000,NULL,&count);
	test_KErrNone(r);
	t.SetPriority(EPriorityRealTime);
	t.Resume();
	User::AfterHighRes(1000000);
	t.Kill(0);
	t.Close();
	test(!Exists(_L("SemSpeed")));
	test.Printf(_L("%d wait/signal in 1 second\n"),count);

	S.Close();
	test.End();
	}


GLDEF_C TInt E32Main()
	{
	TInt cpus = UserSvr::HalFunction(EHalGroupKernel, EKernelHalNumLogicalCpus, 0, 0);
	if (cpus != 1)
		{
		test(cpus>1);
		// This test will require compatibility mode (and probably other changes)
		// to work on SMP - it depends on explicit scheduling order.
		test.Printf(_L("T_SEMUTX2 skipped, does not work on SMP\n"));
		return KErrNone;
		}	
	
	test.Title();

	test.Start(_L("Test mutexes and semaphores"));
	RThread().SetPriority(EPriorityMuchMore);
	TInt r=Main.Duplicate(RThread());
	test_KErrNone(r);

	Test0();
	Test1();
	TestMutex1();
	TestMutex2();
	TestSemaphore();
	
	TestMutexSpeed();
	TestSemSpeed();

	Main.Close();
	test.End();
	return KErrNone;
	}