diff -r 000000000000 -r a41df078684a kerneltest/e32test/dll/t_tls.cpp --- /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 +#include +#include +#include +#include +#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 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