--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/active/t_schedrace.cpp Mon Sep 27 10:52:00 2010 +0100
@@ -0,0 +1,381 @@
+// Copyright (c) 2010 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\active\t_schedrace.cpp
+// Overview:
+// Test for race conditions in emulator scheduler
+// API Information:
+// RMessage2, RMessagePtr2, RSessionBase, CSession2, CServer2, CPeriodic
+// Details:
+// - The client and server shuttle messages back and forth with variable timing
+// - The client also has a periodic timer, which fires at a random phase relative to the IPC
+// - The client should have higher priority than the server
+// - The race may occur when the timer interrupt preempts kernel code and disables interrupts
+// - The "preempted" code nonetheless runs and sees the "impossible" disabled interrupts
+// - The result is a precondition failure at a random point
+// Platforms/Drives/Compatibility:
+// Will run on all platforms, but precondition failure is expected only on the UDEB emulator.
+// Assumptions/Requirement/Pre-requisites:
+// Failures and causes:
+// Base Port information:
+
+#define __E32TEST_EXTENSION__
+
+#include <e32std.h>
+#include <e32std_private.h>
+#include <e32math.h>
+#include <e32panic.h>
+#include <e32svr.h>
+#include <e32test.h>
+#include <e32ver.h>
+
+_LIT(KServerName, "CTestServer");
+const TVersion version(KE32MajorVersionNumber,
+ KE32MinorVersionNumber,
+ KE32BuildVersionNumber);
+
+// Globals for counting number of times each thread/object runs
+TInt nIdleCalls, nTimerCalls, nServerCalls;
+
+
+
+//
+// Server classes and code ...
+//
+class CTestServer : public CServer2
+ {
+public:
+ IMPORT_C CTestServer(RTest* aTest) : CServer2(EPriorityIdle), iTest(aTest) {};
+protected:
+ IMPORT_C CSession2* NewSessionL(const TVersion& aVersion, const RMessage2& aMessage) const;
+public:
+ RTest* iTest;
+ };
+
+class CTestSession : public CSession2
+ {
+public:
+ IMPORT_C void ServiceL(const RMessage2& aMessage);
+ enum Action
+ {
+ EStop = 0,
+ EDelay
+ };
+public:
+ RTest* iTest;
+ };
+
+//
+// CTestServer functions
+//
+EXPORT_C CSession2* CTestServer::NewSessionL(const TVersion& aVersion, const RMessage2& /*aMessage*/) const
+ {
+ if (User::QueryVersionSupported(version, aVersion) == EFalse)
+ User::Leave(KErrNotSupported);
+
+ CTestSession* newCTestSession = new CTestSession;
+ if (newCTestSession == NULL)
+ User::Leave(KErrNoMemory);
+ newCTestSession->iTest = iTest;
+
+ return newCTestSession;
+ }
+
+//
+// CTestSession functions
+//
+EXPORT_C void CTestSession::ServiceL(const RMessage2& aMessage)
+ {
+ if ((++nServerCalls & 0x03ff) == 10000) // never true, to suppress message
+ iTest->Printf(_L("S:calls: I=%-7d S=%-7d T=%-7d D=%-7d\n"),
+ nIdleCalls, nServerCalls, nTimerCalls, aMessage.Int0());
+ TInt r = KErrNone;
+ TInt i = 0;
+
+ switch (aMessage.Function())
+ {
+ case EStop:
+ CActiveScheduler::Stop();
+ break;
+
+ case EDelay:
+ // Spin a bit before replying
+ // The compiler can't optimise this away because 'i' is used below ...
+ for (i = 0; i < aMessage.Int0(); ++i)
+ ;
+ break;
+
+ default:
+ r = KErrNotSupported;
+ break;
+ }
+
+ if (i != -1) // always true :)
+ aMessage.Complete(r);
+ }
+
+//
+// Thread to run the server code
+//
+TInt ServerThread(TAny*)
+ {
+ RTest test(_L("T_SCHEDRACE server"));
+ test.Title();
+
+// UserSvr::FsRegisterThread();
+
+ test.Start(_L("Create and install ActiveScheduler"));
+ CActiveScheduler* pScheduler = new CActiveScheduler;
+ test_NotNull(pScheduler);
+ CActiveScheduler::Install(pScheduler);
+
+ test.Next(_L("Creating and starting Server"));
+ CTestServer* pServer = new CTestServer(&test);
+ test_NotNull(pServer);
+ test_KErrNone(pServer->Start(KServerName)); // Starting a CServer2 also Adds it to the ActiveScheduler
+
+ test.Next(_L("Rendezvous with main thread, then Start ActiveScheduler"));
+ RThread self;
+ self.Rendezvous(KErrNone);
+ test.Printf(_L(" There might be something going on beneath this window\n"));
+ CActiveScheduler::Start();
+
+ // This code is not reached until the active scheduler exits.
+ test.Next(_L("Destroy Server and ActiveScheduler"));
+ delete pServer;
+ delete pScheduler;
+ test.Close();
+ return (KErrNone);
+ }
+
+
+
+//
+// Client classes and code ...
+//
+class RTestSession : public RSessionBase
+ {
+ public:
+ RTestSession(RTest* aTest, TInt aTicks) : iTest(aTest), iMax(aTicks)
+ {
+ iDelay = iMax;
+ iDelta = -1;
+ };
+ TInt Connect(const TDesC& aServer, int aSlots)
+ {
+ return RSessionBase::CreateSession(aServer, version, aSlots);
+ };
+ TInt SendBlind(TInt aFunction, const TIpcArgs& aArgs) const
+ {
+ return RSessionBase::Send(aFunction, aArgs);
+ };
+ TInt SendSync(TInt aFunction, const TIpcArgs& aArgs) const
+ {
+ return RSessionBase::SendReceive(aFunction, aArgs);
+ };
+ void SendAsync(TInt aFunction, const TIpcArgs& aArgs, TRequestStatus& aStatus) const
+ {
+ RSessionBase::SendReceive(aFunction, aArgs, aStatus);
+ };
+ public:
+ RTest* iTest;
+ TInt iMax;
+ TInt iDelay;
+ TInt iDelta;
+ };
+
+RTestSession* TheSession;
+
+class CIdler : public CIdle
+ {
+ public:
+ CIdler() : CIdle(EPriorityIdle)
+ {
+ CActiveScheduler::Add(this);
+ };
+ void Callback();
+ };
+
+void CIdler::Callback()
+ {
+ if ((++nIdleCalls & 0x03ff) == 10000) // never true, to suppress message
+ TheSession->iTest->Printf(_L("I:calls: I=%-7d S=%-7d T=%-7d D=%-7d\n"),
+ nIdleCalls, nServerCalls, nTimerCalls, TheSession->iDelay);
+ // TInt delay = Math::Random() & 0xffff; // up to ~64ms
+ TheSession->SendBlind(CTestSession::EDelay, TIpcArgs(TheSession->iDelay));
+ TheSession->SendAsync(CTestSession::EDelay, TIpcArgs(0), iStatus);
+ SetActive();
+ }
+
+//
+// CIdler callback wrapper
+//
+TInt IdleCallback(TAny* aPtr)
+ {
+ ((CIdler*)aPtr)->Callback();
+ return EFalse;
+ }
+
+//
+// CPeriodic callback
+//
+TInt TimerCallback(TAny*)
+ {
+ if ((++nTimerCalls & 0x003f) == 1) // true in one out of 64 cycles
+ TheSession->iTest->Printf(_L("T:calls: I=%-7d S=%-7d T=%-7d D=%-7d\n"),
+ nIdleCalls, nServerCalls, nTimerCalls, TheSession->iDelay);
+ if (TheSession->iDelay >= TheSession->iMax)
+ TheSession->iDelta = -1;
+// if (TheSession->iDelay < 1)
+// TheSession->iDelta = 1;
+ TheSession->iDelay += TheSession->iDelta;
+ if (TheSession->iDelay < 0)
+ CActiveScheduler::Stop();
+ return ETrue;
+ }
+
+//
+// Thread to run the client code
+//
+TInt ClientThread(TAny*)
+ {
+ RTest test(_L("T_SCHEDRACE client"));
+ test.Title();
+
+// UserSvr::FsRegisterThread();
+
+ test.Start(_L("Create Session"));
+ TheSession = new RTestSession(&test, 5*60*64); // will run for 5 minutes
+ test_NotNull(TheSession);
+ test_KErrNone(TheSession->Connect(KServerName, 2));
+
+ test.Start(_L("Create and install ActiveScheduler"));
+ CActiveScheduler* pScheduler = new CActiveScheduler;
+ test_NotNull(pScheduler);
+ CActiveScheduler::Install(pScheduler);
+
+ test.Next(_L("Create timer and idle task"));
+ CPeriodic* pTimer = CPeriodic::New(CActive::EPriorityStandard);
+ test_NotNull(pTimer);
+ CIdler* pIdler = new CIdler();
+ test_NotNull(pIdler);
+
+ test.Next(_L("Rendezvous with main thread"));
+ RThread self;
+ self.Rendezvous(KErrNone);
+
+ test.Next(_L("Start idle task, timer, and active scheduler"));
+ pIdler->Start(TCallBack(IdleCallback, pIdler));
+ pTimer->Start(1000000, 1000, TCallBack(TimerCallback, pTimer));
+ test.Printf(_L(" There might be something going on beneath this window\n"));
+ CActiveScheduler::Start();
+
+ // This code is not reached until the active scheduler exits.
+ TheSession->SendSync(CTestSession::EStop, TIpcArgs());
+ TheSession->Close();
+ test.Next(_L("Destroy Idle task, Timer, and ActiveScheduler"));
+ delete pIdler;
+ delete pTimer;
+ delete pScheduler;
+ test.Close();
+ return (KErrNone);
+ }
+
+
+//
+// Main program ...
+//
+GLDEF_C TInt E32Main()
+ {
+ RTest test(_L("Main T_SCHEDRACE test"));
+ test.Title();
+
+ test.Start(_L("Create server and client threads"));
+ const TInt KHeapMinSize = 0x1000;
+ const TInt KHeapMaxSize = 0x1000;
+ RThread serverThread, clientThread;
+ test_KErrNone(serverThread.Create(_L("Server Thread"), ServerThread, KDefaultStackSize, KHeapMinSize, KHeapMaxSize, NULL));
+ test_KErrNone(clientThread.Create(_L("Client Thread"), ClientThread, KDefaultStackSize, KHeapMinSize, KHeapMaxSize, NULL));
+
+ TRequestStatus serverStat, clientStat;
+ serverThread.Rendezvous(serverStat);
+ clientThread.Rendezvous(clientStat);
+ serverThread.SetPriority(EPriorityMuchLess);
+ clientThread.SetPriority(EPriorityMore);
+
+ test.Next(_L("Start the threads"));
+ serverThread.Resume();
+ User::WaitForRequest(serverStat);
+ test_KErrNone(serverStat.Int());
+ clientThread.Resume();
+ User::WaitForRequest(clientStat);
+ test_KErrNone(clientStat.Int());
+
+ test.Next(_L("Wait for the threads to stop"));
+ serverThread.Logon(serverStat);
+ clientThread.Logon(clientStat);
+ User::WaitForRequest(clientStat);
+ User::WaitForRequest(serverStat);
+
+ switch (clientThread.ExitType())
+ {
+ case EExitKill:
+ test.Printf(_L(" Client thread killed\n"));
+ break;
+
+ case EExitTerminate:
+ test.Printf(_L("!!Client thread terminated:"));
+ test.Panic(clientThread.ExitCategory(), clientThread.ExitReason());
+ break;
+
+ case EExitPanic:
+ test.Panic(_L("!!Client thread panicked:"));
+ test.Panic(clientThread.ExitCategory(), clientThread.ExitReason());
+ break;
+
+ default:
+ test.Panic(_L("!!Client thread did something bizarre"), clientThread.ExitReason());
+ break;
+ }
+
+ switch (serverThread.ExitType())
+ {
+ case EExitKill:
+ test.Printf(_L(" Server thread killed\n"));
+ break;
+
+ case EExitTerminate:
+ test.Printf(_L("!!Server thread terminated:"));
+ test.Panic(serverThread.ExitCategory(), serverThread.ExitReason());
+ break;
+
+ case EExitPanic:
+ //
+ // To catch a panic put a breakpoint in User::Panic() (in UCDT\UC_UNC.CPP).
+ //
+ test.Printf(_L("!!Server thread panicked:"));
+ test.Panic(serverThread.ExitCategory(), serverThread.ExitReason());
+ break;
+
+ default:
+ test.Panic(_L("!!Server thread did something bizarre"), serverThread.ExitReason());
+ break;
+ }
+
+ test.Next(_L("Close the threads"));
+ CLOSE_AND_WAIT(serverThread);
+ CLOSE_AND_WAIT(clientThread);
+ test.End();
+ return (KErrNone);
+ }
+