sl@0: // Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32test\dll\t_tls.cpp sl@0: // Overview: sl@0: // Test and benchmark TLS (Thread Local Storage) functions sl@0: // API Information: sl@0: // UserSvr sl@0: // Details: sl@0: // - Use the UserSvr methods: DllTls, DllSetTls and DllFreeTls to test sl@0: // the thread local storage. Verify the expected results. Verify that sl@0: // the heap was not corrupted by any of the tests. sl@0: // - In a separate thread, use the UserSvr methods: DllTls, DllSetTls and sl@0: // DllFreeTls to test the thread local storage. Verify the expected results. sl@0: // - Test the TLS cleanup handlers and verify results are as expected. Verify sl@0: // that the heap was not corrupted by any of the tests. sl@0: // - Benchmark how many DllSetTls, DllTls and DllFreeTls operations can be sl@0: // performed in 1/2 second. sl@0: // Platforms/Drives/Compatibility: sl@0: // All. sl@0: // Assumptions/Requirement/Pre-requisites: sl@0: // Failures and causes: sl@0: // Base Port information: sl@0: // Tests and benchmarks Tls functions sl@0: // 14/10/96 Modified scheme on Arm release sl@0: // SetTls - 352 ~1300 sl@0: // Tls - 3510 ~1600 sl@0: // 15/10/96 Array scheme on Arm release sl@0: // Set Tls - ~1900 sl@0: // Tls - ~2550 sl@0: // sl@0: // sl@0: sl@0: #define __E32TEST_EXTENSION__ sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include "u32std.h" sl@0: sl@0: // Can't tell whether allocations will be on the kernel or user heap, so perform all heap check sl@0: // operations on both sl@0: #define HEAP_MARK __UHEAP_MARK; __KHEAP_MARK sl@0: #define HEAP_MARKEND __UHEAP_MARKEND; __KHEAP_MARKEND sl@0: #define HEAP_RESET __UHEAP_RESET; __KHEAP_RESET sl@0: #define HEAP_CHECK(x) __UHEAP_CHECK(x); __KHEAP_CHECK(x) sl@0: #define HEAP_FAILNEXT(x) __UHEAP_FAILNEXT(x); __KHEAP_FAILNEXT(x) sl@0: sl@0: TInt const KCheckHandle=67338721; sl@0: TUint8* const KCheckValue=(TUint8*)8525124; sl@0: sl@0: LOCAL_D RTest test(_L("T_TLS")); sl@0: sl@0: _LIT(KTestDllName, "t_tlsdll"); sl@0: const TInt KTestDllOrdSet = 1; sl@0: const TInt KTestDllOrdGet = 2; sl@0: const TInt KTestDllOrdFree = 3; sl@0: sl@0: typedef TInt (*TTestDllSetFn)(TAny*); sl@0: typedef TAny* (*TTestDllGetFn)(); sl@0: typedef void (*TTestDllFreeFn)(); sl@0: sl@0: TInt TestDllSetTls(RLibrary aLib, TAny* aValue) sl@0: { sl@0: TTestDllSetFn f = (TTestDllSetFn)aLib.Lookup(KTestDllOrdSet); sl@0: return (*f)(aValue); sl@0: } sl@0: sl@0: TAny* TestDllGetTls(RLibrary aLib) sl@0: { sl@0: TTestDllGetFn f = (TTestDllGetFn)aLib.Lookup(KTestDllOrdGet); sl@0: return (*f)(); sl@0: } sl@0: sl@0: void TestDllFreeTls(RLibrary aLib) sl@0: { sl@0: TTestDllFreeFn f = (TTestDllFreeFn)aLib.Lookup(KTestDllOrdFree); sl@0: (*f)(); sl@0: } sl@0: sl@0: #define DISPLAY_PROGRESS test.Printf(_L("Line %d\n"), __LINE__) sl@0: sl@0: void Test() sl@0: { sl@0: test.Start(_L("Stuff without Setting")); sl@0: sl@0: HEAP_MARK; sl@0: sl@0: test(UserSvr::DllTls(KCheckHandle)==NULL); sl@0: UserSvr::DllFreeTls(KCheckHandle); sl@0: test(UserSvr::DllTls(KCheckHandle)==NULL); sl@0: test(UserSvr::DllTls(0)==NULL); sl@0: UserSvr::DllFreeTls(0); sl@0: test(UserSvr::DllTls(0)==NULL); sl@0: sl@0: HEAP_CHECK(0); sl@0: sl@0: test.Next(_L("Set")); sl@0: test(UserSvr::DllSetTls(KCheckHandle,KCheckValue)==KErrNone); sl@0: test.Next(_L("Get")); sl@0: test(UserSvr::DllTls(KCheckHandle)==KCheckValue); sl@0: test.Next(_L("Free")); sl@0: UserSvr::DllFreeTls(KCheckHandle); sl@0: test(UserSvr::DllTls(KCheckHandle)==NULL); sl@0: sl@0: HEAP_CHECK(0); sl@0: sl@0: test.Next(_L("Set lots")); sl@0: TInt i=0; sl@0: for(;i<1000;i+=3) sl@0: { sl@0: test(UserSvr::DllSetTls(KCheckHandle+i,KCheckValue-i)==KErrNone); sl@0: test(UserSvr::DllTls(KCheckHandle+i)==KCheckValue-i); sl@0: } sl@0: for(i=999;i>0;i-=3) sl@0: { sl@0: test(UserSvr::DllTls(KCheckHandle+i)==KCheckValue-i); sl@0: UserSvr::DllFreeTls(KCheckHandle+i); sl@0: test(UserSvr::DllTls(KCheckHandle+i)==NULL); sl@0: } sl@0: test(UserSvr::DllTls(KCheckHandle+i)==KCheckValue-i); sl@0: UserSvr::DllFreeTls(KCheckHandle+i); sl@0: test(UserSvr::DllTls(KCheckHandle+i)==NULL); sl@0: sl@0: HEAP_MARKEND; sl@0: sl@0: sl@0: test.Next(_L("Test with DLL ID")); sl@0: sl@0: HEAP_MARK; sl@0: sl@0: test(UserSvr::DllTls(KCheckHandle, 1)==0); sl@0: test(UserSvr::DllTls(KCheckHandle, 2)==0); sl@0: DISPLAY_PROGRESS; sl@0: #ifdef _DEBUG sl@0: HEAP_FAILNEXT(1); sl@0: DISPLAY_PROGRESS; sl@0: test(UserSvr::DllSetTls(KCheckHandle, 1, KCheckValue)==KErrNoMemory); sl@0: #endif sl@0: test(UserSvr::DllSetTls(KCheckHandle, 1, KCheckValue)==KErrNone); sl@0: test(UserSvr::DllTls(KCheckHandle, 1)==KCheckValue); sl@0: test(UserSvr::DllTls(KCheckHandle, 2)==0); sl@0: DISPLAY_PROGRESS; sl@0: HEAP_FAILNEXT(1); sl@0: DISPLAY_PROGRESS; sl@0: test(UserSvr::DllSetTls(KCheckHandle, 3, KCheckValue)==KErrNone); sl@0: sl@0: #ifdef _DEBUG sl@0: RSemaphore s; sl@0: test(s.CreateLocal(0)==KErrNoMemory); sl@0: #endif sl@0: sl@0: HEAP_RESET; sl@0: sl@0: test(UserSvr::DllTls(KCheckHandle, 1)==0); sl@0: test(UserSvr::DllTls(KCheckHandle, 2)==0); sl@0: test(UserSvr::DllTls(KCheckHandle, 3)==KCheckValue); sl@0: UserSvr::DllFreeTls(KCheckHandle); sl@0: sl@0: DISPLAY_PROGRESS; sl@0: HEAP_MARKEND; sl@0: DISPLAY_PROGRESS; sl@0: sl@0: test.Next(_L("Test reloading DLL")); sl@0: sl@0: RLibrary l; sl@0: TInt r = l.Load(KTestDllName); sl@0: test(r==KErrNone); sl@0: for (i=0; i<2; ++i) sl@0: { sl@0: test.Printf(_L("i=%d\n"),i); sl@0: test(TestDllGetTls(l)==0); sl@0: test(TestDllSetTls(l, KCheckValue)==KErrNone); sl@0: test(TestDllGetTls(l)==KCheckValue); sl@0: if (i==0) sl@0: { sl@0: TestDllFreeTls(l); sl@0: test(TestDllGetTls(l)==0); sl@0: } sl@0: l.Close(); sl@0: r = l.Load(KTestDllName); sl@0: test(r==KErrNone); sl@0: test(TestDllGetTls(l)==0); sl@0: TestDllFreeTls(l); sl@0: } sl@0: l.Close(); sl@0: sl@0: test.End(); sl@0: } sl@0: sl@0: TInt TestThread(TAny*) sl@0: { sl@0: RTest test(_L("T_TLS Thread")); sl@0: test.Start(_L("Stuff without Setting")); sl@0: test(UserSvr::DllTls(KCheckHandle)==NULL); sl@0: UserSvr::DllFreeTls(KCheckHandle); sl@0: test(UserSvr::DllTls(KCheckHandle)==NULL); sl@0: test(UserSvr::DllTls(0)==NULL); sl@0: UserSvr::DllFreeTls(0); sl@0: test(UserSvr::DllTls(0)==NULL); sl@0: test.Next(_L("Set")); sl@0: test(UserSvr::DllSetTls(KCheckHandle,KCheckValue)==KErrNone); sl@0: test.Next(_L("Get")); sl@0: test(UserSvr::DllTls(KCheckHandle)==KCheckValue); sl@0: test.Next(_L("Free")); sl@0: UserSvr::DllFreeTls(KCheckHandle); sl@0: test(UserSvr::DllTls(KCheckHandle)==NULL); sl@0: test.Next(_L("Set lots")); sl@0: TInt i=0; sl@0: for(;i<1000;i+=3) sl@0: { sl@0: test(UserSvr::DllSetTls(KCheckHandle+i,KCheckValue-i)==KErrNone); sl@0: test(UserSvr::DllTls(KCheckHandle+i)==KCheckValue-i); sl@0: } sl@0: for(i=999;i>=0;i-=3) sl@0: { sl@0: test(UserSvr::DllTls(KCheckHandle+i)==KCheckValue-i); sl@0: UserSvr::DllFreeTls(KCheckHandle+i); sl@0: test(UserSvr::DllTls(KCheckHandle+i)==NULL); sl@0: } sl@0: sl@0: test.Close(); sl@0: return KErrNone; sl@0: } sl@0: sl@0: TInt HeapSize(RHeap* aHeap = NULL) sl@0: { sl@0: if (!aHeap) sl@0: aHeap = &User::Heap(); sl@0: TInt size; sl@0: aHeap->AllocSize(size); sl@0: return size; sl@0: } sl@0: sl@0: TBool TLSStoredOnUserHeap() sl@0: { sl@0: TInt initCount = HeapSize(); sl@0: TInt i; sl@0: const TInt KMax = 2; sl@0: for(i = 0 ; i < KMax ; ++i) sl@0: test(UserSvr::DllSetTls(KCheckHandle+i,KCheckValue-i) == KErrNone); sl@0: TInt finalCount = HeapSize(); sl@0: for(i = 0 ; i < KMax ; ++i) sl@0: UserSvr::DllFreeTls(KCheckHandle+i); sl@0: return initCount != finalCount; sl@0: } sl@0: sl@0: class RDummyAllocator : public RAllocator sl@0: { sl@0: public: sl@0: TInt AccessCount() { return iAccessCount; } sl@0: }; sl@0: sl@0: RHeap* MainHeap = NULL; sl@0: sl@0: TInt HeapAccessCount(TAny* aHeap) sl@0: { sl@0: RDummyAllocator* heap = (RDummyAllocator*)aHeap; sl@0: return heap->AccessCount(); sl@0: } sl@0: sl@0: TBool HeapExists(RHeap* aHeap) sl@0: { sl@0: RThread thread; sl@0: test_KErrNone(thread.Create(_L("HeapExistsThread"), HeapAccessCount, 0x1000, MainHeap, (TAny*)aHeap)); sl@0: TRequestStatus status; sl@0: thread.Logon(status); sl@0: thread.Resume(); sl@0: User::WaitForRequest(status); sl@0: sl@0: TInt r; sl@0: if (thread.ExitType() == EExitKill) sl@0: { sl@0: r = thread.ExitReason(); sl@0: test_NotNegative(r); sl@0: } sl@0: else sl@0: { sl@0: test_Equal(EExitPanic, thread.ExitType()); sl@0: test_Equal(3, thread.ExitReason()); sl@0: r = 0; sl@0: } sl@0: thread.Close(); sl@0: sl@0: return r != 0; sl@0: } sl@0: sl@0: TInt TestUserSideTls1Thread(TAny*) sl@0: { sl@0: // Ensure TLS uses initial heap sl@0: if (UserSvr::DllSetTls(KCheckHandle,KCheckValue) != KErrNone) sl@0: return __LINE__; sl@0: UserSvr::DllFreeTls(KCheckHandle); sl@0: sl@0: RHeap* tlsHeap = &User::Heap(); sl@0: TInt tlsInitSize = HeapSize(tlsHeap); sl@0: sl@0: // Switch heap sl@0: RHeap* newHeap = User::ChunkHeap(NULL, 0x1000, 0x1000); sl@0: if (!newHeap) sl@0: return __LINE__; sl@0: TInt newInitSize = HeapSize(newHeap); sl@0: User::SwitchHeap(newHeap); sl@0: tlsHeap->Close(); sl@0: sl@0: // Allocate more TLS data sl@0: for(TInt i = 0 ; i < 100 ; ++i) sl@0: { sl@0: if (UserSvr::DllSetTls(KCheckHandle+i,KCheckValue-i) != KErrNone) sl@0: return __LINE__; sl@0: } sl@0: sl@0: // Test TLS data was allocated on original heap sl@0: if (!(HeapSize(tlsHeap) > tlsInitSize)) sl@0: return __LINE__; sl@0: if (HeapSize(newHeap) != newInitSize) sl@0: return __LINE__; sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: void TestUserSideTls1() sl@0: { sl@0: test.Next(_L("Test user-side TLS behaviour when switching heaps")); sl@0: sl@0: RThread thread; sl@0: test_KErrNone(thread.Create(_L("TestUserSideTls1Thread"), TestUserSideTls1Thread, 0x1000, 0x1000, 0x1000, 0)); sl@0: sl@0: TRequestStatus status; sl@0: thread.Logon(status); sl@0: thread.Resume(); sl@0: User::WaitForRequest(status); sl@0: sl@0: test_Equal(EExitKill, thread.ExitType()); sl@0: test_Equal(KErrNone, thread.ExitReason()); sl@0: thread.Close(); sl@0: } sl@0: sl@0: TInt TestUserSideTls2Thread(TAny*) sl@0: { sl@0: // Allocate some TLS data sl@0: for(TInt i = 0 ; i < 100 ; ++i) sl@0: { sl@0: if (UserSvr::DllSetTls(KCheckHandle+i,KCheckValue-i) != KErrNone) sl@0: return __LINE__; sl@0: } sl@0: sl@0: // Exit normally sl@0: return KErrNone; sl@0: } sl@0: sl@0: void TestUserSideTls2() sl@0: { sl@0: test.Next(_L("Test user-side TLS data cleanup on thread exit")); sl@0: sl@0: RHeap* tlsHeap = User::ChunkHeap(NULL, 0x1000, 0x1000); sl@0: test_NotNull(tlsHeap); sl@0: TInt initSize = HeapSize(tlsHeap); sl@0: sl@0: RThread thread; sl@0: test_KErrNone(thread.Create(_L("TestUserSideTls2Thread"), TestUserSideTls2Thread, 0x1000, tlsHeap, 0)); sl@0: TThreadId id = thread.Id(); sl@0: sl@0: TRequestStatus status; sl@0: thread.Logon(status); sl@0: thread.Resume(); sl@0: User::WaitForRequest(status); sl@0: sl@0: test_Equal(EExitKill, thread.ExitType()); sl@0: test_Equal(KErrNone, thread.ExitReason()); sl@0: thread.Close(); sl@0: sl@0: // Check TLS data freed sl@0: test_Equal(initSize, HeapSize(tlsHeap)); sl@0: tlsHeap->Close(); sl@0: sl@0: // Check heap no longer exists sl@0: test(!HeapExists(tlsHeap)); sl@0: sl@0: // Check thread no longer exists sl@0: RThread thread2; sl@0: test_Equal(KErrNotFound, thread2.Open(id)); sl@0: } sl@0: sl@0: TInt TestUserSideTls3Thread(TAny*) sl@0: { sl@0: // Allocate some TLS data sl@0: for(TInt i = 0 ; i < 100 ; ++i) sl@0: { sl@0: if (UserSvr::DllSetTls(KCheckHandle+i,KCheckValue-i) != KErrNone) sl@0: return __LINE__; sl@0: } sl@0: sl@0: // Panic sl@0: User::Panic(_L("Test"), 999); sl@0: return KErrNone; sl@0: } sl@0: sl@0: void TestUserSideTls3() sl@0: { sl@0: test.Next(_L("Test user-side TLS data cleanup on thread panic")); sl@0: sl@0: RHeap* tlsHeap = User::ChunkHeap(NULL, 0x1000, 0x1000); sl@0: test_NotNull(tlsHeap); sl@0: sl@0: RThread thread; sl@0: test_KErrNone(thread.Create(_L("TestUserSideTls3Thread"), TestUserSideTls3Thread, 0x1000, tlsHeap, 0)); sl@0: TThreadId id = thread.Id(); sl@0: sl@0: TRequestStatus status; sl@0: thread.Logon(status); sl@0: thread.Resume(); sl@0: User::WaitForRequest(status); sl@0: sl@0: test_Equal(EExitPanic, thread.ExitType()); sl@0: test_Equal(999, thread.ExitReason()); sl@0: sl@0: thread.Close(); sl@0: tlsHeap->Close(); sl@0: sl@0: // Check heap no longer exists sl@0: test(!HeapExists(tlsHeap)); sl@0: sl@0: // Check thread no longer exists sl@0: RThread thread2; sl@0: test_Equal(KErrNotFound, thread2.Open(id)); sl@0: } sl@0: sl@0: TInt TestUserSideTls4Thread(TAny*) sl@0: { sl@0: // Shouldn't ever run sl@0: return KErrGeneral; sl@0: } sl@0: sl@0: void TestUserSideTls4() sl@0: { sl@0: test.Next(_L("Test user-side TLS data cleanup on early thread kill")); sl@0: sl@0: RHeap* tlsHeap = User::ChunkHeap(NULL, 0x1000, 0x1000); sl@0: test_NotNull(tlsHeap); sl@0: sl@0: RThread thread; sl@0: test_KErrNone(thread.Create(_L("TestUserSideTls4Thread"), TestUserSideTls4Thread, 0x1000, tlsHeap, 0)); sl@0: TThreadId id = thread.Id(); sl@0: sl@0: thread.Kill(101); sl@0: sl@0: TRequestStatus status; sl@0: thread.Logon(status); sl@0: User::WaitForRequest(status); sl@0: sl@0: test_Equal(EExitKill, thread.ExitType()); sl@0: test_Equal(101, thread.ExitReason()); sl@0: sl@0: thread.Close(); sl@0: tlsHeap->Close(); sl@0: sl@0: // Check heap no longer exists sl@0: test(!HeapExists(tlsHeap)); sl@0: sl@0: // Check thread no longer exists sl@0: RThread thread2; sl@0: test_Equal(KErrNotFound, thread2.Open(id)); sl@0: } sl@0: sl@0: #if 0 sl@0: class CTest : public CBase sl@0: { sl@0: public: sl@0: static CTest* New(TInt aSize, TInt aTls, TInt aLinkedTls); sl@0: virtual ~CTest(); sl@0: public: sl@0: TAny* iAlloc; sl@0: TInt iTls; sl@0: TInt iLinkedTls; sl@0: }; sl@0: sl@0: void TlsCleanup2(TAny* a) sl@0: { sl@0: delete ((CTest*)a); sl@0: } sl@0: sl@0: CTest::~CTest() sl@0: { sl@0: RDebug::Print(_L("~CTest %d %d"), iTls, iLinkedTls); sl@0: User::Free(iAlloc); sl@0: UserSvr::DllFreeTls(iTls); sl@0: if (iLinkedTls) sl@0: { sl@0: CTest* p = (CTest*)UserSvr::DllTls(iLinkedTls); sl@0: if (p) sl@0: delete p; sl@0: } sl@0: } sl@0: sl@0: CTest* CTest::New(TInt aSize, TInt aTls, TInt aLinkedTls) sl@0: { sl@0: CTest* p = new CTest; sl@0: test(p!=NULL); sl@0: p->iTls = aTls; sl@0: p->iAlloc = User::Alloc(aSize); sl@0: test(p->iAlloc!=NULL); sl@0: p->iLinkedTls = aLinkedTls; sl@0: test(UserSvr::DllSetTls(aTls, p, TlsCleanup2)==KErrNone); sl@0: return p; sl@0: } sl@0: sl@0: sl@0: void TlsCleanupCheck(TAny*) sl@0: { sl@0: __UHEAP_MARKEND; sl@0: } sl@0: sl@0: void TlsCleanup1(TAny* a) sl@0: { sl@0: User::Free(a); sl@0: } sl@0: sl@0: TInt TlsCleanupThread1(TAny* a) sl@0: { sl@0: __UHEAP_MARK; sl@0: TInt n = (TInt)a; sl@0: TInt i; sl@0: TInt r = UserSvr::DllSetTls(0, NULL, TlsCleanupCheck); sl@0: if (r!=KErrNone) sl@0: return r; sl@0: for (i=0; i exitCat = t.ExitCategory(); sl@0: test.Printf(_L("Exit Info: %d,%d,%S\n"), exitType, exitReason, &exitCat); sl@0: } sl@0: sl@0: void DoTestCleanupHandler(TThreadFunction aFunc, TAny* aArg) sl@0: { sl@0: RThread t; sl@0: TInt r = t.Create(KNullDesC, aFunc, 0x1000, NULL, aArg); sl@0: test(r==KErrNone); sl@0: TRequestStatus s; sl@0: t.Logon(s); sl@0: t.Resume(); sl@0: User::WaitForRequest(s); sl@0: DisplayExitInfo(t); sl@0: test(t.ExitType()==EExitKill); sl@0: test(t.ExitReason()==KErrNone); sl@0: test(s==KErrNone); sl@0: CLOSE_AND_WAIT(t); sl@0: } sl@0: sl@0: void TestCleanupHandler() sl@0: { sl@0: HEAP_MARK; sl@0: sl@0: test.Start(_L("Test TLS cleanup handlers")); sl@0: DoTestCleanupHandler(TlsCleanupThread1, (TAny*)1 ); sl@0: DoTestCleanupHandler(TlsCleanupThread1, (TAny*)2 ); sl@0: DoTestCleanupHandler(TlsCleanupThread1, (TAny*)3 ); sl@0: DoTestCleanupHandler(TlsCleanupThread2, NULL ); sl@0: sl@0: test.End(); sl@0: sl@0: HEAP_MARKEND; sl@0: } sl@0: #endif sl@0: sl@0: void Benchmark() sl@0: { sl@0: const TInt KMaxEntries=(512*1024)/sizeof(STls); // limits TLS entries to 512K of storage sl@0: const TInt KMaxTime=500000; // limits time to half a second sl@0: sl@0: HEAP_MARK; sl@0: sl@0: test.Start(_L("SetTls()"));//Note: much slower if done in reverse order sl@0: TInt count=0; sl@0: TTime start; sl@0: TTime finish; sl@0: TTime now; sl@0: start.HomeTime(); sl@0: finish.HomeTime(); sl@0: finish+=TTimeIntervalMicroSeconds(KMaxTime); sl@0: while (now.HomeTime(),now