kerneltest/e32test/dll/t_tls.cpp
changeset 0 a41df078684a
child 293 0659d0e1a03c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/dll/t_tls.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,738 @@
+// Copyright (c) 1996-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\dll\t_tls.cpp
+// Overview:
+// Test and benchmark TLS (Thread Local Storage) functions
+// API Information:
+// UserSvr
+// Details:
+// - Use the UserSvr methods: DllTls, DllSetTls and DllFreeTls to test 
+// the thread local storage. Verify the expected results. Verify that 
+// the heap was not corrupted by any of the tests.
+// - In a separate thread, use the UserSvr methods: DllTls, DllSetTls and 
+// DllFreeTls to test the thread local storage. Verify the expected results.
+// - Test the TLS cleanup handlers and verify results are as expected. Verify 
+// that the heap was not corrupted by any of the tests.
+// - Benchmark how many DllSetTls, DllTls and DllFreeTls operations can be
+// performed in 1/2 second.
+// Platforms/Drives/Compatibility:
+// All.
+// Assumptions/Requirement/Pre-requisites:
+// Failures and causes:
+// Base Port information:
+// Tests and benchmarks Tls functions
+// 14/10/96 Modified scheme on Arm release
+// SetTls - 352	~1300
+// Tls - 3510 ~1600
+// 15/10/96 Array scheme on Arm release
+// Set Tls - ~1900
+// Tls - ~2550
+// 
+//
+
+#define __E32TEST_EXTENSION__
+
+#include <e32test.h>
+#include <e32svr.h>
+#include <f32dbg.h>
+#include <e32def.h>
+#include <e32def_private.h>
+#include "u32std.h"
+
+// Can't tell whether allocations will be on the kernel or user heap, so perform all heap check
+// operations on both
+#define HEAP_MARK			__UHEAP_MARK; __KHEAP_MARK
+#define HEAP_MARKEND		__UHEAP_MARKEND; __KHEAP_MARKEND
+#define HEAP_RESET			__UHEAP_RESET; __KHEAP_RESET
+#define HEAP_CHECK(x)		__UHEAP_CHECK(x); __KHEAP_CHECK(x)
+#define HEAP_FAILNEXT(x)	__UHEAP_FAILNEXT(x); __KHEAP_FAILNEXT(x)
+
+TInt const KCheckHandle=67338721;
+TUint8* const KCheckValue=(TUint8*)8525124;
+
+LOCAL_D RTest test(_L("T_TLS"));
+
+_LIT(KTestDllName, "t_tlsdll");
+const TInt KTestDllOrdSet	= 1;
+const TInt KTestDllOrdGet	= 2;
+const TInt KTestDllOrdFree	= 3;
+
+typedef TInt (*TTestDllSetFn)(TAny*);
+typedef TAny* (*TTestDllGetFn)();
+typedef void (*TTestDllFreeFn)();
+
+TInt TestDllSetTls(RLibrary aLib, TAny* aValue)
+	{
+	TTestDllSetFn f = (TTestDllSetFn)aLib.Lookup(KTestDllOrdSet);
+	return (*f)(aValue);
+	}
+
+TAny* TestDllGetTls(RLibrary aLib)
+	{
+	TTestDllGetFn f = (TTestDllGetFn)aLib.Lookup(KTestDllOrdGet);
+	return (*f)();
+	}
+
+void TestDllFreeTls(RLibrary aLib)
+	{
+	TTestDllFreeFn f = (TTestDllFreeFn)aLib.Lookup(KTestDllOrdFree);
+	(*f)();
+	}
+
+#define DISPLAY_PROGRESS	test.Printf(_L("Line %d\n"), __LINE__)
+
+void Test()
+	{
+	test.Start(_L("Stuff without Setting"));
+
+	HEAP_MARK;
+
+	test(UserSvr::DllTls(KCheckHandle)==NULL);
+	UserSvr::DllFreeTls(KCheckHandle);
+	test(UserSvr::DllTls(KCheckHandle)==NULL);
+	test(UserSvr::DllTls(0)==NULL);
+	UserSvr::DllFreeTls(0);
+	test(UserSvr::DllTls(0)==NULL);
+
+	HEAP_CHECK(0);
+
+	test.Next(_L("Set"));
+	test(UserSvr::DllSetTls(KCheckHandle,KCheckValue)==KErrNone);
+	test.Next(_L("Get"));
+	test(UserSvr::DllTls(KCheckHandle)==KCheckValue);
+	test.Next(_L("Free"));
+	UserSvr::DllFreeTls(KCheckHandle);
+	test(UserSvr::DllTls(KCheckHandle)==NULL);
+
+	HEAP_CHECK(0);
+
+	test.Next(_L("Set lots"));
+	TInt i=0;
+	for(;i<1000;i+=3)
+		{
+		test(UserSvr::DllSetTls(KCheckHandle+i,KCheckValue-i)==KErrNone);
+		test(UserSvr::DllTls(KCheckHandle+i)==KCheckValue-i);
+		}
+	for(i=999;i>0;i-=3)
+		{
+		test(UserSvr::DllTls(KCheckHandle+i)==KCheckValue-i);
+		UserSvr::DllFreeTls(KCheckHandle+i);
+		test(UserSvr::DllTls(KCheckHandle+i)==NULL);
+		}
+	test(UserSvr::DllTls(KCheckHandle+i)==KCheckValue-i);
+	UserSvr::DllFreeTls(KCheckHandle+i);
+	test(UserSvr::DllTls(KCheckHandle+i)==NULL);
+
+	HEAP_MARKEND;
+
+
+	test.Next(_L("Test with DLL ID"));
+
+	HEAP_MARK;
+
+	test(UserSvr::DllTls(KCheckHandle, 1)==0);
+	test(UserSvr::DllTls(KCheckHandle, 2)==0);
+	DISPLAY_PROGRESS;
+#ifdef _DEBUG
+	HEAP_FAILNEXT(1);
+	DISPLAY_PROGRESS;
+	test(UserSvr::DllSetTls(KCheckHandle, 1, KCheckValue)==KErrNoMemory);
+#endif
+	test(UserSvr::DllSetTls(KCheckHandle, 1, KCheckValue)==KErrNone);
+	test(UserSvr::DllTls(KCheckHandle, 1)==KCheckValue);
+	test(UserSvr::DllTls(KCheckHandle, 2)==0);
+	DISPLAY_PROGRESS;
+	HEAP_FAILNEXT(1);
+	DISPLAY_PROGRESS;
+	test(UserSvr::DllSetTls(KCheckHandle, 3, KCheckValue)==KErrNone);
+
+#ifdef _DEBUG
+	RSemaphore s;
+	test(s.CreateLocal(0)==KErrNoMemory);
+#endif
+
+	HEAP_RESET;
+
+	test(UserSvr::DllTls(KCheckHandle, 1)==0);
+	test(UserSvr::DllTls(KCheckHandle, 2)==0);
+	test(UserSvr::DllTls(KCheckHandle, 3)==KCheckValue);
+	UserSvr::DllFreeTls(KCheckHandle);
+
+	DISPLAY_PROGRESS;
+	HEAP_MARKEND;
+	DISPLAY_PROGRESS;
+
+	test.Next(_L("Test reloading DLL"));
+
+	RLibrary l;
+	TInt r = l.Load(KTestDllName);
+	test(r==KErrNone);
+	for (i=0; i<2; ++i)
+		{
+		test.Printf(_L("i=%d\n"),i);
+		test(TestDllGetTls(l)==0);
+		test(TestDllSetTls(l, KCheckValue)==KErrNone);
+		test(TestDllGetTls(l)==KCheckValue);
+		if (i==0)
+			{
+			TestDllFreeTls(l);
+			test(TestDllGetTls(l)==0);
+			}
+		l.Close();
+		r = l.Load(KTestDllName);
+		test(r==KErrNone);
+		test(TestDllGetTls(l)==0);
+		TestDllFreeTls(l);
+		}
+	l.Close();
+
+	test.End();
+	}
+
+TInt TestThread(TAny*)
+	{
+	RTest test(_L("T_TLS Thread"));
+	test.Start(_L("Stuff without Setting"));
+	test(UserSvr::DllTls(KCheckHandle)==NULL);
+	UserSvr::DllFreeTls(KCheckHandle);
+	test(UserSvr::DllTls(KCheckHandle)==NULL);
+	test(UserSvr::DllTls(0)==NULL);
+	UserSvr::DllFreeTls(0);
+	test(UserSvr::DllTls(0)==NULL);
+	test.Next(_L("Set"));
+	test(UserSvr::DllSetTls(KCheckHandle,KCheckValue)==KErrNone);
+	test.Next(_L("Get"));
+	test(UserSvr::DllTls(KCheckHandle)==KCheckValue);
+	test.Next(_L("Free"));
+	UserSvr::DllFreeTls(KCheckHandle);
+	test(UserSvr::DllTls(KCheckHandle)==NULL);
+	test.Next(_L("Set lots"));
+	TInt i=0;
+	for(;i<1000;i+=3)
+		{
+		test(UserSvr::DllSetTls(KCheckHandle+i,KCheckValue-i)==KErrNone);
+		test(UserSvr::DllTls(KCheckHandle+i)==KCheckValue-i);
+		}
+	for(i=999;i>=0;i-=3)
+		{
+		test(UserSvr::DllTls(KCheckHandle+i)==KCheckValue-i);
+		UserSvr::DllFreeTls(KCheckHandle+i);
+		test(UserSvr::DllTls(KCheckHandle+i)==NULL);
+		}
+
+	test.Close();
+	return KErrNone;
+	}
+
+TInt HeapSize(RHeap* aHeap = NULL)
+	{
+	if (!aHeap)
+		aHeap = &User::Heap();
+	TInt size;
+	aHeap->AllocSize(size);
+	return size;
+	}
+
+TBool TLSStoredOnUserHeap()
+	{
+	TInt initCount = HeapSize();
+	TInt i;
+	const TInt KMax = 2;
+	for(i = 0 ; i < KMax ; ++i)
+		test(UserSvr::DllSetTls(KCheckHandle+i,KCheckValue-i) == KErrNone);
+	TInt finalCount = HeapSize();
+	for(i = 0 ; i < KMax ; ++i)
+		UserSvr::DllFreeTls(KCheckHandle+i);
+	return initCount != finalCount;
+	}
+
+class RDummyAllocator : public RAllocator
+	{
+public:
+	TInt AccessCount() { return iAccessCount; }
+	};
+
+RHeap* MainHeap = NULL;
+
+TInt HeapAccessCount(TAny* aHeap)
+	{
+	RDummyAllocator* heap = (RDummyAllocator*)aHeap;
+	return heap->AccessCount();
+	}
+
+TBool HeapExists(RHeap* aHeap)
+	{
+	RThread thread;
+	test_KErrNone(thread.Create(_L("HeapExistsThread"), HeapAccessCount, 0x1000, MainHeap, (TAny*)aHeap));
+	TRequestStatus status;
+	thread.Logon(status);
+	thread.Resume();
+	User::WaitForRequest(status);
+
+	TInt r;
+	if (thread.ExitType() == EExitKill)
+		{
+		r = thread.ExitReason();
+		test_NotNegative(r);
+		}
+	else
+		{
+		test_Equal(EExitPanic, thread.ExitType());
+		test_Equal(3, thread.ExitReason());
+		r = 0;
+		}
+	thread.Close();
+	
+	return r != 0;
+	}
+
+TInt TestUserSideTls1Thread(TAny*)
+	{
+	// Ensure TLS uses initial heap
+	if (UserSvr::DllSetTls(KCheckHandle,KCheckValue) != KErrNone)
+		return __LINE__;
+	UserSvr::DllFreeTls(KCheckHandle);
+
+	RHeap* tlsHeap = &User::Heap();
+	TInt tlsInitSize = HeapSize(tlsHeap);
+
+	// Switch heap
+	RHeap* newHeap = User::ChunkHeap(NULL, 0x1000, 0x1000);
+	if (!newHeap)
+		return __LINE__;
+	TInt newInitSize = HeapSize(newHeap);
+	User::SwitchHeap(newHeap);
+	tlsHeap->Close();
+
+	// Allocate more TLS data
+	for(TInt i = 0 ; i < 100 ; ++i)
+		{
+		if (UserSvr::DllSetTls(KCheckHandle+i,KCheckValue-i) != KErrNone)
+			return __LINE__;
+		}
+
+	// Test TLS data was allocated on original heap
+	if (!(HeapSize(tlsHeap) > tlsInitSize))
+		return __LINE__;
+	if (HeapSize(newHeap) != newInitSize)
+		return __LINE__;
+	
+	return KErrNone;
+	}
+
+void TestUserSideTls1()
+	{
+	test.Next(_L("Test user-side TLS behaviour when switching heaps"));
+
+	RThread thread;
+	test_KErrNone(thread.Create(_L("TestUserSideTls1Thread"), TestUserSideTls1Thread, 0x1000, 0x1000, 0x1000, 0));
+	
+	TRequestStatus status;
+	thread.Logon(status);
+	thread.Resume();
+	User::WaitForRequest(status);
+	
+	test_Equal(EExitKill, thread.ExitType());
+	test_Equal(KErrNone, thread.ExitReason());
+	thread.Close();
+	}
+
+TInt TestUserSideTls2Thread(TAny*)
+	{
+	// Allocate some TLS data
+	for(TInt i = 0 ; i < 100 ; ++i)
+		{
+		if (UserSvr::DllSetTls(KCheckHandle+i,KCheckValue-i) != KErrNone)
+			return __LINE__;
+		}
+
+	// Exit normally
+	return KErrNone;
+	}
+
+void TestUserSideTls2()
+	{
+	test.Next(_L("Test user-side TLS data cleanup on thread exit"));
+
+	RHeap* tlsHeap = User::ChunkHeap(NULL, 0x1000, 0x1000);
+	test_NotNull(tlsHeap);
+	TInt initSize = HeapSize(tlsHeap);
+
+	RThread thread;
+	test_KErrNone(thread.Create(_L("TestUserSideTls2Thread"), TestUserSideTls2Thread, 0x1000, tlsHeap, 0));
+	TThreadId id = thread.Id();
+	
+	TRequestStatus status;
+	thread.Logon(status);
+	thread.Resume();
+	User::WaitForRequest(status);
+	
+	test_Equal(EExitKill, thread.ExitType());
+	test_Equal(KErrNone, thread.ExitReason());
+	thread.Close();
+
+	// Check TLS data freed
+	test_Equal(initSize, HeapSize(tlsHeap));	
+	tlsHeap->Close();
+
+	// Check heap no longer exists
+	test(!HeapExists(tlsHeap));
+
+	// Check thread no longer exists
+	RThread thread2;
+	test_Equal(KErrNotFound, thread2.Open(id));
+	}
+
+TInt TestUserSideTls3Thread(TAny*)
+	{
+	// Allocate some TLS data
+	for(TInt i = 0 ; i < 100 ; ++i)
+		{
+		if (UserSvr::DllSetTls(KCheckHandle+i,KCheckValue-i) != KErrNone)
+			return __LINE__;
+		}
+
+	// Panic
+	User::Panic(_L("Test"), 999);
+	return KErrNone;
+	}
+
+void TestUserSideTls3()
+	{
+	test.Next(_L("Test user-side TLS data cleanup on thread panic"));
+
+	RHeap* tlsHeap = User::ChunkHeap(NULL, 0x1000, 0x1000);
+	test_NotNull(tlsHeap);
+
+	RThread thread;
+	test_KErrNone(thread.Create(_L("TestUserSideTls3Thread"), TestUserSideTls3Thread, 0x1000, tlsHeap, 0));
+	TThreadId id = thread.Id();
+	
+	TRequestStatus status;
+	thread.Logon(status);
+	thread.Resume();
+	User::WaitForRequest(status);
+	
+	test_Equal(EExitPanic, thread.ExitType());
+	test_Equal(999, thread.ExitReason());
+	
+	thread.Close();
+	tlsHeap->Close();
+	
+	// Check heap no longer exists
+	test(!HeapExists(tlsHeap));
+
+	// Check thread no longer exists
+	RThread thread2;
+	test_Equal(KErrNotFound, thread2.Open(id));
+	}
+
+TInt TestUserSideTls4Thread(TAny*)
+	{
+	// Shouldn't ever run
+	return KErrGeneral;
+	}
+
+void TestUserSideTls4()
+	{
+	test.Next(_L("Test user-side TLS data cleanup on early thread kill"));
+	
+	RHeap* tlsHeap = User::ChunkHeap(NULL, 0x1000, 0x1000);
+	test_NotNull(tlsHeap);
+
+	RThread thread;
+	test_KErrNone(thread.Create(_L("TestUserSideTls4Thread"), TestUserSideTls4Thread, 0x1000, tlsHeap, 0));
+	TThreadId id = thread.Id();
+	
+	thread.Kill(101);
+
+	TRequestStatus status;
+	thread.Logon(status);
+	User::WaitForRequest(status);
+	
+	test_Equal(EExitKill, thread.ExitType());
+	test_Equal(101, thread.ExitReason());
+	
+	thread.Close();
+	tlsHeap->Close();
+	
+	// Check heap no longer exists
+	test(!HeapExists(tlsHeap));
+
+	// Check thread no longer exists
+	RThread thread2;
+	test_Equal(KErrNotFound, thread2.Open(id));
+	}
+
+#if 0
+class CTest : public CBase
+	{
+public:
+	static CTest* New(TInt aSize, TInt aTls, TInt aLinkedTls);
+	virtual ~CTest();
+public:
+	TAny* iAlloc;
+	TInt iTls;
+	TInt iLinkedTls;
+	};
+
+void TlsCleanup2(TAny* a)
+	{
+	delete ((CTest*)a);
+	}
+
+CTest::~CTest()
+	{
+	RDebug::Print(_L("~CTest %d %d"), iTls, iLinkedTls);
+	User::Free(iAlloc);
+	UserSvr::DllFreeTls(iTls);
+	if (iLinkedTls)
+		{
+		CTest* p = (CTest*)UserSvr::DllTls(iLinkedTls);
+		if (p)
+			delete p;
+		}
+	}
+
+CTest* CTest::New(TInt aSize, TInt aTls, TInt aLinkedTls)
+	{
+	CTest* p = new CTest;
+	test(p!=NULL);
+	p->iTls = aTls;
+	p->iAlloc = User::Alloc(aSize);
+	test(p->iAlloc!=NULL);
+	p->iLinkedTls = aLinkedTls;
+	test(UserSvr::DllSetTls(aTls, p, TlsCleanup2)==KErrNone);
+	return p;
+	}
+
+
+void TlsCleanupCheck(TAny*)
+	{
+	__UHEAP_MARKEND;
+	}
+
+void TlsCleanup1(TAny* a)
+	{
+	User::Free(a);
+	}
+
+TInt TlsCleanupThread1(TAny* a)
+	{
+	__UHEAP_MARK;
+	TInt n = (TInt)a;
+	TInt i;
+	TInt r = UserSvr::DllSetTls(0, NULL, TlsCleanupCheck);
+	if (r!=KErrNone)
+		return r;
+	for (i=0; i<n; ++i)
+		{
+		TAny* p = User::Alloc(64);
+		if (!p)
+			return KErrNoMemory;
+		r = UserSvr::DllSetTls(i+1, p, TlsCleanup1);
+		if (r!=KErrNone)
+			return r;
+		}
+	return KErrNone;
+	}
+
+TInt TlsCleanupThread2(TAny*)
+	{
+	__UHEAP_MARK;
+	TInt r = UserSvr::DllSetTls(0, NULL, TlsCleanupCheck);
+	if (r!=KErrNone)
+		return r;
+
+	CTest::New(256, 1, 2);
+	CTest::New(256, 2, 3);
+	CTest::New(256, 3, 0);
+	CTest::New(256, 6, 5);
+	CTest::New(256, 5, 4);
+	CTest::New(256, 4, 0);
+	CTest::New(256, 9, 8);
+	CTest::New(256, 8, 7);
+	CTest::New(256, 7, 9);
+	CTest::New(256, 10, 11);
+	CTest* p = CTest::New(256, 11, 12);
+	CTest::New(256, 12, 0);
+
+	delete p;
+
+	return KErrNone;
+	}
+
+void DisplayExitInfo(RThread t)
+	{
+	TInt exitType = t.ExitType();
+	TInt exitReason = t.ExitReason();
+	TBuf<32> exitCat = t.ExitCategory();
+	test.Printf(_L("Exit Info: %d,%d,%S\n"), exitType, exitReason, &exitCat);
+	}
+
+void DoTestCleanupHandler(TThreadFunction aFunc, TAny* aArg)
+	{
+	RThread t;
+	TInt r = t.Create(KNullDesC, aFunc, 0x1000, NULL, aArg);
+	test(r==KErrNone);
+	TRequestStatus s;
+	t.Logon(s);
+	t.Resume();
+	User::WaitForRequest(s);
+	DisplayExitInfo(t);
+	test(t.ExitType()==EExitKill);
+	test(t.ExitReason()==KErrNone);
+	test(s==KErrNone);
+	CLOSE_AND_WAIT(t);
+	}
+
+void TestCleanupHandler()
+	{
+	HEAP_MARK;
+
+	test.Start(_L("Test TLS cleanup handlers"));
+	DoTestCleanupHandler(TlsCleanupThread1, (TAny*)1 );
+	DoTestCleanupHandler(TlsCleanupThread1, (TAny*)2 );
+	DoTestCleanupHandler(TlsCleanupThread1, (TAny*)3 );
+	DoTestCleanupHandler(TlsCleanupThread2, NULL );
+
+	test.End();
+
+	HEAP_MARKEND;
+	}
+#endif
+
+void Benchmark()
+	{
+	const TInt KMaxEntries=(512*1024)/sizeof(STls); // limits TLS entries to 512K of storage
+	const TInt KMaxTime=500000; // limits time to half a second
+	
+	HEAP_MARK;
+
+	test.Start(_L("SetTls()"));//Note: much slower if done in reverse order
+	TInt count=0;
+	TTime start;
+	TTime finish;
+	TTime now;
+	start.HomeTime();
+	finish.HomeTime();
+	finish+=TTimeIntervalMicroSeconds(KMaxTime);
+	while (now.HomeTime(),now<finish && count<KMaxEntries)
+		{
+		test(UserSvr::DllSetTls(count,KCheckValue)==KErrNone);
+		count++;
+		}
+	TTimeIntervalMicroSeconds elapsed=now.MicroSecondsFrom(start);
+	TInt scount=(TInt)((count*500000.0)/elapsed.Int64()); // scale count up to 1/2 second
+	test.Printf(_L("     Done %d in 1/2 sec\n"),scount);
+	if (count==KMaxEntries)
+		test.Printf(_L("     (%d in %f sec)\n"),count,elapsed.Int64()/1000000.0);
+
+	TInt max=count;
+
+	test.Next(_L("Tls()"));
+	TInt i=0;
+	count=0;
+	finish.HomeTime();
+	finish+=TTimeIntervalMicroSeconds(KMaxTime);
+	while (now.HomeTime(),now<finish)
+		{
+		test(UserSvr::DllTls(i)==KCheckValue);
+		count++;
+		if (++i==max)
+			i=0;
+		}
+	elapsed=now.MicroSecondsFrom(start);
+	scount=(TInt)((count*500000.0)/elapsed.Int64()); // scale count up to 1/2 second
+	test.Printf(_L("     Done %d in 1/2 sec\n"),scount);
+
+	test.Next(_L("FreeTls()"));//Note: much faster if done in reverse order
+	count=0;
+	start.HomeTime();
+	finish.HomeTime();
+	finish+=TTimeIntervalMicroSeconds(KMaxTime);
+	while (now.HomeTime(),now<finish)
+		{
+		UserSvr::DllFreeTls(count++);
+		if (count==max)
+			break;
+		}
+	elapsed=now.MicroSecondsFrom(start);
+	scount=(TInt)((count*500000.0)/elapsed.Int64()); // scale count up to 1/2 second
+	test.Printf(_L("     Done %d in 1/2 sec\n"),scount);
+	if (count==max)
+		test.Printf(_L("     (%d in %f sec)\n"),count,elapsed.Int64()/1000000.0);
+	
+	while (count<max)
+		UserSvr::DllFreeTls(--max);
+
+	HEAP_MARKEND;
+	test.End();
+	}
+
+TInt E32Main()
+	{
+
+	test.Title();
+	test.Start(_L("Test"));
+
+	// Turn off lazy dll unloading
+	RLoader l;
+	test(l.Connect()==KErrNone);
+	test(l.CancelLazyDllUnload()==KErrNone);
+	l.Close();
+
+	Test();
+	test.Next(_L("Thread Test"));
+
+	HEAP_MARK;
+
+	RThread thread;
+	TInt r=thread.Create(_L("TLS test thread"),TestThread,KDefaultStackSize,0x1000,0x8000,NULL);
+	test(r==KErrNone);
+	TRequestStatus stat;
+	thread.Logon(stat);
+	thread.Resume();
+	User::WaitForRequest(stat);
+	test(stat==KErrNone);
+	test(thread.ExitCategory()==_L("Kill"));
+	test(thread.ExitType()==EExitKill); 
+	test(thread.ExitReason()==KErrNone); 
+	CLOSE_AND_WAIT(thread);
+	
+	HEAP_MARKEND;
+
+	if (TLSStoredOnUserHeap())
+		{
+		MainHeap = &User::Heap();
+		TestUserSideTls1();
+		TestUserSideTls2();
+		TestUserSideTls3();
+		TestUserSideTls4();		
+		}
+	
+	User::After(100);
+	
+	// On serial screen systems, WSERV may not yet have completed the disconnect
+	// from the second thread. This causes the kernel heap check to fail.
+	// So we wait here until WSERV can run.
+	test.Printf(_L("Wait for WSERV\n"));
+
+//	TestCleanupHandler();
+
+	test.Next(_L("Benchmark"));
+	Benchmark();
+	test.End();
+	return KErrNone;
+	}