diff -r 000000000000 -r a41df078684a kerneltest/e32test/cppexceptions/t_unmap.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/cppexceptions/t_unmap.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1610 @@ +// Copyright (c) 2004-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\cppexceptions\t_unmap.cpp +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "d_unmap.h" + +_LIT(KTestName, "t_unmap"); +_LIT(KTestThreadName, "t_unmap test thread"); +_LIT(KNopThreadName, "nop [DLL unload checking] thread"); +_LIT(KTUnmapPanic, "t_unmap"); +_LIT(KThread, "Thread"); + +_LIT(KUnhandledExcCategory, "KERN-EXEC"); +const TInt KUnhandledExcReason = 3; + +enum TUnmapPanic + { + EPanickingThread = 123456789 + }; + +RTest test(KTestName); + +RTest testThreadA(KTestThreadName); +RTest testThreadB(KTestThreadName); +RTest testThreadC(KTestThreadName); +RTest testThreadD(KTestThreadName); +RTest testThreadE(KTestThreadName); +RTest testThreadF(KTestThreadName); +RTest testThreadG(KTestThreadName); +RTest testThreadH(KTestThreadName); + +RTest testThreadI(KTestThreadName); +RTest testThreadJ(KTestThreadName); +RTest testThreadK(KTestThreadName); + +RSemaphore Thread1Semaphore; +RSemaphore Thread2Semaphore; + +RSemaphore FinishedOpSemaphore; + +RLibrary ThreadALibraryHandle; +RLibrary ThreadBLibraryHandle; +RLibrary ThreadCLibraryHandle; +RLibrary ThreadDLibraryHandle; +RLibrary ThreadELibraryHandle; +RLibrary ThreadFLibraryHandle; +RLibrary ThreadGLibraryHandle; +RLibrary ThreadHLibraryHandle; +RLibrary ThreadILibraryHandle; +RLibrary ThreadJLibraryHandle; +RLibrary ThreadKLibraryHandle; + +TBool CheckKernelHeap; + +void TestThreads(); + +TInt ThreadA(TAny*); +TInt ThreadB(TAny*); +TInt ThreadC(TAny*); +TInt ThreadD(TAny*); +TInt ThreadE(TAny*); +TInt ThreadF(TAny*); +TInt ThreadG(TAny*); +TInt ThreadH(TAny*); +TInt ThreadI(TAny*); +TInt ThreadJ(TAny*); +TInt ThreadK(TAny*); + +TInt DoThreadAL(TAny*); +TInt DoThreadBL(TAny*); +TInt DoThreadCL(TAny*); +TInt DoThreadDL(TAny*); +TInt DoThreadEL(TAny*); +TInt DoThreadFL(TAny*); +TInt DoThreadGL(TAny*); +TInt DoThreadHL(TAny*); +TInt DoThreadIL(TAny*); +TInt DoThreadJL(TAny*); +TInt DoThreadKL(TAny*); + +struct STestThreadInfo + { + TThreadFunction iThreadFn; + TExitType iExitType; + TInt iMappedSignals; + TInt iLeaveSignals; + }; + +static STestThreadInfo const TheThreadArray[] = + { + { &ThreadA, EExitPanic, 0, 0 }, + { &ThreadB, EExitKill, 0, 0 }, + { &ThreadC, EExitPanic, 1, 0 }, + { &ThreadD, EExitPanic, 2, 0 }, + { &ThreadE, EExitKill, 2, 2 }, + { &ThreadF, EExitPanic, 2, 1 }, + { &ThreadG, EExitKill, 3, 1 }, + { &ThreadH, EExitKill, 1, 1 }, + { &ThreadI, EExitKill, 1, 3 }, + { &ThreadJ, EExitPanic, 1, 2 }, + { &ThreadK, EExitPanic, 1, 1 } + }; + +struct SNopThreadInfo + { + TLibraryFunction iFunc; +#ifdef __WINS__ + TInt32 iOriginalContents; +#endif + }; + +static SNopThreadInfo NopThreadInfo; + +static const TInt TheThreadCount = (sizeof(TheThreadArray) / sizeof(STestThreadInfo)); + +static const TInt KHeapSize = 0x2000; + +TInt E32Main() + { + // Turn off lazy dll unloading + RLoader l; + test(l.Connect()==KErrNone); + test(l.CancelLazyDllUnload()==KErrNone); + l.Close(); + + test.Start(_L("Check code seg unmapping over User::Leave()/C++ exceptions.")); + + __UHEAP_MARK; + // + CTrapCleanup* cleanup = CTrapCleanup::New(); + TInt r = KErrNoMemory; + if (cleanup) + { + TRAP(r, TestThreads()); + + test.Printf(_L("Returned %d, expected %d\n"), r, KErrNone); + test(r == KErrNone); + } + + delete cleanup; + // + __UHEAP_MARKEND; + + test.End(); + + return r; + } + +TInt NopThread(TAny*) + { +#ifdef __WINS__ + TInt32 current = *(TInt*)NopThreadInfo.iFunc; + if (current != NopThreadInfo.iOriginalContents) + current = *(TInt32*)NULL; // cause panic +#endif + TInt r = NopThreadInfo.iFunc(); + if (r) + return KErrNone; + else + return KErrGeneral; + } + +void TestLoadWhileUnload(); + +void TestThreads() + { + __KHEAP_MARK; + + test.Next(_L("Create synchronisation semaphores")); + TInt r = Thread1Semaphore.CreateLocal(0); + test(r == KErrNone); + + r = Thread2Semaphore.CreateLocal(0); + test(r == KErrNone); + + r = FinishedOpSemaphore.CreateLocal(0); + test(r == KErrNone); + + // Turn off JIT [threads can panic to test exit cleanup] + TBool jit = User::JustInTime(); + User::SetJustInTime(EFalse); + + TInt count, count2; + + // Do kernel heap checking + CheckKernelHeap = ETrue; + + test.Next(_L("Run threads on their own")); + for (count = 0; count < TheThreadCount ; ++count) + { + // Set up descriptor for thread's name + TBuf16<7> name(KThread); + name.Append('A' + count); + + // Create thread + RThread thread; + TInt r = thread.Create( + name, + TheThreadArray[count].iThreadFn, + KDefaultStackSize, + KHeapSize, + KHeapSize, + &Thread1Semaphore + ); + test(r == KErrNone); + + // Set up notification of thread's death + TRequestStatus status; + thread.Logon(status); + + // Load library + RLibrary library; + r = library.Load(KLeavingDll); + test(r == KErrNone); + + // Remember the address of the NOP function + NopThreadInfo.iFunc = library.Lookup(2); +#ifdef __WINS__ + NopThreadInfo.iOriginalContents = *(TInt32*)NopThreadInfo.iFunc; +#endif + + // Run thread + thread.Resume(); + + // Wait until it has an open handle to the library + FinishedOpSemaphore.Wait(); + + // Close our handle to the library + library.Close(); + + // Check library is still loaded + for (count2 = 0; count2 < TheThreadArray[count].iMappedSignals; ++count2) + { + // Tell it we're ready to go + Thread1Semaphore.Signal(); + + // Wait for it to finish next step + FinishedOpSemaphore.Wait(); + + // Create NOP thread to call NOP function to check DLL still loaded + RThread nopThread; + r = nopThread.Create( + KNopThreadName, + NopThread, + KDefaultStackSize, + KHeapSize, + KHeapSize, + NULL + ); + test(r == KErrNone); + + // Set up notification of thread's death + TRequestStatus nopStatus; + nopThread.Logon(nopStatus); + + // Run thread + nopThread.Resume(); + + // Wait for it to die + User::WaitForRequest(nopStatus); + + // Check the exit info + test(nopThread.ExitType() == EExitKill); + test(nopThread.ExitReason() == KErrNone); + + // Close thread handle + CLOSE_AND_WAIT(nopThread); + } + + // Check User::Leave() library unloading behaviour + for (count2 = 0; count2 < TheThreadArray[count].iLeaveSignals; ++count2) + { + // Tell it we're ready to go + Thread1Semaphore.Signal(); + + // Wait for it to finish next step + FinishedOpSemaphore.Wait(); + + // Create NOP thread to call NOP function to check whether DLL is still loaded + RThread nopThread; + r = nopThread.Create( + KNopThreadName, + NopThread, + KDefaultStackSize, + KHeapSize, + KHeapSize, + NULL + ); + test(r == KErrNone); + + // Set up notification of thread's death + TRequestStatus nopStatus; + nopThread.Logon(nopStatus); + + // Run thread + nopThread.Resume(); + + // Wait for it to die + User::WaitForRequest(nopStatus); + + // Check the exit info +#ifdef __LEAVE_EQUALS_THROW__ + test(nopThread.ExitType() == EExitKill); + test(nopThread.ExitReason() == KErrGeneral); +#else //!__LEAVE_EQUALS_THROW__ + test(nopThread.ExitType() == EExitPanic); + test(nopThread.ExitCategory() == KUnhandledExcCategory); + test(nopThread.ExitReason() == KUnhandledExcReason); +#endif //__LEAVE_EQUALS_THROW__ + + // Close thread handle + CLOSE_AND_WAIT(nopThread); + } + + // Tell it we're ready to go again + Thread1Semaphore.Signal(); + + if (TheThreadArray[count].iExitType == EExitKill) + { + // Wait for it to finish last step + FinishedOpSemaphore.Wait(); + + User::After(100000); // let supervisor run + + // Create NOP thread to call NOP function to check DLL is unloaded + RThread nopThread; + r = nopThread.Create( + KNopThreadName, + NopThread, + KDefaultStackSize, + KHeapSize, + KHeapSize, + NULL + ); + test(r == KErrNone); + + // Set up notification of thread's death + TRequestStatus nopStatus; + nopThread.Logon(nopStatus); + + // Run thread + nopThread.Resume(); + + // Wait for it to die + User::WaitForRequest(nopStatus); + + // Check the exit info + test(nopThread.ExitType() == EExitPanic); + test(nopThread.ExitCategory() == KUnhandledExcCategory); + test(nopThread.ExitReason() == KUnhandledExcReason); + + // Close thread handle + CLOSE_AND_WAIT(nopThread); + + // Let main thread die now + Thread1Semaphore.Signal(); + } + + // Wait for thread to exit + User::WaitForRequest(status); + + // Check the exit type & category + test(thread.ExitType() == TheThreadArray[count].iExitType); + + // Check category & reason, if appropriate + if (thread.ExitType() == EExitPanic) + { + test(thread.ExitCategory() == KTUnmapPanic); + test(thread.ExitReason() == EPanickingThread); + } + + // Close thread handle + thread.Close(); + } + + // Turn off kernel heap checking + CheckKernelHeap = EFalse; + + test.Next(_L("Run threads against each other")); + for (count = 0; count < TheThreadCount ; ++count) + { + for (count2 = 0; count2 < TheThreadCount ; ++count2) + { + // Can't run the same threads back to back + if (count == count2) + { + continue; + } + + // Set up descriptors for threads' names + _LIT(KFirstThread, " - 1"); + _LIT(KSecondThread, " - 2"); + TBuf16<11> name(KThread); + TBuf16<11> name2(KThread); + name.Append('A' + count); + name.Append(KFirstThread); + name2.Append('A' + count2); + name2.Append(KSecondThread); + + // Create threads + RThread thread; + TInt r = thread.Create( + name, + TheThreadArray[count].iThreadFn, + KDefaultStackSize, + KHeapSize, + KHeapSize, + &Thread1Semaphore + ); + test(r == KErrNone); + + RThread thread2; + r = thread2.Create( + name2, + TheThreadArray[count2].iThreadFn, + KDefaultStackSize, + KHeapSize, + KHeapSize, + &Thread2Semaphore + ); + test(r == KErrNone); + + // Set up notification of threads' death + TRequestStatus status, status2; + thread.Logon(status); + thread2.Logon(status2); + + // Run first thread + thread.Resume(); + + // Wait until just before it's closed the library handle + FinishedOpSemaphore.Wait(); + + // Run second thread + thread2.Resume(); + + // Wait until just before it's closed the library handle + FinishedOpSemaphore.Wait(); + + // Tell first thread we're ready to go + TInt signals = TheThreadArray[count].iMappedSignals + + TheThreadArray[count].iLeaveSignals + + ((TheThreadArray[count].iExitType == EExitPanic) ? 1 : 2); + Thread1Semaphore.Signal(signals); + + // Eat up 'FinishedOp' signals + while(--signals>0) + FinishedOpSemaphore.Wait(); + + // Wait for it to finish + User::WaitForRequest(status); + + // Check the exit type & category of the first thread + test(thread.ExitType() == TheThreadArray[count].iExitType); + + // Check category & reason of the first thread, if appropriate + if (thread.ExitType() == EExitPanic) + { + test(thread.ExitCategory() == KTUnmapPanic); + test(thread.ExitReason() == EPanickingThread); + } + + // Tell second thread we're ready to go + signals = TheThreadArray[count2].iMappedSignals + + TheThreadArray[count2].iLeaveSignals + + ((TheThreadArray[count2].iExitType == EExitPanic) ? 1 : 2); + Thread2Semaphore.Signal(signals); + + // Eat up 'FinishedOp' signals + while(--signals>0) + FinishedOpSemaphore.Wait(); + + // Wait for it to finish + User::WaitForRequest(status2); + + // Check the exit type & category of the second thread + test(thread2.ExitType() == TheThreadArray[count2].iExitType); + + // Check category & reason of the second thread, if appropriate + if (thread2.ExitType() == EExitPanic) + { + test(thread2.ExitCategory() == KTUnmapPanic); + test(thread2.ExitReason() == EPanickingThread); + } + + // Close thread handles + CLOSE_AND_WAIT(thread); + CLOSE_AND_WAIT(thread2); + } + } + + // Test two processes at once to deal with race conditions + test.Printf(_L("Create two processes at once to map the same library\n")); + RSemaphore procSem1, procSem2; + test(procSem1.CreateGlobal(KNullDesC, 0)==KErrNone); + test(procSem2.CreateGlobal(KNullDesC, 0)==KErrNone); + RProcess proc1, proc2; + test(proc1.Create(_L("T_UNMAP2"), KNullDesC)==KErrNone); + test(proc2.Create(_L("T_UNMAP2"), KNullDesC)==KErrNone); + test(proc1.SetParameter(1, procSem1)==KErrNone); + test(proc1.SetParameter(2, procSem2)==KErrNone); + test(proc2.SetParameter(1, procSem2)==KErrNone); + test(proc2.SetParameter(2, procSem1)==KErrNone); + TRequestStatus proc1stat, proc2stat; + proc1.Logon(proc1stat); + proc2.Logon(proc2stat); + test.Printf(_L("Start processes\n")); + proc1.Resume(); + proc2.Resume(); + test.Printf(_L("Wait for them to exit\n")); + User::WaitForRequest(proc1stat); + test(proc1.ExitType() == EExitKill); + test(proc1.ExitReason() == KErrNone); + User::WaitForRequest(proc2stat); + test(proc2.ExitType() == EExitKill); + test(proc2.ExitReason()==KErrNone); + CLOSE_AND_WAIT(proc1); + CLOSE_AND_WAIT(proc2); + procSem1.Close(); + procSem2.Close(); + + // Test load while unload + TestLoadWhileUnload(); + + // Restore JIT setting + User::SetJustInTime(jit); + + // Close synchronisation semaphores + Thread1Semaphore.Close(); + Thread2Semaphore.Close(); + FinishedOpSemaphore.Close(); + + __KHEAP_MARKEND; + } + +// Test loading a library while another thread is unloading it in an unwind +void TestLoadWhileUnload() + { + // Set up descriptor for thread's name + TBuf16<7> name(KThread); + name.Append('H'); + + // Create thread + RThread thread; + TInt r = thread.Create( + name, + ThreadH, + KDefaultStackSize, + KHeapSize, + KHeapSize, + &Thread1Semaphore + ); + test(r == KErrNone); + + // Set up notification of thread's death + TRequestStatus status; + thread.Logon(status); + + // Run thread + thread.Resume(); + + // Wait until it has an open handle to the library + FinishedOpSemaphore.Wait(); + + // Tell it to go ahead and leave + Thread1Semaphore.Signal(); + + // Wait for it to start unwinding + FinishedOpSemaphore.Wait(); + + // Tell it to go ahead and close the library handle + Thread1Semaphore.Signal(); + + // Wait for it to have closed the library + FinishedOpSemaphore.Wait(); + + // Load library + RLibrary library; + r = library.Load(KLeavingDll); + test(r == KErrNone); + + // Remember the address of the NOP function + NopThreadInfo.iFunc = library.Lookup(2); +#ifdef __WINS__ + NopThreadInfo.iOriginalContents = *(TInt32*)NopThreadInfo.iFunc; +#endif + + User::After(100000); // let supervisor run + + // Check User::Leave() library unloading behaviour + for (TInt i = 0; i < 2; ++i) + { + // Create NOP thread to call NOP function to check whether DLL is still loaded + RThread nopThread; + r = nopThread.Create( + KNopThreadName, + NopThread, + KDefaultStackSize, + KHeapSize, + KHeapSize, + NULL + ); + test(r == KErrNone); + + // Set up notification of thread's death + TRequestStatus nopStatus; + nopThread.Logon(nopStatus); + + // Run thread + nopThread.Resume(); + + // Wait for it to die + User::WaitForRequest(nopStatus); + + // Check the exit info + test(nopThread.ExitType() == EExitKill); + test(nopThread.ExitReason() == KErrNone); + + // Close thread handle + CLOSE_AND_WAIT(nopThread); + + // Tell it we're ready to go + Thread1Semaphore.Signal(); + + // Wait for it to finish next step + if (i==0) + FinishedOpSemaphore.Wait(); + } + + // Wait for thread to exit + User::WaitForRequest(status); + + // Check the exit type & category + test(thread.ExitType() == EExitKill); + + // Close thread handle + CLOSE_AND_WAIT(thread); + + // Close our handle to the library + library.Close(); + + // Create NOP thread to call NOP function to check DLL is unloaded + RThread nopThread; + r = nopThread.Create( + KNopThreadName, + NopThread, + KDefaultStackSize, + KHeapSize, + KHeapSize, + NULL + ); + test(r == KErrNone); + + // Set up notification of thread's death + TRequestStatus nopStatus; + nopThread.Logon(nopStatus); + + // Run thread + nopThread.Resume(); + + // Wait for it to die + User::WaitForRequest(nopStatus); + + // Check the exit info + test(nopThread.ExitType() == EExitPanic); + test(nopThread.ExitCategory() == KUnhandledExcCategory); + test(nopThread.ExitReason() == KUnhandledExcReason); + + // Close thread handle + CLOSE_AND_WAIT(nopThread); + } + +// +// Cleanup operations +// + +void Checkpoint(TAny* aSemaphore) + { + if(aSemaphore) + { + FinishedOpSemaphore.Signal(); + static_cast(aSemaphore)->Wait(); + } + } + +void DieDieDie(TAny* aSemaphore) + { + // Check-point before panicking + Checkpoint(aSemaphore); + + User::Panic(KTUnmapPanic, EPanickingThread); + } + +void PauseLeaving(TAny* aSemaphore) + { + Checkpoint(aSemaphore); + } + +void TrapLeave(TAny* aSemaphore) + { + TRAP_IGNORE( + { + CleanupStack::PushL(TCleanupItem(&PauseLeaving, aSemaphore)); + + Checkpoint(aSemaphore); + + User::Leave(KErrGeneral); + + CleanupStack::Pop(); // pause op + } + ); + + Checkpoint(aSemaphore); + } + +void TrapLeaveAndDie(TAny* aSemaphore) + { + TRAP_IGNORE( + { + CleanupStack::PushL(TCleanupItem(&DieDieDie, aSemaphore)); + + Checkpoint(aSemaphore); + + User::Leave(KErrGeneral); + + CleanupStack::Pop(); //DieDieDie op + } + ); + } + +void TrapLeaveAndClose_ThreadE(TAny* aSemaphore) + { + CleanupStack::Pop(&ThreadELibraryHandle); + + TRAP_IGNORE( + { + CleanupStack::PushL(TCleanupItem(&PauseLeaving, aSemaphore)); + + CleanupClosePushL(ThreadELibraryHandle); + + CleanupStack::PushL(TCleanupItem(&PauseLeaving, aSemaphore)); + + Checkpoint(aSemaphore); + + User::Leave(KErrGeneral); + + CleanupStack::Pop(); //pre-close pause op + + CleanupStack::Pop(&ThreadELibraryHandle); + + CleanupStack::Pop(); //post-close pause op + } + ); + + Checkpoint(aSemaphore); + } + +void TrapLeaveCloseAndDie_ThreadF(TAny* aSemaphore) + { + CleanupStack::Pop(&ThreadFLibraryHandle); + + TRAP_IGNORE( + { + CleanupStack::PushL(TCleanupItem(&DieDieDie, aSemaphore)); + + CleanupClosePushL(ThreadFLibraryHandle); + + CleanupStack::PushL(TCleanupItem(&PauseLeaving, aSemaphore)); + + Checkpoint(aSemaphore); + + User::Leave(KErrGeneral); + + CleanupStack::Pop(); //pre-close pause op + + CleanupStack::Pop(&ThreadFLibraryHandle); + + CleanupStack::Pop(); //DieDieDie op + } + ); + } + + +/** +Here's a list of interesting things that could happen to a thread which +has an open handle to library on cleanup stack: + +a) Panicks +b) Closes handle normally +c) Leaves and panicks before closing handle +d) Recursively leaves and panicks before closing handle +e) Recursively leaves and closes handle in recursive leave +f) Recursively leaves and panicks in recursive leave, after closing handle +g) Recursively leaves and returns to first leave without closing handle; first leave closes handle +h) Leaves and closes handle +i) Leaves and closes handle, then recursively leaves +j) Leaves and closes handle, then recursively leaves and panicks in recursive leave +k) Leaves and panicks after closing handle, but before leave completes + +Other ideas yet to be done: + +l) TRAPs a leave, then closes handle +m) TRAPs a recusive leave, then closes handle + +The thread functions below correspond to these. + +These are the ways a library's code seg can be held open by a process: + +a) Open handle to the library +b) Open reference to code seg because the last reference to the library was closed during a leave and + the process has not gone leave-idle + +We then test both these by testing at extra points during the sequences above that +the code segments are either mapped or unmapped, as appropriate. +*/ + +TInt ThreadA(TAny* aSemaphore) + { + __UHEAP_MARK; + if (CheckKernelHeap) + { + __KHEAP_MARK; + } + + new (&testThreadA) RTest(KNullDesC); + testThreadA.Start(KNullDesC); + // + CTrapCleanup* cleanup = CTrapCleanup::New(); + TInt r = KErrNoMemory; + if (cleanup) + { + TRAP(r, DoThreadAL(aSemaphore)); + + // Check-point after closing the library handle + Checkpoint(aSemaphore); + + testThreadA.Printf(_L("A: Returned %d, expected %d\n"), r, KErrNone); + testThreadA(r == KErrNone); + } + + delete cleanup; + // + testThreadA.End(); + testThreadA.Close(); + + if (CheckKernelHeap) + { + User::After(100000); // let supervisor run + __KHEAP_MARKEND; + } + __UHEAP_MARKEND; + + return r; + } + +TInt DoThreadAL(TAny* aSemaphore) + { + testThreadA.Printf(_L("A: Loading DLL.\n")); + User::LeaveIfError(ThreadALibraryHandle.Load(KLeavingDll)); + + testThreadA.Printf(_L("A: Pushing cleanup item to kill this thread!\n")); + CleanupStack::PushL(TCleanupItem(&DieDieDie, aSemaphore)); + + testThreadA.Printf(_L("A: Cleaning up Panic operation.\n")); + CleanupStack::PopAndDestroy(); // DieDieDie op + + ThreadALibraryHandle.Close(); + + return KErrNone; + } + +TInt ThreadB(TAny* aSemaphore) + { + __UHEAP_MARK; + if (CheckKernelHeap) + { + __KHEAP_MARK; + } + + new (&testThreadB) RTest(KNullDesC); + testThreadB.Start(KNullDesC); + // + CTrapCleanup* cleanup = CTrapCleanup::New(); + TInt r = KErrNoMemory; + if (cleanup) + { + TRAP(r, DoThreadBL(aSemaphore)); + + // Check-point after closing the library handle + Checkpoint(aSemaphore); + + testThreadB.Printf(_L("B: Returned %d, expected %d\n"), r, KErrNone); + testThreadB(r == KErrNone); + } + + delete cleanup; + // + testThreadB.End(); + testThreadB.Close(); + + if (CheckKernelHeap) + { + User::After(100000); // let supervisor run + __KHEAP_MARKEND; + } + __UHEAP_MARKEND; + + return r; + } + +TInt DoThreadBL(TAny* aSemaphore) + { + testThreadB.Printf(_L("B: Loading DLL.\n")); + User::LeaveIfError(ThreadBLibraryHandle.Load(KLeavingDll)); + + testThreadB.Printf(_L("B: Pushing handle to dynamically loaded DLL onto the cleanup stack.\n")); + CleanupClosePushL(ThreadBLibraryHandle); + + // Check-point whilst holding the open library handle + Checkpoint(aSemaphore); + + testThreadB.Printf(_L("B: Cleaning up DLL handle.\n")); + CleanupStack::PopAndDestroy(&ThreadBLibraryHandle); + + return KErrNone; + } + +TInt ThreadC(TAny* aSemaphore) + { + __UHEAP_MARK; + if (CheckKernelHeap) + { + __KHEAP_MARK; + } + + new (&testThreadC) RTest(KNullDesC); + testThreadC.Start(KNullDesC); + // + CTrapCleanup* cleanup = CTrapCleanup::New(); + TInt r = KErrNoMemory; + if (cleanup) + { + TRAP(r, DoThreadCL(aSemaphore)); + + // Check-point after closing the library handle + Checkpoint(aSemaphore); + + testThreadC.Printf(_L("C: Returned %d, expected %d\n"), r, KErrGeneral); + testThreadC(r == KErrGeneral); + + r = KErrNone; + } + + delete cleanup; + // + testThreadC.End(); + testThreadC.Close(); + + if (CheckKernelHeap) + { + User::After(100000); // let supervisor run + __KHEAP_MARKEND; + } + __UHEAP_MARKEND; + + return r; + } + +TInt DoThreadCL(TAny* aSemaphore) + { + testThreadC.Printf(_L("C: Loading DLL.\n")); + User::LeaveIfError(ThreadCLibraryHandle.Load(KLeavingDll)); + + testThreadC.Printf(_L("C: Pushing handle to dynamically loaded DLL onto the cleanup stack.\n")); + CleanupClosePushL(ThreadCLibraryHandle); + + testThreadC.Printf(_L("C: Pushing cleanup item to kill this thread before closing handle!\n")); + CleanupStack::PushL(TCleanupItem(&DieDieDie, aSemaphore)); + + testThreadC.Printf(_L("C: Looking up leaving function.\n")); + TLibraryFunction leaving = ThreadCLibraryHandle.Lookup(1); + User::LeaveIfNull((TAny*)leaving); + + // Check-point whilst holding the open library handle + Checkpoint(aSemaphore); + + testThreadC.Printf(_L("C: Calling leaving function.\n")); + (*leaving)(); + + testThreadC.Printf(_L("C: Cleaning up Panic operation.\n")); + CleanupStack::Pop(aSemaphore); // DieDieDie op + + testThreadC.Printf(_L("C: Cleaning up DLL handle.\n")); + CleanupStack::PopAndDestroy(&ThreadCLibraryHandle); + + return KErrNone; + } + +TInt ThreadD(TAny* aSemaphore) + { + __UHEAP_MARK; + if (CheckKernelHeap) + { + __KHEAP_MARK; + } + + new (&testThreadD) RTest(KNullDesC); + testThreadD.Start(KNullDesC); + // + CTrapCleanup* cleanup = CTrapCleanup::New(); + TInt r = KErrNoMemory; + if (cleanup) + { + TRAP(r, DoThreadDL(aSemaphore)); + + // Check-point after closing the library handle + Checkpoint(aSemaphore); + + testThreadD.Printf(_L("D: Returned %d, expected %d\n"), r, KErrGeneral); + testThreadD(r == KErrGeneral); + + r = KErrNone; + } + + delete cleanup; + // + testThreadD.End(); + testThreadD.Close(); + + if (CheckKernelHeap) + { + User::After(100000); // let supervisor run + __KHEAP_MARKEND; + } + __UHEAP_MARKEND; + + return r; + } + +TInt DoThreadDL(TAny* aSemaphore) + { + testThreadD.Printf(_L("D: Loading DLL.\n")); + User::LeaveIfError(ThreadDLibraryHandle.Load(KLeavingDll)); + + testThreadD.Printf(_L("D: Pushing handle to dynamically loaded DLL onto the cleanup stack.\n")); + CleanupClosePushL(ThreadDLibraryHandle); + + testThreadD.Printf(_L("D: Pushing cleanup item to recursively leave and then kill this thread before closing handle!\n")); + CleanupStack::PushL(TCleanupItem(&TrapLeaveAndDie, aSemaphore)); + + testThreadD.Printf(_L("D: Looking up leaving function.\n")); + TLibraryFunction leaving = ThreadDLibraryHandle.Lookup(1); + User::LeaveIfNull((TAny*)leaving); + + // Check-point whilst holding the open library handle + Checkpoint(aSemaphore); + + testThreadD.Printf(_L("D: Calling leaving function.\n")); + (*leaving)(); + + testThreadD.Printf(_L("D: Cleaning up DLL handle.\n")); + CleanupStack::PopAndDestroy(&ThreadDLibraryHandle); + + testThreadD.Printf(_L("D: Cleaning up recursive leave operation.\n")); + CleanupStack::Pop(aSemaphore); // recursive leave op + + return KErrNone; + } + +TInt ThreadE(TAny* aSemaphore) + { + __UHEAP_MARK; + if (CheckKernelHeap) + { + __KHEAP_MARK; + } + + new (&testThreadE) RTest(KNullDesC); + testThreadE.Start(KNullDesC); + // + CTrapCleanup* cleanup = CTrapCleanup::New(); + TInt r = KErrNoMemory; + if (cleanup) + { + TRAP(r, DoThreadEL(aSemaphore)); + + // Check-point after closing the library handle + Checkpoint(aSemaphore); + + testThreadE.Printf(_L("E: Returned %d, expected %d\n"), r, KErrGeneral); + testThreadE(r == KErrGeneral); + + r = KErrNone; + } + + delete cleanup; + // + testThreadE.End(); + testThreadE.Close(); + + if (CheckKernelHeap) + { + User::After(100000); // let supervisor run + __KHEAP_MARKEND; + } + __UHEAP_MARKEND; + + return r; + } + +TInt DoThreadEL(TAny* aSemaphore) + { + testThreadE.Printf(_L("E: Loading DLL.\n")); + User::LeaveIfError(ThreadELibraryHandle.Load(KLeavingDll)); + + testThreadE.Printf(_L("E: Pushing handle to dynamically loaded DLL onto the cleanup stack.\n")); + CleanupClosePushL(ThreadELibraryHandle); + + testThreadE.Printf(_L("E: Pushing cleanup item to recursively leave and then close the handle in the recursive leave\n")); + CleanupStack::PushL(TCleanupItem(&TrapLeaveAndClose_ThreadE, aSemaphore)); + + testThreadE.Printf(_L("E: Looking up leaving function.\n")); + TLibraryFunction leaving = ThreadELibraryHandle.Lookup(1); + User::LeaveIfNull((TAny*)leaving); + + // Check-point whilst holding the open library handle + Checkpoint(aSemaphore); + + testThreadE.Printf(_L("E: Calling leaving function.\n")); + (*leaving)(); + + testThreadE.Printf(_L("E: Cleaning up recursive leave operation.\n")); + CleanupStack::Pop(aSemaphore); // recursive leave op + + // NB: library handle removed from cleanup stack + + return KErrNone; + } + +TInt ThreadF(TAny* aSemaphore) + { + __UHEAP_MARK; + if (CheckKernelHeap) + { + __KHEAP_MARK; + } + + new (&testThreadF) RTest(KNullDesC); + testThreadF.Start(KNullDesC); + // + CTrapCleanup* cleanup = CTrapCleanup::New(); + TInt r = KErrNoMemory; + if (cleanup) + { + TRAP(r, DoThreadFL(aSemaphore)); + + // Check-point after closing the library handle + Checkpoint(aSemaphore); + + testThreadF.Printf(_L("F: Returned %d, expected %d\n"), r, KErrGeneral); + testThreadF(r == KErrGeneral); + + r = KErrNone; + } + + delete cleanup; + // + testThreadF.End(); + testThreadF.Close(); + + if (CheckKernelHeap) + { + User::After(100000); // let supervisor run + __KHEAP_MARKEND; + } + __UHEAP_MARKEND; + + return r; + } + +TInt DoThreadFL(TAny* aSemaphore) + { + testThreadF.Printf(_L("F: Loading DLL.\n")); + User::LeaveIfError(ThreadFLibraryHandle.Load(KLeavingDll)); + + testThreadF.Printf(_L("F: Pushing handle to dynamically loaded DLL onto the cleanup stack.\n")); + CleanupClosePushL(ThreadFLibraryHandle); + + testThreadF.Printf(_L("F: Pushing cleanup item to recursively leave and then panic in recursive leave after closing the library handle\n")); + CleanupStack::PushL(TCleanupItem(&TrapLeaveCloseAndDie_ThreadF, aSemaphore)); + + testThreadF.Printf(_L("F: Looking up leaving function.\n")); + TLibraryFunction leaving = ThreadFLibraryHandle.Lookup(1); + User::LeaveIfNull((TAny*)leaving); + + // Check-point whilst holding the open library handle + Checkpoint(aSemaphore); + + testThreadF.Printf(_L("F: Calling leaving function.\n")); + (*leaving)(); + + testThreadF.Printf(_L("F: Cleaning up recursive leave operation.\n")); + CleanupStack::Pop(aSemaphore); // recursive leave op + + // NB: library handle removed from cleanup stack + + return KErrNone; + } + +TInt ThreadG(TAny* aSemaphore) + { + __UHEAP_MARK; + if (CheckKernelHeap) + { + __KHEAP_MARK; + } + + new (&testThreadG) RTest(KNullDesC); + testThreadG.Start(KNullDesC); + // + CTrapCleanup* cleanup = CTrapCleanup::New(); + TInt r = KErrNoMemory; + if (cleanup) + { + TRAP(r, DoThreadGL(aSemaphore)); + + // Check-point after closing the library handle + Checkpoint(aSemaphore); + + testThreadG.Printf(_L("G: Returned %d, expected %d\n"), r, KErrGeneral); + testThreadG(r == KErrGeneral); + + r = KErrNone; + } + + delete cleanup; + // + testThreadG.End(); + testThreadG.Close(); + + if (CheckKernelHeap) + { + User::After(100000); // let supervisor run + __KHEAP_MARKEND; + } + __UHEAP_MARKEND; + + return r; + } + +TInt DoThreadGL(TAny* aSemaphore) + { + testThreadG.Printf(_L("G: Loading DLL.\n")); + User::LeaveIfError(ThreadGLibraryHandle.Load(KLeavingDll)); + + testThreadG.Printf(_L("G: Pushing cleanup item to synchronise after closing library handle.\n")); + CleanupStack::PushL(TCleanupItem(&PauseLeaving, aSemaphore)); + + testThreadG.Printf(_L("G: Pushing handle to dynamically loaded DLL onto the cleanup stack.\n")); + CleanupClosePushL(ThreadGLibraryHandle); + + testThreadG.Printf(_L("G: Pushing cleanup item to recursively leave, doing nothing, before closing handle\n")); + CleanupStack::PushL(TCleanupItem(&TrapLeave, aSemaphore)); + + testThreadG.Printf(_L("G: Looking up leaving function.\n")); + TLibraryFunction leaving = ThreadGLibraryHandle.Lookup(1); + User::LeaveIfNull((TAny*)leaving); + + // Check-point whilst holding the open library handle + Checkpoint(aSemaphore); + + testThreadG.Printf(_L("G: Calling leaving function.\n")); + (*leaving)(); + + testThreadG.Printf(_L("G: Cleaning up recursive leave operation.\n")); + CleanupStack::Pop(aSemaphore); // trap leave op + + testThreadG.Printf(_L("G: Cleaning up DLL handle.\n")); + CleanupStack::PopAndDestroy(&ThreadGLibraryHandle); + + return KErrNone; + } + +TInt ThreadH(TAny* aSemaphore) + { + __UHEAP_MARK; + if (CheckKernelHeap) + { + __KHEAP_MARK; + } + + new (&testThreadH) RTest(KNullDesC); + testThreadH.Start(KNullDesC); + // + CTrapCleanup* cleanup = CTrapCleanup::New(); + TInt r = KErrNoMemory; + if (cleanup) + { + TRAP(r, DoThreadHL(aSemaphore)); + + // Check-point after closing the library handle + Checkpoint(aSemaphore); + + testThreadH.Printf(_L("H: Returned %d, expected %d\n"), r, KErrGeneral); + testThreadH(r == KErrGeneral); + + r = KErrNone; + } + + delete cleanup; + // + testThreadH.End(); + testThreadH.Close(); + + if (CheckKernelHeap) + { + User::After(100000); // let supervisor run + __KHEAP_MARKEND; + } + __UHEAP_MARKEND; + + return r; + } + +TInt DoThreadHL(TAny* aSemaphore) + { + testThreadH.Printf(_L("H: Loading DLL.\n")); + User::LeaveIfError(ThreadHLibraryHandle.Load(KLeavingDll)); + + testThreadH.Printf(_L("H: Pushing cleanup item to synchronise after closing library handle.\n")); + CleanupStack::PushL(TCleanupItem(&PauseLeaving, aSemaphore)); + + testThreadH.Printf(_L("H: Pushing handle to dynamically loaded DLL onto the cleanup stack.\n")); + CleanupClosePushL(ThreadHLibraryHandle); + + testThreadH.Printf(_L("H: Pushing cleanup item to synchronise during leave\n")); + CleanupStack::PushL(TCleanupItem(&PauseLeaving, aSemaphore)); + + testThreadH.Printf(_L("H: Looking up leaving function.\n")); + TLibraryFunction leaving = ThreadHLibraryHandle.Lookup(1); + User::LeaveIfNull((TAny*)leaving); + + // Check-point whilst holding the open library handle + Checkpoint(aSemaphore); + + testThreadH.Printf(_L("H: Calling leaving function.\n")); + (*leaving)(); + + testThreadH.Printf(_L("H: Cleaning up leave pausing operation.\n")); + CleanupStack::Pop(aSemaphore); // pause leave op + + testThreadH.Printf(_L("H: Cleaning up DLL handle.\n")); + CleanupStack::PopAndDestroy(&ThreadHLibraryHandle); + + return KErrNone; + } + +TInt ThreadI(TAny* aSemaphore) + { + __UHEAP_MARK; + if (CheckKernelHeap) + { + __KHEAP_MARK; + } + + new (&testThreadI) RTest(KNullDesC); + testThreadI.Start(KNullDesC); + // + CTrapCleanup* cleanup = CTrapCleanup::New(); + TInt r = KErrNoMemory; + if (cleanup) + { + TRAP(r, DoThreadIL(aSemaphore)); + + // Check-point after closing the library handle + Checkpoint(aSemaphore); + + testThreadI.Printf(_L("I: Returned %d, expected %d\n"), r, KErrGeneral); + testThreadI(r == KErrGeneral); + + r = KErrNone; + } + + delete cleanup; + // + testThreadI.End(); + testThreadI.Close(); + + if (CheckKernelHeap) + { + User::After(100000); // let supervisor run + __KHEAP_MARKEND; + } + __UHEAP_MARKEND; + + return r; + } + +TInt DoThreadIL(TAny* aSemaphore) + { + testThreadI.Printf(_L("I: Loading DLL.\n")); + User::LeaveIfError(ThreadILibraryHandle.Load(KLeavingDll)); + + testThreadI.Printf(_L("I: Pushing cleanup item to recursively leave, doing nothing, after closing handle\n")); + CleanupStack::PushL(TCleanupItem(&TrapLeave, aSemaphore)); + + testThreadI.Printf(_L("I: Pushing handle to dynamically loaded DLL onto the cleanup stack.\n")); + CleanupClosePushL(ThreadILibraryHandle); + + testThreadI.Printf(_L("I: Pushing cleanup item to synchronise during leave\n")); + CleanupStack::PushL(TCleanupItem(&PauseLeaving, aSemaphore)); + + testThreadI.Printf(_L("I: Looking up leaving function.\n")); + TLibraryFunction leaving = ThreadILibraryHandle.Lookup(1); + User::LeaveIfNull((TAny*)leaving); + + // Check-point whilst holding the open library handle + Checkpoint(aSemaphore); + + testThreadI.Printf(_L("I: Calling leaving function.\n")); + (*leaving)(); + + testThreadI.Printf(_L("I: Cleaning up leave pausing operation.\n")); + CleanupStack::Pop(aSemaphore); // pause leave op + + testThreadI.Printf(_L("I: Cleaning up DLL handle.\n")); + CleanupStack::PopAndDestroy(&ThreadILibraryHandle); + + testThreadI.Printf(_L("I: Cleaning up recursive leave operation.\n")); + CleanupStack::Pop(); // trap leave op + + return KErrNone; + } + +TInt ThreadJ(TAny* aSemaphore) + { + __UHEAP_MARK; + if (CheckKernelHeap) + { + __KHEAP_MARK; + } + + new (&testThreadJ) RTest(KNullDesC); + testThreadJ.Start(KNullDesC); + // + CTrapCleanup* cleanup = CTrapCleanup::New(); + TInt r = KErrNoMemory; + if (cleanup) + { + TRAP(r, DoThreadJL(aSemaphore)); + + // Check-point after closing the library handle + Checkpoint(aSemaphore); + + testThreadJ.Printf(_L("J: Returned %d, expected %d\n"), r, KErrGeneral); + testThreadJ(r == KErrGeneral); + + r = KErrNone; + } + + delete cleanup; + // + testThreadJ.End(); + testThreadJ.Close(); + + if (CheckKernelHeap) + { + User::After(100000); // let supervisor run + __KHEAP_MARKEND; + } + __UHEAP_MARKEND; + + return r; + } + +TInt DoThreadJL(TAny* aSemaphore) + { + testThreadJ.Printf(_L("J: Loading DLL.\n")); + User::LeaveIfError(ThreadJLibraryHandle.Load(KLeavingDll)); + + testThreadJ.Printf(_L("J: Pushing cleanup item to recursively leave and panic, after closing handle\n")); + CleanupStack::PushL(TCleanupItem(&TrapLeaveAndDie, aSemaphore)); + + testThreadJ.Printf(_L("J: Pushing handle to dynamically loaded DLL onto the cleanup stack.\n")); + CleanupClosePushL(ThreadJLibraryHandle); + + testThreadJ.Printf(_L("J: Pushing cleanup item to synchronise during leave\n")); + CleanupStack::PushL(TCleanupItem(&PauseLeaving, aSemaphore)); + + testThreadJ.Printf(_L("J: Looking up leaving function.\n")); + TLibraryFunction leaving = ThreadJLibraryHandle.Lookup(1); + User::LeaveIfNull((TAny*)leaving); + + // Check-point whilst holding the open library handle + Checkpoint(aSemaphore); + + testThreadJ.Printf(_L("J: Calling leaving function.\n")); + (*leaving)(); + + testThreadJ.Printf(_L("J: Cleaning up leave pausing operation.\n")); + CleanupStack::Pop(aSemaphore); // pause leave op + + testThreadJ.Printf(_L("J: Cleaning up DLL handle.\n")); + CleanupStack::PopAndDestroy(&ThreadJLibraryHandle); + + testThreadJ.Printf(_L("J: Cleaning up recursive leave operation.\n")); + CleanupStack::Pop(); // leave and die op + + return KErrNone; + } + +TInt ThreadK(TAny* aSemaphore) + { + __UHEAP_MARK; + if (CheckKernelHeap) + { + __KHEAP_MARK; + } + + new (&testThreadK) RTest(KNullDesC); + testThreadK.Start(KNullDesC); + // + CTrapCleanup* cleanup = CTrapCleanup::New(); + TInt r = KErrNoMemory; + if (cleanup) + { + TRAP(r, DoThreadKL(aSemaphore)); + + // Check-point after closing the library handle + Checkpoint(aSemaphore); + + testThreadK.Printf(_L("K: Returned %d, expected %d\n"), r, KErrGeneral); + testThreadK(r == KErrGeneral); + + r = KErrNone; + } + + delete cleanup; + // + testThreadK.End(); + testThreadK.Close(); + + if (CheckKernelHeap) + { + User::After(100000); // let supervisor run + __KHEAP_MARKEND; + } + __UHEAP_MARKEND; + + return r; + } + +TInt DoThreadKL(TAny* aSemaphore) + { + testThreadK.Printf(_L("K: Loading DLL.\n")); + User::LeaveIfError(ThreadKLibraryHandle.Load(KLeavingDll)); + + testThreadK.Printf(_L("K: Pushing cleanup item to panic, after closing handle\n")); + CleanupStack::PushL(TCleanupItem(&DieDieDie, aSemaphore)); + + testThreadK.Printf(_L("K: Pushing handle to dynamically loaded DLL onto the cleanup stack.\n")); + CleanupClosePushL(ThreadKLibraryHandle); + + testThreadK.Printf(_L("K: Pushing cleanup item to synchronise during leave\n")); + CleanupStack::PushL(TCleanupItem(&PauseLeaving, aSemaphore)); + + testThreadK.Printf(_L("K: Looking up leaving function.\n")); + TLibraryFunction leaving = ThreadKLibraryHandle.Lookup(1); + User::LeaveIfNull((TAny*)leaving); + + // Check-point whilst holding the open library handle + Checkpoint(aSemaphore); + + testThreadK.Printf(_L("K: Calling leaving function.\n")); + (*leaving)(); + + testThreadK.Printf(_L("K: Cleaning up leave pausing operation.\n")); + CleanupStack::Pop(aSemaphore); // pause leave op + + testThreadK.Printf(_L("K: Cleaning up DLL handle.\n")); + CleanupStack::PopAndDestroy(&ThreadKLibraryHandle); + + testThreadK.Printf(_L("K: Cleaning up panic operation.\n")); + CleanupStack::Pop(); // die op + + return KErrNone; + }