sl@0: // Copyright (c) 2002-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\emul\t_emul.cpp sl@0: // Overview: sl@0: // Test the mechanism to escape threads from the emulator in order sl@0: // to block on Windows objects sl@0: // Test launching processes doesn't leak windows TLS indices sl@0: // API Information: sl@0: // Emulator sl@0: // Details: sl@0: // - Test the mechanism to escape threads from the emulator in order sl@0: // to block on Windows objects: sl@0: // - test escape and re-enter mechanism. sl@0: // - block on Windows in EPOC thread and escaped EPOC thread. 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: // sl@0: // sl@0: sl@0: #define __E32TEST_EXTENSION__ sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include "e32test.h" sl@0: #include "emulator.h" sl@0: #include "t_emul.h" sl@0: sl@0: #define WIN32_LEAN_AND_MEAN sl@0: #pragma warning( disable : 4201 ) // nonstandard extension used : nameless struct/union sl@0: #include sl@0: #pragma warning( default : 4201 ) // nonstandard extension used : nameless struct/union sl@0: sl@0: LOCAL_D RTest test(_L("t_emul")); sl@0: sl@0: static TInt ELock; sl@0: static HANDLE ESemaphore; sl@0: static TInt EResult; sl@0: sl@0: TInt EscapeTimeoutThread(TAny*) sl@0: { sl@0: User::After(1000000); sl@0: if (__e32_atomic_add_ord32(&ELock, 1) == 0) sl@0: { sl@0: EResult=KErrTimedOut; sl@0: ReleaseSemaphore(ESemaphore,1,NULL); sl@0: } sl@0: return KErrNone; sl@0: } sl@0: sl@0: TInt EscapeSignalThread(TAny*) sl@0: { sl@0: if (__e32_atomic_add_ord32(&ELock, 1) == 0) sl@0: { sl@0: EResult=KErrNone; sl@0: ReleaseSemaphore(ESemaphore,1,NULL); sl@0: } sl@0: return KErrNone; sl@0: } sl@0: sl@0: TInt DoTestEscape(TBool aDoEscape) sl@0: // sl@0: // Test the mechanism to escape threads from the emulator in order to block on Windows objects sl@0: // sl@0: { sl@0: ELock = 0; sl@0: EResult = KRequestPending; sl@0: ESemaphore = CreateSemaphoreA(NULL,0,1,NULL); sl@0: test (ESemaphore != NULL); sl@0: RThread t1,t2; sl@0: TRequestStatus s1,s2; sl@0: TInt r = t1.Create(KNullDesC,&EscapeSignalThread,0x1000,NULL,NULL); sl@0: test (r == KErrNone); sl@0: t1.SetPriority(EPriorityLess); sl@0: t1.Logon(s1); sl@0: t1.Resume(); sl@0: r = t2.Create(KNullDesC,&EscapeTimeoutThread,0x1000,NULL,NULL); sl@0: test (r == KErrNone); sl@0: t2.SetPriority(EPriorityMore); sl@0: t2.Logon(s2); sl@0: t2.Resume(); sl@0: // sl@0: if (aDoEscape) sl@0: Emulator::Escape(); sl@0: r = WaitForSingleObject(ESemaphore,INFINITE); sl@0: if (aDoEscape) sl@0: Emulator::Reenter(); sl@0: test (r==WAIT_OBJECT_0); sl@0: // sl@0: r = EResult; sl@0: t1.Kill(0); sl@0: t2.Kill(0); sl@0: t1.Close(); sl@0: t2.Close(); sl@0: User::WaitForRequest(s1); sl@0: User::WaitForRequest(s2); sl@0: // sl@0: CloseHandle(ESemaphore); sl@0: return r; sl@0: } sl@0: sl@0: void TestEscape() sl@0: // sl@0: // Test the mechanism to escape threads from the emulator in order to block on Windows objects sl@0: // sl@0: { sl@0: test.Start(_L("Test escape and reenter mechanism")); sl@0: for (TInt i = 0;i<10000;++i) sl@0: { sl@0: Emulator::Escape(); sl@0: if (i%100 == 0) sl@0: Sleep(10); sl@0: Emulator::Reenter(); sl@0: } sl@0: test.Next(_L("Block on Windows in EPOC thread")); sl@0: TInt r = DoTestEscape(EFalse); sl@0: test (r == KErrTimedOut); sl@0: test.Next(_L("Block on Windows in escaped EPOC thread")); sl@0: r = DoTestEscape(ETrue); sl@0: test (r == KErrNone); sl@0: test.End(); sl@0: } sl@0: sl@0: TInt CountRemainingTlsIndicies() sl@0: { sl@0: const TInt KMax = 2000; sl@0: sl@0: TBool allocated[KMax]; sl@0: memclr(allocated, sizeof(TBool) * KMax); sl@0: TInt i; sl@0: for (i = 0 ; i < KMax ; ++i) sl@0: { sl@0: TInt index = TlsAlloc(); sl@0: if (index == TLS_OUT_OF_INDEXES) sl@0: break; sl@0: test(index >= 0 && index < KMax); sl@0: allocated[index] = ETrue; sl@0: } sl@0: for (TInt j = 0 ; j < KMax ; ++j) sl@0: { sl@0: if (allocated[j]) sl@0: test(TlsFree(j)); sl@0: } sl@0: return i; sl@0: } sl@0: sl@0: void RunSlave(TSlaveAction aAction) sl@0: { sl@0: RProcess p; sl@0: TBuf<8> arg; sl@0: arg.Format(_L("%d"), aAction); sl@0: test_KErrNone(p.Create(KTEmulSlaveName, arg)); sl@0: p.Resume(); sl@0: TRequestStatus status; sl@0: p.Logon(status); sl@0: User::WaitForRequest(status); sl@0: test_KErrNone(status.Int()); sl@0: test_Equal(EExitKill, p.ExitType()); sl@0: p.Close(); sl@0: } sl@0: sl@0: void TestRuntimeCleanup() sl@0: { sl@0: test.Start(_L("Test Codewarrior runtime library is correctly cleaned up")); sl@0: TInt initIndicies = CountRemainingTlsIndicies(); sl@0: sl@0: test.Next(_L("Test creating a process doesn't leak windows TLS indicies")); sl@0: RunSlave(ESlaveDoNothing); sl@0: test_Equal(initIndicies, CountRemainingTlsIndicies()); sl@0: sl@0: test.Next(_L("Test leaving in an exe doesn't leak windows TLS indicies")); sl@0: RunSlave(ESlaveTrapExceptionInExe); sl@0: test_Equal(initIndicies, CountRemainingTlsIndicies()); sl@0: sl@0: test.Next(_L("Test leaving in a linked DLL doesn't leak windows TLS indicies")); sl@0: RunSlave(ESlaveTrapExceptionInLinkedDll); sl@0: test_Equal(initIndicies, CountRemainingTlsIndicies()); sl@0: sl@0: test.Next(_L("Test leaving in a loaded DLL doesn't leak windows TLS indicies")); sl@0: RunSlave(ESlaveTrapExceptionInLoadedDll); sl@0: test_Equal(initIndicies, CountRemainingTlsIndicies()); sl@0: sl@0: test.Next(_L("Test cleanup doesn't happen while DLL still loaded")); sl@0: RLibrary l; sl@0: test_KErrNone(l.Load(KTEmulDll2Name)); sl@0: RunSlave(ESlaveTrapExceptionInLoadedDll); sl@0: TInt midCount = CountRemainingTlsIndicies(); sl@0: test(initIndicies > midCount); sl@0: sl@0: test.Next(_L("Test previous detach doesn't cause runtime to be re-initalised")); sl@0: TTrapExceptionInDllFunc func = sl@0: (TTrapExceptionInDllFunc)l.Lookup(KTrapExceptionInDllOrdinal); sl@0: test_NotNull(func); sl@0: func(); sl@0: test_Equal(midCount, CountRemainingTlsIndicies()); sl@0: l.Close(); sl@0: test_Equal(initIndicies, CountRemainingTlsIndicies()); sl@0: sl@0: test.End(); sl@0: } sl@0: sl@0: sl@0: void DoSomething2L() sl@0: { sl@0: test.Printf(_L("@\n")); sl@0: } sl@0: sl@0: sl@0: void DoSomething1L() sl@0: { sl@0: TInt i = -1, j = 1; sl@0: for ( ; ; ) sl@0: { sl@0: i++; sl@0: //DoSomething2L() must only be called once , else we know that trap mechanism didn't work sl@0: test( i<2); sl@0: if (i == j) sl@0: { sl@0: User::Leave(KErrNotFound); sl@0: } sl@0: sl@0: TRAP_IGNORE(DoSomething2L()); sl@0: TRAPD(errr, DoSomething2L()); sl@0: TInt r; sl@0: TRAP(r, DoSomething2L()); sl@0: sl@0: } sl@0: } sl@0: sl@0: sl@0: void LeaveIfArgIsTwo(TInt aCall); sl@0: sl@0: class CFred : public CBase sl@0: { sl@0: public: sl@0: static CFred* NewLC(); sl@0: CFred(); sl@0: ~CFred(); sl@0: }; sl@0: sl@0: void DoSomething3L() sl@0: { sl@0: // Push something on the cleanup stack so we can see whyen it gets cleaned up. sl@0: CFred *fred = CFred::NewLC(); sl@0: sl@0: TInt beforeFunc=0; sl@0: TInt betweenFuncAndTRAPD=0; sl@0: TInt afterTRAPD=0; sl@0: for(TInt loop=1; loop<=2; ++loop) sl@0: { sl@0: ++beforeFunc; sl@0: test.Printf(_L("Before LeaveIfArgIsTwo()\n")); sl@0: sl@0: // The first time around the loop, this function call works. sl@0: // The second time, it leaves KErrGeneral sl@0: // Then when TRAP mechanism isn't working properly the emulator (correctly) sl@0: // would delete fred, and (incorrectly) jump to the line sl@0: // just after the TRAPD call a few lines further down the file! sl@0: LeaveIfArgIsTwo(loop); sl@0: sl@0: ++betweenFuncAndTRAPD; sl@0: test.Printf(_L("Between LeaveIfArgIsTwo() and TRAPD\n")); sl@0: sl@0: TRAPD(err, test.Printf(_L("Inside TRAPD\n") ) ); sl@0: sl@0: sl@0: // It should only be possible to reach this section of code by executing all the lines sl@0: // between LeaveIfArgIsTwo and here. sl@0: ++afterTRAPD; sl@0: sl@0: } sl@0: sl@0: // Should NEVER get here because LeaveIfArgIsTwo did a leave the second time around the loop sl@0: test.Printf(_L("After loop (should NEVER get here) -\n\ sl@0: beforeFunc %d\n\ sl@0: betweenFuncAndTRAPD %d\n\ sl@0: afterTRAPD %d\n"), sl@0: beforeFunc, betweenFuncAndTRAPD, afterTRAPD); sl@0: sl@0: test.Printf(_L("It should be impossible for afterTRAPD to be larger than betweenFuncAndTRAPD\n")); sl@0: test(afterTRAPD <= betweenFuncAndTRAPD); sl@0: sl@0: // Cleanup our cleanup stack. sl@0: CleanupStack::PopAndDestroy(&fred); sl@0: } sl@0: sl@0: void LeaveIfArgIsTwo(TInt aCall) sl@0: { sl@0: sl@0: test.Printf(_L("aCall %d\n"), aCall); sl@0: if(aCall == 2) sl@0: { sl@0: User::Leave(KErrGeneral); sl@0: } sl@0: } sl@0: sl@0: CFred *CFred::NewLC() sl@0: { sl@0: CFred *self = new(ELeave) CFred; sl@0: CleanupStack::PushL(self); sl@0: return self; sl@0: } sl@0: sl@0: CFred::CFred() sl@0: { sl@0: test.Printf(_L("CFred %x\n"), this); sl@0: } sl@0: sl@0: CFred::~CFred() sl@0: { sl@0: test.Printf(_L("~CFred %x\n"), this); sl@0: } sl@0: sl@0: sl@0: sl@0: GLDEF_C TInt E32Main() sl@0: // sl@0: // sl@0: // sl@0: { sl@0: test.Title(); sl@0: test.Start(_L("Starting tests ...")); sl@0: sl@0: // Turn off evil lazy dll unloading sl@0: RLoader l; sl@0: test(l.Connect()==KErrNone); sl@0: test(l.CancelLazyDllUnload()==KErrNone); sl@0: l.Close(); sl@0: sl@0: TestEscape(); sl@0: #ifdef __CW32__ sl@0: TestRuntimeCleanup(); sl@0: #endif sl@0: sl@0: // The following tests were added to test that the TRAP mechanism works correctly in case of sl@0: // nested TRAP's. A compiler bug (winscw) caused the following tests to fail, since the wrong sl@0: // trap handler would be invoked, when User::Leave() was called. sl@0: sl@0: test.Next(_L("Check User::Leave is handled by the correct TRAP handler when nested TRAPs are present - Simple scenario\n")); sl@0: sl@0: TRAPD(err, DoSomething1L()); sl@0: test(err == KErrNotFound); sl@0: sl@0: test.Next(_L("Check User::Leave is handled by the correct TRAP handler when nested TRAPs are present - Cleanup stack scenario\n")); sl@0: __UHEAP_MARK; sl@0: CTrapCleanup* tc = CTrapCleanup::New(); sl@0: if (tc == 0) return KErrNoMemory; sl@0: sl@0: TRAPD(err2, DoSomething3L()); sl@0: test(err2==KErrGeneral); sl@0: sl@0: delete tc; sl@0: __UHEAP_MARKEND; sl@0: sl@0: sl@0: test.End(); sl@0: test.Close(); sl@0: return KErrNone; sl@0: } sl@0: