--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/misc/test_thread.h Sat Feb 20 00:10:51 2010 +0200
@@ -0,0 +1,240 @@
+// Copyright (c) 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.
+//
+// Description: Some helper classes to assist with writing multi-threaded tests
+
+#ifndef TEST_THREAD_H
+#define TEST_THREAD_H
+
+#define __E32TEST_EXTENSION__
+#include <e32test.h>
+#include <e32svr.h>
+#include <e32des8.h>
+#include <e32des8_private.h>
+#include <e32cmn.h>
+#include <e32cmn_private.h>
+#include <e32std.h>
+#include <e32std_private.h>
+
+
+_LIT(KPciPanicCat, "test_thread.h");
+
+static const TInt KPciHeapSize=0x2000;
+
+enum TPciPanicCode
+ {
+ EThreadCreateFailed
+ };
+
+/**
+A utility class for running functions in other threads/processes
+*/
+class TTestRemote
+ {
+public:
+ virtual TInt WaitForExitL() = 0;
+ virtual ~TTestRemote()
+ {}
+
+ virtual void Rendezvous(TRequestStatus& aStatus) = 0;
+
+protected:
+ TTestRemote()
+ {}
+
+ static TInt RunFunctor(TAny* aFunctor)
+ {
+ TFunctor& functor = *(TFunctor*)aFunctor;
+ functor();
+ return KErrNone;
+ }
+
+ TRequestStatus iLogonStatus;
+ static TInt iCount;
+ };
+TInt TTestRemote::iCount=0;
+
+class TTestThread : public TTestRemote
+ {
+public:
+ TTestThread(const TDesC& aName, TThreadFunction aFn, TAny* aData, TBool aAutoResume=ETrue)
+ {
+ Init(aName, aFn, aData, aAutoResume);
+ }
+
+ /**
+ Run aFunctor in another thread
+ */
+ TTestThread(const TDesC& aName, TFunctor& aFunctor, TBool aAutoResume=ETrue)
+ {
+ Init(aName, RunFunctor, &aFunctor, aAutoResume);
+ }
+
+ ~TTestThread()
+ {
+ //RTest::CloseHandleAndWaitForDestruction(iThread);
+ iThread.Close();
+ }
+
+ void Resume()
+ {
+ iThread.Resume();
+ }
+
+ /**
+ If thread exited normally, return its return code
+ Otherwise, leave with exit reason
+ */
+ virtual TInt WaitForExitL()
+ {
+ User::WaitForRequest(iLogonStatus);
+ const TInt exitType = iThread.ExitType();
+ const TInt exitReason = iThread.ExitReason();
+
+ __ASSERT_ALWAYS(exitType != EExitPending, User::Panic(_L("TTestThread"),0));
+
+ if(exitType != EExitKill)
+ User::Leave(exitReason);
+
+ return exitReason;
+ }
+
+ virtual void Rendezvous(TRequestStatus& aStatus)
+ {
+ iThread.Rendezvous(aStatus);
+ }
+
+private:
+ void Init(const TDesC& aName, TThreadFunction aFn, TAny* aData, TBool aAutoResume)
+ {
+ TKName name(aName);
+ name.AppendFormat(_L("-%d"), iCount++);
+ TInt r=iThread.Create(name, aFn, KDefaultStackSize, KPciHeapSize, KPciHeapSize, aData);
+ if(r!=KErrNone)
+ {
+ RDebug::Printf("RThread::Create failed, code=%d", r);
+ User::Panic(KPciPanicCat, EThreadCreateFailed);
+ }
+
+ iThread.Logon(iLogonStatus);
+ __ASSERT_ALWAYS(iLogonStatus == KRequestPending, User::Panic(_L("TTestThread"),0));
+
+ if(aAutoResume)
+ iThread.Resume();
+ }
+
+ RThread iThread;
+ };
+
+class CTest : public CBase, public TFunctor
+ {
+public:
+ ~CTest()
+ {
+ iName.Close();
+ }
+
+ virtual void operator()()
+ {
+ RTest test(iName);
+ test.Start(iName);
+ for(TInt i=0; i<iIterations; i++)
+ {
+ test.Next(iName);
+ RunTest();
+ }
+ test.End();
+ }
+
+ virtual void RunTest() = 0;
+
+ virtual CTest* Clone() const = 0;
+
+ const TDesC& Name() const
+ {
+ return iName;
+ }
+
+protected:
+ CTest(const TDesC& aName, TInt aIterations)
+ :iIterations(aIterations)
+ {
+ iName.CreateL(aName);
+ }
+
+
+
+ CTest(const CTest& aOther)
+ :iIterations(aOther.iIterations)
+ {
+ iName.CreateL(aOther.iName);
+ }
+
+ //It would be useful to have an RTest member, but this can't be
+ //initialised untill the new thread is running as it will refer to
+ //the creating thread
+ RBuf iName;
+ const TInt iIterations;
+ };
+
+/**
+Make aNumberOfThreads copies of aTest and run
+each in its own thread
+
+@param test Reference to test object
+@param aTest Referance
+*/
+void MultipleTestRun(RTest& test, const CTest& aTest, TInt aNumberOfThreads)
+ {
+ RPointerArray<CTest> testArray;
+ RPointerArray<TTestThread> threadArray;
+
+ for(TInt i=0; i<aNumberOfThreads; i++)
+ {
+ test.Printf(_L("Create test thread"));
+ CTest* newTest = aTest.Clone();
+ test_NotNull(newTest);
+
+ TTestThread* thread = new TTestThread(aTest.Name(), *newTest);
+ test_NotNull(thread);
+
+ threadArray.AppendL(thread);
+ testArray.AppendL(newTest);
+ }
+
+ const TInt count = threadArray.Count();
+ for(TInt j=0; j<count; j++)
+ {
+ TTestThread* thread = threadArray[j];
+
+ TInt r = KErrNone;
+ TRAPD(leaveCode, r = thread->WaitForExitL());
+ if(leaveCode != KErrNone)
+ {
+ test.Printf(_L("Thread %d: Panic code:%d\n"), j, leaveCode);
+ test_KErrNone(leaveCode);
+ }
+
+ if(r!=KErrNone)
+ {
+ test.Printf(_L("Thread Number %d\n"), j);
+ test_KErrNone(r);
+ }
+ }
+
+ threadArray.ResetAndDestroy();
+ threadArray.Close();
+
+ testArray.ResetAndDestroy();
+ testArray.Close();
+ }
+
+#endif //TEST_THREAD_H
+