kerneltest/e32test/thread/t_thread.cpp
author Mike Kinghan <mikek@symbian.org>
Thu, 25 Nov 2010 14:35:45 +0000
branchGCC_SURGE
changeset 305 1ba12ef4ef89
parent 102 ef2a444a7410
child 257 3e88ff8f41d5
child 269 d57b86b1867a
permissions -rw-r--r--
Enhance the base/rom extension to generate the symbol file of the rom built. The symbol file is placed in epoc32/rom/<baseport_name>, along with the rom log and final oby file.

// 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\thread\t_thread.cpp
// Overview:
// Tests the RThread class
// API Information:
// RThread, RUndertaker
// Details:
// - Create a thread, verify its priority, change the priority and verify results.
// - Verify naming and renaming threads works as expected.
// - Test logging on, resuming and closing a thread. Verify results are as expected.
// - Test creating threads with a variety of invalid parameters. Verify results.
// - Test the RUndertaker methods: create some threads, logon to the undertaker,
// verify results upon killing a thread and setting the thread handle.
// - Check kernel allocation when creating threads and undertakers. Verify the
// heap has not been corrupted.
// - Run a thread multiple times, panic within the thread, panic external to the
// thread and exit a thread in a variety of ways. Verify results are as expected.
// - Create a semaphore and some threads, verify the threads can wait on and signal
// the semaphore.
// - Test unclosed but completed threads.
// - Suspend and resume some threads in a variety of ways, verify results are as
// expected.
// - Test thread duplication.
// - Test opening a thread using an full name, perform various tests by finding, 
// killing, closing, recreating etc. Verify the results are as expected.
// - Test creating a thread using a duplicate name then reuse the thread with a
// valid name. Verify results are as expected.
// - Verify that a panicked thread releases an existing mutex.
// - Test thread ID: attempt to open a nonexistent thread by ID, verify different
// threads have different IDs, verify open by ID works as expected.
// - Test RThread::StackInfo(), print results and verify results are as expected.
// Platforms/Drives/Compatibility:
// All.
// Assumptions/Requirement/Pre-requisites:
// Failures and causes:
// Base Port information:
// 
//

#define __E32TEST_EXTENSION__
#include <e32test.h>
#include <e32panic.h>
#include <e32svr.h>
#include <u32hal.h>
#include <e32atomics.h>
#include <e32def.h>
#include <e32def_private.h>
#include "../misc/prbs.h"

const TInt KNumThreads=20;

const TInt KExitPanicNum=999;
const TInt KHeapSize=0x200;
const TInt KThreadReturnValue=9999;
const TInt KTerminationReason=1234;
const TInt KKillReason=4321;  
enum TInstruction {ENormal,EInstrPanic,EWait};

const TInt KWaitTime=800000;

class TReadInfo
	{
public:
	TDesC* tdesc;
	TPtrC* tptrc;
	TDes* tdes;
	TPtr* tptr;
	HBufC* hbufc;
	TBufC<0x20>* tbufc;
	TBuf<0x20>* tbuf;
	TPtr* tptrdes;
	TAny* anAddress;
	};

LOCAL_D RTest test(_L("T_THREAD"));
LOCAL_D RTest rtest(_L("Read thread tests"));
LOCAL_D	RTest wtest(_L("Write thread tests"));

#define rtest(x) rtest(x,__LINE__)
#define wtest(x) wtest(x,__LINE__)

LOCAL_C TInt LoopyThread(TAny*)
	{
	
	FOREVER
		User::AfterHighRes(1000);
	}

LOCAL_D void testUndertaker(TOwnerType anOwnerType)
//
// Test RThreadWatcher
//
	{

	RThread thread1;
	TInt r;
	test.Start(_L("Test the RUndertaker class"));
	test.Next(_L("Create a thread"));
//	if (anOwnerType==EOwnerThread)
//		User::SetDebugMask(0x8000867c);
	r=thread1.Create(_L("Loopy1"),LoopyThread,KDefaultStackSize,KHeapSize,KHeapSize,(TAny*)NULL,anOwnerType);
	test(r==KErrNone);
	thread1.Resume();

	TRequestStatus stat1;
	TInt threadHandle1;
	RThread w1;
	RUndertaker u1;
	test.Next(_L("Create an RUndertaker"));
	r=u1.Create();
	test(r==KErrNone);
	test.Next(_L("Logon to RUndertaker"));
	r=u1.Logon(stat1,threadHandle1);
	test(r==KErrNone);
	test.Next(_L("Logon again & check we're rejected"));
	r=u1.Logon(stat1,threadHandle1);
	test(r==KErrInUse);
	test.Next(_L("Cancel logon to RUndertaker"));
	r=u1.LogonCancel();
	test(r==KErrNone);
	test(stat1==KErrCancel);

	test.Next(_L("Logon to RUndertaker again"));
	u1.Logon(stat1,threadHandle1);
	
	test.Next(_L("Create another thread"));
	RThread thread2;
	r=thread2.Create(_L("Loopy2"),LoopyThread,KDefaultStackSize,KHeapSize,KHeapSize,(TAny*)NULL,anOwnerType);
	test(r==KErrNone);
	thread2.Resume();

	TRequestStatus stat2;
	TInt threadHandle2;
	RThread w2;
	RUndertaker u2;
	test.Next(_L("Create another RUndertaker"));
	r=u2.Create();
	test(r==KErrNone);
	test.Next(_L("Logon to RUndertaker"));
	r=u2.Logon(stat2,threadHandle2);
	test(r==KErrNone);

	test.Next(_L("Create yet another thread"));
	RThread thread3;
	r=thread3.Create(_L("Loopy3"),LoopyThread,KDefaultStackSize,KHeapSize,KHeapSize,(TAny*)NULL,anOwnerType);
	test(r==KErrNone);
	thread3.Resume();

	test.Next(_L("Kill the first thread & check the undertakers"));
	thread1.Kill(0x489);
	thread1.Close();

	User::WaitForRequest(stat1);
	User::WaitForRequest(stat2);
	test(stat1==KErrDied);
	test(stat2==KErrDied);

	RThread keep1;
	RThread keep2;
	test.Next(_L("Set the RThread handles"));
	keep1.SetHandle(threadHandle1);
	keep2.SetHandle(threadHandle2);

	test.Next(_L("Test the exit reasons"));
	test(keep1.ExitReason()==0x489);
	test(keep2.ExitReason()==0x489);
//	test.Printf(_L("Thread name %S\n"),&(w1.Name()));

	test.Next(_L("Logon again with both watchers"));
	r=u1.Logon(stat1,threadHandle1);
	test(r==KErrNone);
	r=u2.Logon(stat2,threadHandle2);
	test(r==KErrNone);

	test.Next(_L("Kill the 3rd thread & check the undertakers"));
	thread3.Kill(0x999);
	thread3.Close();

	User::WaitForRequest(stat1);
	User::WaitForRequest(stat2);
	test(stat1==KErrDied);
	test(stat2==KErrDied);

	test.Next(_L("Set the RThread handles"));
	w1.SetHandle(threadHandle1);
	w2.SetHandle(threadHandle2);

	test.Next(_L("Test the exit reasons"));
	test(w1.ExitReason()==0x999);
	test(w2.ExitReason()==0x999);
//	test.Printf(_L("Thread name %S\n"),&(w1.Name()));
	w1.Close();
	CLOSE_AND_WAIT(w2);

	test.Next(_L("Logon again with both undertakers"));
	r=u1.Logon(stat1,threadHandle1);
	test(r==KErrNone);
	r=u2.Logon(stat2,threadHandle2);
	test(r==KErrNone);

	test.Next(_L("Kill the 2nd thread & check the undertakers"));
	thread2.Kill(0x707);
	thread2.Close();

	User::WaitForRequest(stat1);
	User::WaitForRequest(stat2);
	test(stat1==KErrDied);
	test(stat2==KErrDied);

	test.Next(_L("Set the RThread handles"));
	w1.SetHandle(threadHandle1);
	w2.SetHandle(threadHandle2);

	test.Next(_L("Test the exit reasons"));
	test(w1.ExitReason()==0x707);
	test(w2.ExitReason()==0x707);
//	test.Printf(_L("Thread name %S\n"),&(w1.Name()));

	test.Next(_L("Check kernel allocation"));
	test.Next(_L("Please wait while I create & close masses of threads"));
	RThread t[KNumThreads];
	TInt j;
	for (j=0; j<KNumThreads; j++)
		{
		TBuf<0x10> name;
		name.Format(_L("LoopyThread%d"),j);
		test(t[j].Create(name, LoopyThread, KDefaultStackSize, KHeapSize, KHeapSize, (TAny*)NULL,anOwnerType)==KErrNone);
		}
	for (j=0; j<KNumThreads-1; j++)
		{
		t[j].Kill(666);
		CLOSE_AND_WAIT(t[j]);
		}

	test.Next(_L("Please wait while I close & create some undertakers"));
	u1.Close();
	u2.Close();
	r=u1.Create();
	test(r==KErrNone);
	r=u2.Create();
	test(r==KErrNone);

	test.Next(_L("Mark kernel heap"));
	__KHEAP_MARK;

	test.Next(_L("Create thread"));
	RThread threadx;
	r=threadx.Create(_L("Loopyx"),LoopyThread,KDefaultStackSize,KHeapSize,KHeapSize,(TAny*)NULL,anOwnerType);
	test(r==KErrNone);
	test.Next(_L("Resume thread"));
	threadx.Resume();

	test.Next(_L("Create undertaker"));
	TRequestStatus statx;
	TInt threadHandlex;
	RUndertaker ux;
	r=ux.Create();
	test(r==KErrNone);
	test.Next(_L("Logon to undertaker"));
	r=ux.Logon(statx,threadHandlex);
	test(r==KErrNone);
	test.Next(_L("Kill thread"));
	threadx.Kill(0x666);
	threadx.Close();
	User::WaitForRequest(statx);
	test(statx==KErrDied);
	test.Next(_L("Close thread"));
	RThread wx;
	wx.SetHandle(threadHandlex);
	CLOSE_AND_WAIT(wx);

	test.Next(_L("Close undertaker"));
	ux.Close();

	test.Next(_L("Check kernel heap"));
	__KHEAP_MARKEND;
	w1.Close();
	CLOSE_AND_WAIT(w2);
	keep1.Close();
	CLOSE_AND_WAIT(keep2);
	t[KNumThreads-1].Kill(666);
	CLOSE_AND_WAIT(t[KNumThreads-1]);

	test.Next(_L("Close RUndertakers"));
	u1.Close();
	u2.Close();
	test.End();
	}

volatile TInt IFLAG;

TInt InstructionThread(TAny* anInstruction)
	{
	__e32_atomic_store_ord32(&IFLAG, 1);
	RThread::Rendezvous(KErrNone);
	TInstruction what=(TInstruction)(TInt)anInstruction;
	if (what==EInstrPanic)
		User::Panic(_L("Hello"), KExitPanicNum);
	if (what==ENormal)
		return(KThreadReturnValue);
	User::After(500000);
	return(KErrNone);
	}

TInt StartInstructionThread(RThread& aT, const TDesC& aName, TInt aInstruction, TOwnerType aOwnerType, TRequestStatus* aL, TRequestStatus* aR)
	{
	TInt r = aT.Create(aName, &InstructionThread, KDefaultStackSize, KHeapSize, KHeapSize, (TAny*)aInstruction, aOwnerType);
	if (r!=KErrNone)
		return r;
	if (aL)
		{
		aT.Logon(*aL);
		TInt s = aL->Int();
		test_Equal(s, KRequestPending);
		}
	if (aR)
		{
		aT.Rendezvous(*aR);
		TInt s = aR->Int();
		test_Equal(s, KRequestPending);
		aT.Resume();
		User::WaitForRequest(*aR);
		s = aR->Int();
		test_KErrNone(s);
		}
	return r;
	}


LOCAL_D TInt test4Thread(TAny *aSem)
//
// Wait to be released on the semaphore.
// Then release the semaphore.
//
	{

	RSemaphore& sem=(*(RSemaphore*)aSem);
	sem.Wait();
	sem.Signal();
	return(KErrNone);
	}

TInt BadPriority(TAny* aThread)
	{
	((RThread*)aThread)->SetPriority(EPriorityNull);
	return KErrNone;
	}

_LIT(KLitRomString,"RomStringRomStringRomStringRomStringRomStringRomStringRomString");

LOCAL_C TInt BadFullNameThread(TAny* aPar)
	{
	RThread thread;
	
	switch ((TInt)aPar)
		{
		case 0:
			{
			HBufC* hBuf = HBufC::New(5);//Size 5 is not sufficient. thread.FullName should panic.
			test(NULL != hBuf);
			RBuf rBuf(hBuf);
			thread.FullName(rBuf);
			rBuf.Close();
			}
			return(KErrNone);

		case 1:
			{
			TPtr ptr((TText*)(KLitRomString.iBuf), KLitRomString.iTypeLength);
			// Passing descriptor whose data is in ROM. This may behave in different ways
			// on differrent platforms. Here, we just check that Kernel is safe.
			thread.FullName(ptr);
			}
			return(KErrNone);
		}
	return(KErrArgument);
	}


LOCAL_D void test1()
//
// Test 1
//
	{
	
	__UHEAP_MARK;
	RThread thread;
	TRequestStatus stat;
	TInt r;

	test.Start(_L("Close without create"));
	thread.Close();
	
	test.Next(_L("Create ENormal"));
	r = StartInstructionThread(thread, _L("Thread"), ENormal, EOwnerProcess, 0, 0);
	test_KErrNone(r);

	test.Next(_L("Test priorities"));
	test(thread.Priority()==EPriorityNormal);
	thread.SetPriority(EPriorityRealTime);	// WINS will commute this to EPriorityMuchMore
#if defined(__EPOC32__)
	test(thread.Priority()==EPriorityRealTime);
#endif
	thread.SetPriority(EPriorityMuchMore);
	test(thread.Priority()==EPriorityMuchMore);
//	thread.SetPriority(EPriorityNull);
	RThread badThread;
	r = badThread.Create(_L("Bad Priority"),BadPriority,KDefaultStackSize,KHeapSize,KHeapSize,&thread);
	test(r==KErrNone);
	badThread.Logon(stat);
	test(stat==KRequestPending);
	badThread.Resume();
	User::WaitForRequest(stat);
	test(stat==EBadPriority);
	test(badThread.ExitCategory()==_L("KERN-EXEC"));
	test(badThread.ExitReason()==EBadPriority);
	test(badThread.ExitType()==EExitPanic);
	CLOSE_AND_WAIT(badThread);
	test(thread.Priority()==EPriorityMuchMore);

#if defined(__EPOC32__)
	test.Next(_L("Test setting process priority from thread"));
	test(thread.ProcessPriority()==EPriorityForeground);
	thread.SetProcessPriority(EPriorityHigh);
	test(thread.ProcessPriority()==EPriorityHigh);
	test(RProcess().Priority()==EPriorityHigh);
	thread.SetProcessPriority(EPriorityForeground);
	test(thread.ProcessPriority()==EPriorityForeground);
	test(RProcess().Priority()==EPriorityForeground);
#endif

	TBuf<0x100> name;
	test.Next(_L("Test thread name"));
	test(thread.Name()==_L("Thread"));
	test.Next(_L("Get owning process name"));
	RProcess p;
	test(thread.Process(p)==KErrNone);
	name=p.Name();
	name.Append(_L("::"));
	name.Append(thread.Name());
	test.Next(_L("Test fullname - via TFullName RHandleBase::FullName"));
	test(thread.FullName().CompareF(name)==0);

	test.Next(_L("Test fullname - via void RHandleBase::FullName(TDes& aName)"));
	HBufC* hBuf = HBufC::New(100);
	test(NULL != hBuf);
	TPtr ptr = hBuf->Des();
	thread.FullName(ptr);
	test(ptr.CompareF(name)==0);
	RBuf rBuf(hBuf);
	thread.FullName(rBuf);
	test(rBuf.CompareF(name)==0);
	rBuf.Close();

	test.Next(_L("Test void RHandleBase::FullName(TDes& aName) when aName is too short"));
	TInt aaa=badThread.Create(_L("BadFullNameThread1"),BadFullNameThread,KDefaultStackSize,KHeapSize,KHeapSize,(TAny*)0);
	test(aaa==KErrNone);
	badThread.Logon(stat);
	test(badThread.ExitType()==EExitPending);
	badThread.Resume();
	User::WaitForRequest(stat);
	test(badThread.ExitCategory()==_L("KERN-EXEC"));
	test(badThread.ExitReason()==EKUDesSetLengthOverflow);
	test(badThread.ExitType()==EExitPanic);
	CLOSE_AND_WAIT(badThread);

	test.Next(_L("Test void RHandleBase::FullName(TDes& aName) where aName has data in ROM "));
	aaa=badThread.Create(_L("BadFullNameThread2"),BadFullNameThread,KDefaultStackSize,KHeapSize,KHeapSize,(TAny*)1);
	test(aaa==KErrNone);
	badThread.Logon(stat);
	test(badThread.ExitType()==EExitPending);
	badThread.Resume();
	User::WaitForRequest(stat);
	test.Printf(_L("BadFullNameThread2 exited with ExitReason=%d and ExitType=%d\n"),badThread.ExitReason(),badThread.ExitType());
	CLOSE_AND_WAIT(badThread);

	test.Next(_L("Rename current thread"));
	test(User::RenameThread(_L("renamed"))==KErrNone);
	name=p.Name();
	name.Append(_L("::"));
	RThread me;
	name.Append(me.Name());
	test(me.Name()==_L("renamed"));
	test(me.FullName().CompareF(name)==0);

	test.Next(_L("Test running exit types"));
	test(thread.ExitType()==EExitPending);
	test(thread.ExitReason()==0);
	// no getters for iUserHeap and iFrame
	test(thread.ExitCategory()==KNullDesC);

	test.Next(_L("Test logging on"));
	thread.Logon(stat);
	RThread t;
	test(t.RequestCount()==0);
	test(stat==KRequestPending);
	r=thread.LogonCancel(stat); // this generates a signal 
	test(r==KErrNone);
	test(stat==KErrNone);
	test(t.RequestCount()==1); // the request count is 1 due to the signal generated by LogonCancel
	test(thread.RequestCount()==0);

	test.Next(_L("Resuming thread"));
	thread.Resume();   
	test.Next(_L("Absorb cancel"));
	User::WaitForRequest(stat);	
	test.Next(_L("Test LogonCancel on dead thread is ok"));
	r=thread.LogonCancel(stat);
	test(r==KErrGeneral);
	test.Next(_L("Close thread"));
	CLOSE_AND_WAIT(thread);
	test.Next(_L("Close again"));
	thread.Close();
	thread.Close();
	thread.Close();
	thread.Close();
	__UHEAP_MARKEND;
	test.End();
	}


LOCAL_D void test2(TOwnerType anOwnerType)
//
// Test 2
//
	{                                  

	__UHEAP_MARK;
	RThread thread;
	TRequestStatus stat;
	TRequestStatus rstat;
	TInt r;

	test.Start(_L("Run thread 10 times"));
	for (TInt xx=0;xx<10;xx++)
		{
		test.Printf(_L("\r%02d"),xx);
		r = StartInstructionThread(thread, _L("Thread1"), ENormal, anOwnerType, &stat, 0);
		test_KErrNone(r);
		thread.Resume();
		User::WaitForRequest(stat);
		CLOSE_AND_WAIT(thread);
		}
	test.Printf(_L("\n"));

	test.Next(_L("Panic within thread"));
	r = StartInstructionThread(thread, _L("Thread2"), EInstrPanic, anOwnerType, &stat, 0);
	test_KErrNone(r);
	test(thread.ExitType()==EExitPending);
	thread.Resume();
	User::WaitForRequest(stat);
	test(thread.ExitCategory()==_L("Hello"));
	test(thread.ExitReason()==KExitPanicNum);
	test(thread.ExitType()==EExitPanic);
	CLOSE_AND_WAIT(thread);

	test.Next(_L("Panic external to thread"));
	TInt ijk;
	TUint seed[2] = { 0xadf85458, 0 };
	TUint maxcount = 0;
	for (ijk=0; ijk<8192; ++ijk)
		{
		if (!(ijk&255))
			test.Printf(_L("%d\n"), ijk);
		r = StartInstructionThread(thread, _L("Thread3"), EWait, anOwnerType, &stat, 0);
		test_KErrNone(r);
		__e32_atomic_store_ord32(&IFLAG, 0);
		thread.Resume();
		thread.SetPriority(EPriorityMore);
		if (maxcount==0)
			{
			while (__e32_atomic_load_acq32(&IFLAG)==0 && --maxcount!=0)
				{
				}
			maxcount = 0u - maxcount;
			test.Printf(_L("maxcount=%u\n"), maxcount);
			}
		else
			{
			TUint random = Random(seed);
			random %= maxcount;
			++random;
			while (__e32_atomic_load_acq32(&IFLAG)==0 && --random!=0)
				{
				}
			}
		thread.Panic(_L("panic"), 123);
		User::WaitForRequest(stat);
		test(thread.ExitCategory()==_L("panic"));
		test(thread.ExitReason()==123);
		test(thread.ExitType()==EExitPanic);
		r = RTest::CloseHandleAndWaitForDestruction(thread);
		test_KErrNone(r);
		}
	
	test.Next(_L("Internal exit"));
	r = StartInstructionThread(thread, _L("Thread4"), ENormal, anOwnerType, &stat, 0);
	test_KErrNone(r);
	test(thread.ExitType()==EExitPending);
	thread.Resume();
	User::WaitForRequest(stat);
	test(thread.ExitCategory()==_L("Kill"));
	test(thread.ExitReason()==KThreadReturnValue);
	test(thread.ExitType()==EExitKill);
	CLOSE_AND_WAIT(thread);

	test.Next(_L("External terminate"));
	r = StartInstructionThread(thread, _L("Thread5"), EWait, anOwnerType, &stat, &rstat);
	test_KErrNone(r);
	test.Next(_L("Terminate"));
	thread.Terminate(KTerminationReason);
	test.Next(_L("Wait"));
	User::WaitForRequest(stat);
	test(thread.ExitCategory()==_L("Terminate"));
	test(thread.ExitReason()==KTerminationReason);
	test(thread.ExitType()==EExitTerminate);
	test.Next(_L("Close"));
	CLOSE_AND_WAIT(thread);
  
	test.Next(_L("External kill"));
	r = StartInstructionThread(thread, _L("Thread6"), EWait, anOwnerType, &stat, &rstat);
	test_KErrNone(r);
	thread.Suspend();
	thread.Resume();
	thread.Kill(KKillReason);
	User::WaitForRequest(stat);
	test(thread.ExitCategory()==_L("Kill"));
	test(thread.ExitReason()==KKillReason);
	test(thread.ExitType()==EExitKill);
	test.Next(_L("Kill again"));
	thread.Kill(KErrNone);
	thread.Kill(KErrNone);
	thread.Kill(KErrNone);
	CLOSE_AND_WAIT(thread);
	test.End();
  	__UHEAP_MARKEND;
	}

LOCAL_D void test3()
//
// Test 3.
//
	{

	test.Start(_L("Read across thread"));
	TReadInfo info;
	TPtrC des1=_L("tdesc");
	info.tdesc=(&des1);
	TPtrC ptr1=_L("tptrc");
	info.tptrc=&ptr1;
	TBuf<0x20> tdes(_L("tdes"));
	info.tdes=&tdes;
	TBuf<0x20> tptrbuf(_L("tptr"));
	TPtr tptr((TText*)tptrbuf.Ptr(),tptrbuf.Length(),tptrbuf.MaxLength());
	info.tptr=&tptr;
	TBuf<0x20> hbufc(_L("hbufc"));
	HBufC *pH=hbufc.Alloc();
	test(pH!=NULL);
	info.hbufc=pH;
	TBufC<0x20> tbufc(_L("tbufc"));
	info.tbufc=&tbufc;
	TBuf<0x20> tbuf(_L("tbuf"));
	info.tbuf=&tbuf;
	TBufC<0x20> tptrdes(_L("tptrdes"));
	TPtr des=tptrdes.Des();
	info.tptrdes=&des;
	TBuf<0x10> b(_L("Hello"));
	info.anAddress=(&b);
	info.anAddress= info.anAddress; //prevents warning (var set but never used)
	delete pH;
	test.End();
	}

LOCAL_D void test4()
//
// Test 4.
//
	{

	test.Start(_L("Create sempahore"));
	RSemaphore sem;
	TInt r=sem.CreateLocal(0);
	test(r==KErrNone);
//
	test.Next(_L("Create thread 1"));
	RThread t;
	r=t.Create(_L("Thread1"),test4Thread,KDefaultStackSize,KHeapSize,KHeapSize,&sem);
	test(r==KErrNone);
	t.Resume();
	t.Close();
//
	test.Next(_L("Create thread 2"));
	r=t.Create(_L("Thread2"),test4Thread,KDefaultStackSize,KHeapSize,KHeapSize,&sem);
	test(r==KErrNone);
	t.Resume();
	t.Close();
//
	test.Next(_L("Create thread 3"));
	r=t.Create(_L("Thread3"),test4Thread,KDefaultStackSize,KHeapSize,KHeapSize,&sem);
	test(r==KErrNone);
	t.Resume();
	t.Close();
//
	test.Next(_L("Release threads"));
	sem.Signal(3);
//
	test.Next(_L("Wait 1"));
	sem.Wait();
//
	test.Next(_L("Wait 2"));
	sem.Wait();
//
	test.Next(_L("Wait 2"));
	sem.Wait();
	sem.Close();
//
	test.End();
	}

TInt MinimalThread(TAny*)
//
// Minimal thread, used in test 5
//
	{
	return(KErrNone);
	}

LOCAL_D void test5()
//
// Test 5 - tests unclosed but completed threads
//
	{

	test.Start(_L("Start thread"));
	RThread thread1;
	test(thread1.Create(_L("Test Thread1"),MinimalThread,KDefaultStackSize,KHeapSize,KHeapSize,NULL)==KErrNone);
	TRequestStatus stat1;
	thread1.Logon(stat1);
	thread1.Resume();
	User::WaitForRequest(stat1);
	test(thread1.ExitType()==EExitKill);
	// 'missing'  thread1.Close();

	test.Next(_L("Start another thread"));
	RThread thread2;
	test(thread2.Create(_L("Test Thread2"),MinimalThread,KDefaultStackSize,KHeapSize,KHeapSize,NULL)==KErrNone);
	TRequestStatus stat2;
	thread2.Logon(stat2);
	thread2.Resume();
	User::WaitForRequest(stat2);  //Goes wrong here in build 48
	test(thread2.ExitType()==EExitKill);
	
	test.Next(_L("Close both threads"));
	CLOSE_AND_WAIT(thread2);
	CLOSE_AND_WAIT(thread1);

	test.End();
	}

LOCAL_D TInt test6Thread(TAny *anArg)
//
//
//
	{
	((RSemaphore*)anArg)->Wait();
	RThread t;
	RThread dup;
	dup.Duplicate(t);
	dup.Panic(_L("Test"),0);

	return KErrNone;
	}

void test6()
//
// Test thread duplication
//
	{

	test.Start(_L("Create thread"));
	RSemaphore sem;
	TInt r=sem.CreateLocal(0);
	test(r==KErrNone);

	RThread t;
	t.Create(_L("test6thread"),test6Thread,KDefaultStackSize,KHeapSize,KHeapSize,&sem);
	test.Next(_L("Resume thread"));
	TRequestStatus stat;
	t.Logon(stat);
	t.Resume();
	sem.Signal();
	User::WaitForRequest(stat);
	test.Next(_L("Close thread"));
	t.Close();
	sem.Close();
	test.End();
	}

RSemaphore gsem;
enum TThreadProgress { EBeforeStart, EStarted, EWaiting, EDoneWaiting, EFinished };
TThreadProgress progress=EBeforeStart;
LOCAL_D TInt test7thread(TAny * /*anArg*/)
//
//
//
	{

	progress=EStarted;
	progress=EWaiting;
	gsem.Wait();
	progress=EDoneWaiting;
	gsem.Wait();
	progress=EFinished;
	return KErrNone;
	}

void test7()
//
//	Suspend/ Resume tests
//
	{

	TInt r=gsem.CreateLocal(0);
	test(r==KErrNone);
	test.Start(_L("Create thread"));
	RThread t;
	r=t.Create(_L("test7thread"), test7thread, KDefaultStackSize,KHeapSize,KHeapSize,NULL);
	test(r==KErrNone);
	TRequestStatus stat;
	t.Logon(stat);
	test.Next(_L("Resume thread"));
	t.Resume();
	User::After(KWaitTime); // wait a bit;
	test.Next(_L("Make thread wait on a semaphore"));
	test(progress==EWaiting);
	test.Next(_L("Suspend waiting thread"));
	t.Suspend();
	test.Next(_L("Signal the semaphore"));
	gsem.Signal();
	User::After(KWaitTime);
	test.Next(_L("Test thread still suspended"));
	test(progress==EWaiting);
	test.Next(_L("resume thread"));
	t.Resume();
	test.Next(_L("Test the thread no longer waiting on the semaphore"));
	User::After(KWaitTime);
	test(progress==EDoneWaiting);
	test.Next(_L("Wait for thread to finish"));
	gsem.Signal();
	User::WaitForRequest(stat);
	test(stat==KErrNone);
	test(progress==EFinished);
	CLOSE_AND_WAIT(t);

	RThread tt;
	progress=EBeforeStart;
	test.Next(_L("Create Thread"));
	r=tt.Create(_L("test7thread"), test7thread, KDefaultStackSize,KHeapSize,KHeapSize,NULL);
	test(r==KErrNone);
	tt.Logon(stat);
	test.Next(_L("Suspend thread without starting it"));
	tt.Suspend();
	tt.Suspend();
	test.Next(_L("Resume and test suspend/resume balance"));
	tt.Resume();
	tt.Resume();
	User::After(KWaitTime);
	test(progress==EBeforeStart);
	tt.Resume();
	test.Next(_L("test thread is suspended on semaphore"));
	User::After(KWaitTime);
	test(progress==EWaiting);
	test.Next(_L("suspend thread"));
	tt.Suspend();
	tt.Suspend();
	test.Next(_L("resume thread"));
	tt.Resume();
	tt.Resume();
	test.Next(_L("test thread still suspended on semaphore"));
	User::After(KWaitTime);
	test(progress==EWaiting);
	test.Next(_L("Suspend, Suspend, Signal semaphore, Suspend"));
	tt.Suspend();
	tt.Suspend();
	gsem.Signal();
	tt.Suspend();
	test.Next(_L("test thread still suspended on semaphore"));
	User::After(KWaitTime);
	test(progress==EWaiting);
	test.Next(_L("Resume thread, checking suspend/resume balance"));
	tt.Resume();
	User::After(KWaitTime);
	test(progress==EWaiting);
	tt.Resume();
	User::After(KWaitTime);
	test(progress==EWaiting);
	tt.Resume();
	User::After(KWaitTime);
	test(progress==EDoneWaiting);
	test.Next(_L("Resume an executing thread"));
	tt.Resume();
	tt.Resume();
//	test.Next(_L("Suspend and check balance"));
//	tt.Suspend();
//	tt.Suspend();
	test.Next(_L("Wait for thread to finish"));
	gsem.Signal();
	User::After(KWaitTime);
	test(progress==EFinished);
	User::WaitForRequest(stat);
	CLOSE_AND_WAIT(tt);

//
	test.Next(_L("Create Thread"));
	r=tt.Create(_L("test7thread"), test7thread, KDefaultStackSize,KHeapSize,KHeapSize,NULL);
	test(r==KErrNone);
	tt.Logon(stat);
	test.Next(_L("Resume"));
	tt.Resume();
	test.Next(_L("Hang thread on semaphore"));
	User::After(KWaitTime);
	test(progress==EWaiting);
	test.Next(_L("Suspend then Resume thread"));
	tt.Suspend();
	tt.Suspend();
	tt.Resume();
	User::After(KWaitTime);
	test(progress==EWaiting);
	tt.Resume();
	test.Next(_L("Check still hanging on semaphore"));
	User::After(KWaitTime);
	test(progress==EWaiting);
	test.Next(_L("Signal Semaphore"));
	gsem.Signal();
	test.Next(_L("Test thread executing again"));
	User::After(KWaitTime);
	test(progress==EDoneWaiting);
	test.Next(_L("Hang thread on another semaphore, and suspend"));
	tt.Suspend();
	test.Next(_L("Signal semaphore, and suspend again"));
	gsem.Signal();
	User::After(KWaitTime);
	test(progress==EDoneWaiting);
	tt.Suspend();
	test.Next(_L("Resume the thread"));
	tt.Resume();
	User::After(KWaitTime);
	test(progress==EDoneWaiting);
	tt.Resume();
	test.Next(_L("Wait for thread to finish"));
	User::After(KWaitTime);
	test(progress==EFinished);
	User::WaitForRequest(stat);
	CLOSE_AND_WAIT(tt);
	test.End();
	}

#if 0
RSemaphore Sem;
LOCAL_D TInt test8thread(TAny* aPtr)
//
//
//
	{

	typedef TBuf<0x20> TTestBuf;
	typedef volatile TTestBuf* TTestBufPtr;
	volatile TTestBufPtr& pB=*(volatile TTestBufPtr*)aPtr;
	if ((TUint)pB != 0xc90fdaa2)
		return KErrGeneral;
	Sem.Wait();
	TDesC* pD=(TDesC*)pB;
	test(*pD==_L("Everything's just hunky-dory"));
	delete (TTestBufPtr*)pB;
	__UHEAP_MARKEND;
	return KErrNone;
	}
#endif

void test8()
//
// Get Heap
//
	{
	// !!! RThread::SetInitialParameter no longer exists

 	/*
	typedef TBuf<0x20> TTestBuf;
	TTestBuf* buf=(TTestBuf*)0xc90fdaa2;

	test.Start(_L("Create thread"));
	RThread thread;
	TInt r=thread.Create(_L("test8thread"),test8thread,KDefaultStackSize,KHeapSize,KHeapSize,NULL);
	test(r==KErrNone);
	r=Sem.CreateLocal(0);
	test(r==KErrNone);
	test.Next(_L("Set parameter"));
	r=thread.SetInitialParameter(&buf);
	test(r==KErrNone);
	TRequestStatus stat;
	thread.Logon(stat);
	thread.SetPriority(EPriorityMore);
	test.Next(_L("Resume thread"));
	thread.Resume();
	test.Next(_L("Set initial parameter NULL"));
	r=thread.SetInitialParameter(NULL);
	test(thread.ExitType()==EExitPending);

	test.Next(_L("Get heap"));
	RHeap* heap;
	heap=thread.Heap();
	test.Next(_L("Alloc inside heap"));
	__RHEAP_MARK(heap);
	buf=(TTestBuf*)heap->Alloc(sizeof(TTestBuf));
	test(buf!=NULL);
	new(buf) TTestBuf;
	*buf=(_L("Everything's just hunky-dory"));

	Sem.Signal();
	User::WaitForRequest(stat);
	test(stat==KErrNone);
	test(thread.ExitType()==EExitKill);
	test(thread.ExitReason()==KErrNone);

	test.Next(_L("Close"));
	thread.Close();
	Sem.Close();
	test.End();
	*/
	}

TInt Thread(TAny* /*aAny*/)
	{

	RTest test(_L("Any old thread"));
	test.Next(_L("Find remote thread"));
	// find the main thread
	TFullName name;
	name=RProcess().Name();
	name.Append(_L("::*"));
	TFindThread ft;
	ft.Find(name);
	TInt r=ft.Next(name);
	test(r==KErrNone);
	RThread t;
	t.Open(ft);

	t.Close();
	return KErrNone;
	}

void test9()
	{

	test.Start(_L("Test create a NULL TPtr"));
	TPtr p(NULL, 10, 10);
	test.Next(_L("Create and run remote thread"));
	RThread t;
	TInt r;
	r=t.Create(_L("Any Old Thread"), Thread, 0x2000, 0x2000, 0x2000, (TAny *)&p);
	test(KErrNone==r);
	TRequestStatus stat;
	t.Logon(stat);
	t.Resume();
	test.Next(_L("Wait for thread to complete"));
	User::WaitForRequest(stat);
	test(stat==KErrNone);
	test(t.ExitCategory()==_L("Kill"));
	test(t.ExitReason()==KErrNone);
	test(t.ExitType()==EExitKill);
	CLOSE_AND_WAIT(t);
	test.End();
    }



TInt FoghornLeghorn(TAny* aMutex)
//
// Thread function
//
	{

	((RSemaphore*)aMutex)->Wait();
	RThread thread;
	TInt r=thread.Create(_L("I say * boy"),FoghornLeghorn,KDefaultStackSize,NULL,aMutex);
	test(r==KErrBadName);
	return KErrNone;
	}

void testOpen()
	{
	
	test.Start(_L("Create Foghorn Leghorn"));
	RSemaphore fogMut;
	fogMut.CreateLocal(0);
	RThread foghorn;
	TInt r=foghorn.Create(_L("Foghorn Leghorn"),FoghornLeghorn,KDefaultStackSize,KHeapSize,KHeapSize,&fogMut);
	test(r==KErrNone);
	test.Next(_L("Logon"));
	TRequestStatus stat;
	foghorn.Logon(stat);
	test(stat==KRequestPending);
	test.Next(_L("Resume Foghorn Leghorn"));
	foghorn.Resume();
	test.Next(_L("Get full name"));
	TFindThread find(_L("*Foghorn Leghorn"));
	TFullName name;
	r=find.Next(name);
	test(r==KErrNone);
	test.Next(_L("Open another handle using full name"));
	RThread leghorn;
	r=leghorn.Open(name);
	test(r==KErrNone);
	test.Next(_L("Kill using second handle"));
	leghorn.Kill(34523);
	User::WaitForRequest(stat);
	test(stat==34523);
	test.Next(_L("Close handles"));
	foghorn.Close();
	CLOSE_AND_WAIT(leghorn);

	test.Next(_L("Again! - Create Foghorn Leghorn"));
	r=foghorn.Create(_L("Foghorn Leghorn"),FoghornLeghorn,KDefaultStackSize,KHeapSize,KHeapSize,&fogMut);
	test(r==KErrNone);
	test.Next(_L("Logon"));
	foghorn.Logon(stat);
	test(stat==KRequestPending);
	test.Next(_L("Resume Foghorn Leghorn"));
	foghorn.Resume();
	test.Next(_L("Get full name"));
	find.Find(_L("*Foghorn Leghorn"));
	r=find.Next(name);
	test(r==KErrNone);
	test.Next(_L("Open another handle using full name"));
	r=leghorn.Open(name);
	test(r==KErrNone);
	test.Next(_L("Kill using second handle"));
	leghorn.Kill(67857);
	User::WaitForRequest(stat);
	test(stat==67857);
	test.Next(_L("Close handles"));
	foghorn.Close();
	CLOSE_AND_WAIT(leghorn);

	test.Next(_L("Create Foghorn Leghorn"));
	r=foghorn.Create(_L("Foghorn Leghorn"),FoghornLeghorn,KDefaultStackSize,KHeapSize,KHeapSize,&fogMut);
	test(r==KErrNone);
	test.Next(_L("Logon"));
	foghorn.Logon(stat);
	test(stat==KRequestPending);
	test.Next(_L("Resume Foghorn Leghorn"));
	foghorn.Resume();
	test.Next(_L("Now close it"));
	foghorn.Close();

	test.Next(_L("Get full name"));
	find.Find(_L("*Foghorn Leghorn"));
	r=find.Next(name);
	test(r==KErrNone);
	test.Next(_L("Open using full name"));
	r=leghorn.Open(name);
	test(r==KErrNone);
	test.Next(_L("Kill"));
	leghorn.Kill(67857);
	User::WaitForRequest(stat);
	test(stat==67857);
	test.Next(_L("Close"));
	CLOSE_AND_WAIT(leghorn);

	test.Next(_L("Start and get it to try to start a new thread"));
	r=foghorn.Create(_L("Foghorn Leghorn"),FoghornLeghorn,KDefaultStackSize,KHeapSize,KHeapSize,&fogMut);
	test(r==KErrNone);
	foghorn.Logon(stat);
	test(stat==KRequestPending);
	foghorn.Resume();
	fogMut.Signal();
	User::WaitForRequest(stat);
	test(stat==KErrNone);
	test(foghorn.ExitCategory()==_L("Kill"));
	test(foghorn.ExitReason()==KErrNone);
	test(foghorn.ExitType()==EExitKill);
	test.Next(_L("Close"));
	CLOSE_AND_WAIT(foghorn);
	fogMut.Close();

	test.End();
	}

TInt Bunny(TAny*)
//
// Thread function
//
	{

	FOREVER
		;
	}

void testReuse()
	{
	
	test.Start(_L("Create thread with duplicate name"));
	RThread thread;
	TFullName name=thread.Name();
	TInt r=thread.Create(name,Bunny,KDefaultStackSize,KHeapSize,KHeapSize,NULL);
	test(r==KErrAlreadyExists);
//	thread.Resume(); handle will be invalid since create failed
	test.Next(_L("Create with a good name"));
	r=thread.Create(_L("Bugs Bunny"),Bunny,KDefaultStackSize,KHeapSize,KHeapSize,NULL);
	test(r==KErrNone);
	TRequestStatus stat;
	thread.Logon(stat);
	test.Next(_L("Resume"));
	thread.Resume();
	test.Next(_L("Kill"));
	thread.Kill(15);
	User::WaitForRequest(stat);
	test(stat==15);
	CLOSE_AND_WAIT(thread);

	test.End();
	}
	

TInt HongKongPhooey(TAny * /*aAny*/)
	{

	RMutex m;
	m.OpenGlobal(_L("Test Mutex"));
	m.Wait();
	User::Panic(_L("Hello"),900);
	return KErrNone;
	}

void testReleaseMutex()
//
// Bug HA-187
//
	{
	TInt r;
	test.Start(_L("Create a global Mutex"));
	RMutex m;
	r=m.CreateGlobal(_L("Test Mutex"));
	test.Next(_L("Create a thread"));
	RThread number1SuperGuy;
	r=number1SuperGuy.Create(_L("Hong Kong Phooey"), HongKongPhooey, KDefaultStackSize,KHeapSize,KHeapSize,NULL);
	test(r==KErrNone);
	TRequestStatus s;
	number1SuperGuy.Logon(s);
	test.Next(_L("Resume Thread"));
	number1SuperGuy.Resume();
	test.Next(_L("Wait on Mutex and Panic"));
	User::WaitForRequest(s);
	test(number1SuperGuy.ExitType()==EExitPanic);
	test(number1SuperGuy.ExitCategory()==_L("Hello"));
	test(number1SuperGuy.ExitReason()==900);
	User::After(100000);	// wait a bit for everything to be cleaned up
	m.Wait();
	test.Next(_L("Close everything"));
	m.Close();
	CLOSE_AND_WAIT(number1SuperGuy);
	test.End();
	}

void testId()
	{

	test.Start(_L("Try to open nonexistant thread by ID"));
	RThread thread;
	TInt r=thread.Open(*(TThreadId*)&KMaxTUint);
	test(r==KErrNotFound);
	test.Next(_L("Get thread ID"));
	r=thread.Create(_L("Buster Bunny"),Bunny,KDefaultStackSize,KHeapSize,KHeapSize,NULL);
	test(r==KErrNone);
	TThreadId id=thread.Id();
	TThreadId id2=thread.Id();
	test(id==id2);
	RThread thread2;
	r=thread2.Create(_L("Babs Bunny"),Bunny,KDefaultStackSize,KHeapSize,KHeapSize,NULL);
	test(r==KErrNone);
	id2=thread2.Id();
	test(id!=id2);
	test(*(TUint*)&id+1==*(TUint*)&id2);
	test.Next(_L("Open by ID"));
	TRequestStatus stat;
	thread.Logon(stat);
	thread.Kill(54624);
	User::WaitForRequest(stat);
	test(stat==54624);
	thread.Close();
	r=thread.Open(id2);
	test(r==KErrNone);
	test(thread.Name()==_L("Babs Bunny"));
	test(thread.FullName()==thread2.FullName());
	thread2.Close();
	id=thread.Id();
	test(id==id2);
	thread.Logon(stat);
	thread.Kill(88863);
	User::WaitForRequest(stat);
	test(stat==88863);
	CLOSE_AND_WAIT(thread);
	
	test.End();
	}

struct SCreateInfo
	{
	TInt iStackSize;
	TInt iMinHeapSize;
	TInt iMaxHeapSize;
	};

TInt BadCreation(TAny* aCreateInfo)
	{
	SCreateInfo& info=*((SCreateInfo*)aCreateInfo);
	RThread thread;
	thread.Create(_L("Won't work"),Bunny,info.iStackSize,info.iMinHeapSize,info.iMaxHeapSize,NULL);
	return KErrNone;
	}

void testCreate()
	{
	test.Start(_L("Negative stack size"));
	RThread thread;
	TRequestStatus stat;
	TInt r;
	{
	SCreateInfo info={-1,0x1000,0x1000};
	r=thread.Create(_L("Test Create"),BadCreation,KDefaultStackSize,KHeapSize,KHeapSize,&info);
	test(KErrNone==r);
	thread.Logon(stat);
	thread.Resume();
	User::WaitForRequest(stat);
	test(stat==EThrdStackSizeNegative);
	test(thread.ExitType()==EExitPanic);
	test(thread.ExitReason()==EThrdStackSizeNegative);
	test(thread.ExitCategory()==_L("USER"));
	CLOSE_AND_WAIT(thread);
	}
//
	test.Next(_L("Negative heap min size"));
	{
	SCreateInfo info={0x1000,-1,0x1000};
	r=thread.Create(_L("Test Create"),BadCreation,KDefaultStackSize,KHeapSize,KHeapSize,&info);
	test(KErrNone==r);
	thread.Logon(stat);
	thread.Resume();
	User::WaitForRequest(stat);
	test(stat==EThrdHeapMinTooSmall);
	test(thread.ExitType()==EExitPanic);
	test(thread.ExitReason()==EThrdHeapMinTooSmall);
	test(thread.ExitCategory()==_L("USER"));
	CLOSE_AND_WAIT(thread);
	}
	test.Next(_L("Negative heap max size"));
	{
	SCreateInfo info={0x1000,0x1000,-1};
	r=thread.Create(_L("Test Create"),BadCreation,KDefaultStackSize,KHeapSize,KHeapSize,&info);
	test(KErrNone==r);
	thread.Logon(stat);
	thread.Resume();
	User::WaitForRequest(stat);
	test(stat==EThrdHeapMaxLessThanMin);
	test(thread.ExitType()==EExitPanic);
	test(thread.ExitReason()==EThrdHeapMaxLessThanMin);
	test(thread.ExitCategory()==_L("USER"));
	CLOSE_AND_WAIT(thread);
	}
	test.Next(_L("heap max size < heap min size"));
	{
	SCreateInfo info={0x1000,0x2001,0x1000};
	r=thread.Create(_L("Test Create"),BadCreation,KDefaultStackSize,KHeapSize,KHeapSize,&info);
	test(KErrNone==r);
	thread.Logon(stat);
	thread.Resume();
	User::WaitForRequest(stat);
	test(stat==EThrdHeapMaxLessThanMin);
	test(thread.ExitType()==EExitPanic);
	test(thread.ExitReason()==EThrdHeapMaxLessThanMin);
	test(thread.ExitCategory()==_L("USER"));
	CLOSE_AND_WAIT(thread);
	}
	test.Next(_L("Little min heap size"));
	{
	SCreateInfo info={0x1000,KMinHeapSize-1,0x1000};
	r=thread.Create(_L("Test Create"),BadCreation,KDefaultStackSize,KHeapSize,KHeapSize,&info);
	test(KErrNone==r);
	thread.Logon(stat);
	thread.Resume();
	User::WaitForRequest(stat);
	test(stat==EThrdHeapMinTooSmall);
	test(thread.ExitType()==EExitPanic);
	test(thread.ExitReason()==EThrdHeapMinTooSmall);
	test(thread.ExitCategory()==_L("USER"));
	CLOSE_AND_WAIT(thread);
	}
	test.End();
	}

TInt StackInfoThread(TAny*)
	{
	TInt a;
	RThread::Rendezvous((TInt)&a);  // Complete rendezvous using address of 'a' which is on the stack
	return 0;
	}

void testThreadStackInfo()
	{
	// Check the info about the current thread's stack
	RThread thread;
	TThreadStackInfo info;
	TInt r = thread.StackInfo(info);
	test(r==KErrNone);
	TLinAddr a = (TLinAddr)&info;
	test.Printf(_L("address on stack=%x iBase=%x iLimit=%x iExpandLimit=%x"),a,info.iBase,info.iLimit,info.iExpandLimit);
	test(a<=info.iBase);
	test(a>=info.iLimit);
	test(info.iExpandLimit<=info.iLimit);

	// Create another thread
	r=thread.Create(_L("StackInfoThread"),StackInfoThread,KDefaultStackSize,KHeapSize,KHeapSize,(TAny*)NULL);
	test(r==KErrNone);
	thread.SetPriority(EPriorityLess);

	// Resume thread and wait for it to run
	TRequestStatus stat;
	thread.Rendezvous(stat);
	thread.Resume();
	User::WaitForRequest(stat);

	// Test getting stack info of another thread
	r = thread.StackInfo(info);
	test(r==KErrNone);
	a = stat.Int(); // a = an address on the threads stack
	test.Printf(_L("address on stack=%x iBase=%x iLimit=%x iExpandLimit=%x"),a,info.iBase,info.iLimit,info.iExpandLimit);
	test(a<=info.iBase);
	test(a>=info.iLimit);
	test(info.iExpandLimit<=info.iLimit);

	// Let thread run to end
	thread.Logon(stat);
	User::WaitForRequest(stat);
	test(stat.Int()==0);
	}

GLDEF_C TInt E32Main()
//
// Main
//
	{	
 
	// don't want just in time debugging as we trap panics
	TBool justInTime=User::JustInTime(); 
	User::SetJustInTime(EFalse); 

	test.Title();
	__UHEAP_MARK;
	

	TFullName name;
	name=RThread().Name();

	test.Start(_L("Test threads"));

	test.Next(_L("Test 1"));
	test1();
 
	test.Next(_L("Test create"));
	testCreate();

	test.Next(_L("Test RUndertaker"));
	testUndertaker(EOwnerProcess);

	test.Next(_L("Test2"));
	test2(EOwnerProcess);
	User::SetJustInTime(justInTime);	
	test.Next(_L("Test3"));
	test3();
	test.Next(_L("Test4"));
	test4();
	test.Next(_L("Completed but unclosed thread"));
	User::SetJustInTime(EFalse);
	test5();
	User::SetJustInTime(justInTime);
	test.Next(_L("Suspend/Resume"));
	test7();
	test.Next(_L("Testing thread duplication"));
	User::SetJustInTime(EFalse);
	test6();
	User::SetJustInTime(justInTime);
	test.Next(_L("Get thread's heap"));
	test8();
	test.Next(_L("Test read NULL remotely (HA-178)"));
	test9();
	test.Next(_L("Test Open(aFullName)"));
	testOpen();
	test.Next(_L("Test Reuse after a failed create"));
	testReuse();
	test.Next(_L("Test thread releases Mutex (HA-178)"));
	User::SetJustInTime(EFalse);
	testReleaseMutex();
	User::SetJustInTime(justInTime);
	test.Next(_L("Test Thread ID"));
	testId();
	test.Next(_L("Test RThread::StackInfo"));
	testThreadStackInfo();
	test.End();
	__UHEAP_MARKEND;
	return(KErrNone);
	}