Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h)
Have multiple extension sections in the bld.inf, one for each version
of the compiler. The RVCT version building the tools will build the
runtime libraries for its version, but make sure we extract all the other
versions from zip archives. Also add the archive for RVCT4.
// 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;
}