// 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:
// 
//

#include <e32test.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(GetNextId()==(x))
#define NUMCHECK(x)	test(NumIdsPending()==(x))

#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(r==KErrNone);
	t.Logon(s);
	test(r==KErrNone);
	User::After(10000);		// make sure we have a full timeslice
	t.Resume();

	test(count==0);			// t shouldn't have run yet
	RThread().SetPriority(EPriorityMuchMore);	// shouldn't reschedule (priority unchanged)
	test(count==0);
	RThread().SetPriority(EPriorityMore);	// shouldn't reschedule (priority decreasing, but not enough)
	test(count==0);
	RThread().SetPriority(EPriorityMuchMore);	// shouldn't reschedule (priority increasing)
	test(count==0);
	RThread().SetPriority(EPriorityNormal);	// should reschedule (we go behind t)
	test(count==1);
	RThread().SetPriority(EPriorityLess);	// should reschedule (priority decreasing to below t)
	test(count==2);
	t.SetPriority(EPriorityMuchMore);		// shouldn't reschedule (round-robin, timeslice not expired)
	test(count==2);
	t.SetPriority(EPriorityNormal);			// shouldn't reschedule (t's priority decreasing)
	test(count==2);
	t.SetPriority(EPriorityNormal);			// shouldn't reschedule (t's priority unchanged)
	test(count==2);
	BusyWait(100000);		// use up our timeslice
	t.SetPriority(EPriorityMuchMore);		// should reschedule (round-robin, timeslice expired)
	test(count==3);
	test(s==KRequestPending);
	test(t.ExitType()==EExitPending);
	t.SetPriority(EPriorityRealTime);		// should reschedule (t increases above current)
	test(count==4);
	test(s==KErrNone);						// t should have exited
	test(t.ExitType()==EExitKill);
	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(r==KErrNone);
	M1.Wait();
	RThread t;
	r=t.Create(_L("Test1"),Test1Thread,0x1000,NULL,NULL);
	test(r==KErrNone);
	TRequestStatus s;
	t.Logon(s);
	t.Resume();
	TBool jit = User::JustInTime();
	User::SetJustInTime(EFalse);
	User::WaitForRequest(s);
	User::SetJustInTime(jit);
	test(s==EAccessDenied);
	test(t.ExitType()==EExitPanic);
	test(t.ExitReason()==EAccessDenied);
	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(r==KErrNone);

	test.Next(_L("Create low priority thread"));
	TInt lowcount=0;
	RThread low;
	r=low.Create(_L("low"),LowThread,0x1000,NULL,&lowcount);
	test(r==KErrNone);
	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(r==KErrNone);
	med.SetPriority(EPriorityNormal);
	test(Exists(_L("med")));

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

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

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

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

	test.Next(_L("Create high priority thread"));
	TInt highcount=0;
	RThread high;
	r=high.Create(_L("high"),HighThread,0x1000,NULL,&highcount);
	test(r==KErrNone);
	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(highcount==1);

	M2.Wait();
	test(lowcount==2);
	test(medcount==3);
	test(highcount==2);
	test.Next(_L("Wait, check medium runs"));
	User::AfterHighRes(KTestDelay/10);
	test(medcount==4);
	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(r==KErrNone);
	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(r==KErrNone);
	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(t[2].ExitType()==EExitKill);	// 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(t[i].ExitType()==EExitPending);
			}
		else
			{
			test(t[i].ExitType()==EExitPanic);
			test(t[i].ExitReason()==EBadHandle);
			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(t[i].ExitType()==EExitPanic);
			test(t[i].ExitReason()==EBadHandle);
			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(r==KErrNone);

	RThread t;
	r=t.Create(_L("Speed"),MutexSpeed,0x1000,NULL,&count);
	test(r==KErrNone);
	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(r==KErrNone);
	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(r==KErrNone);
	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(r==KErrNone);
	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(t[i].ExitType()==EExitPending);
			}
		else if (i!=5)
			{
			test(t[i].ExitType()==EExitPanic);
			test(t[i].ExitReason()==EBadHandle);
			test(t[i].ExitCategory()==_L("KERN-EXEC"));
			t[i].Close();
			test(!Exists(i));
			}
		else
			{
			test(t[i].ExitType()==EExitKill);
			test(t[i].ExitReason()==0);
			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(t[i].ExitType()==EExitPanic);
			test(t[i].ExitReason()==EBadHandle);
			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(r==KErrNone);

	RThread t;
	r=t.Create(_L("SemSpeed"),SemSpeed,0x1000,NULL,&count);
	test(r==KErrNone);
	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()
	{
	test.Title();

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

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

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

