diff -r 000000000000 -r 96e5fb8b040d kerneltest/e32test/misc/t_svrstress.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/misc/t_svrstress.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,735 @@ +// Copyright (c) 2008-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\misc\t_svrstress.cpp +// This is a stress test for client server session connect and disconnect +// + +#include +#include +#define __E32TEST_EXTENSION__ +#include +#include +#include "u32std.h" +#include +#include +#include +#include + +RTest test(_L("T_SVRSTRESS")); + +RSemaphore SyncSemaphore; +TUint32 WaitABit; + +TInt NumMessageSlots; +TInt UseGlobalMessagePool; + +const TInt BigDesLength = 256 * 1024; + +#if 1 +#define TRACE(t) RDebug::RawPrint(_L8(t)) +#else +#define TRACE(t) +#endif + +// +// utility functions... +// + +void WaitForRequest() + { + User::WaitForAnyRequest(); + RThread().RequestSignal(); // put request semaphore count back + } + + +TInt WaitForRequest(TRequestStatus& aStatus,TTimeIntervalMicroSeconds32 aTimeout=2*1000000) + { + RTimer timer; + test_Equal(KErrNone,timer.CreateLocal()); + + TRequestStatus timeoutStatus; + timer.After(timeoutStatus,aTimeout); + + User::WaitForRequest(aStatus,timeoutStatus); + + TInt r; + if(aStatus.Int()==KRequestPending) + { + r = KErrTimedOut; + } + else + { + r = KErrNone; + timer.Cancel(); + User::WaitForRequest(timeoutStatus); + } + + CLOSE_AND_WAIT(timer); + + return r; + } + + +// +// CMyServer +// + +_LIT(KMyServerName,"StressSvr"); + +class CMyServer : public CServer2 + { +public: + CMyServer(TInt aPriority); + static CMyServer* New(TInt aPriority); + virtual CSession2* NewSessionL(const TVersion&, const RMessage2&) const; + }; + + +class CMySession : public CSession2 + { +public: + virtual void ServiceL(const RMessage2& aMessage); + }; + + +CMyServer* CMyServer::New(TInt aPriority) + { + return new CMyServer(aPriority); + } + + +CMyServer::CMyServer(TInt aPriority) + : CServer2(aPriority, ESharableSessions) + {} + + +CSession2* CMyServer::NewSessionL(const TVersion&, const RMessage2&) const + { + TRACE("O"); + return new(ELeave) CMySession; + } + + +TBool RestartServer; + +TInt MyServerThread(TAny*) + { + CActiveScheduler* pR=new CActiveScheduler; + if(!pR) + return KErrNoMemory; + CActiveScheduler::Install(pR); + RestartServer = ETrue; + + while(RestartServer) + { + __UHEAP_MARK; + CMyServer* pS=CMyServer::New(0); + if(!pS) + return KErrNoMemory; + TInt r = pS->Start(KMyServerName); + if(r!=KErrNone) + return r; + + TRACE("S"); + RThread::Rendezvous(KErrNone); + + CActiveScheduler::Start(); + + delete pS; + __UHEAP_MARKEND; + } + + delete pR; + return KErrNone; + } + + +// +// RMyServer +// + +class RMyServer : public RSessionBase + { +public: + enum TFunction + { + EStop, + ESync, + EPing, + EShutdown, + ECompleteWhileCopying + }; +public: + TInt Connect(); + + inline TInt Send(TFunction aFunction) const + { return SendReceive(aFunction); } + + inline TInt Send(TFunction aFunction, const TIpcArgs& aArgs) const + { return SendReceive(aFunction, aArgs); } + + inline void Send(TFunction aFunction, TRequestStatus& aStatus) const + { SendReceive(aFunction, aStatus); } + + inline void Send(TFunction aFunction, const TIpcArgs& aArgs, TRequestStatus& aStatus) const + { SendReceive(aFunction, aArgs, aStatus); } + }; + + +TInt RMyServer::Connect() + { + RMyServer temp; + TInt r = temp.CreateSession(KMyServerName, TVersion(), UseGlobalMessagePool ? -1 : NumMessageSlots); + if(r!=KErrNone) + return r; + + // turn handle into process owned... + RMyServer temp2(temp); + r = temp2.Duplicate(RThread()); + temp.Close(); + + *this = temp2; + return r; + } + + + +// +// CMySession +// + +TInt CopierThread(TAny* aPtr) + { + RMessage2& msg = *(RMessage2*)aPtr; + HBufC* bigdes = HBufC::NewMax(BigDesLength); + if (bigdes == NULL) + return KErrNoMemory; + TPtr ptr = bigdes->Des(); + RThread().Rendezvous(KErrNone); + RDebug::Print(_L("START\n")); + TInt r = msg.Read(2, ptr); + RDebug::Print(_L("DONE\n")); + delete bigdes; + return r; + } + +void CMySession::ServiceL(const RMessage2& aMessage) + { + RThread client; + RThread copier; + aMessage.Client(client); + TRequestStatus* s; + TRequestStatus* s2; + TRequestStatus logon, rendez; + TInt r; + s = (TRequestStatus*)aMessage.Ptr0(); + + switch(aMessage.Function()) + { + case RMyServer::EStop: + TRACE("E"); + CActiveScheduler::Stop(); + break; + + case RMyServer::ESync: + TRACE("Y"); + client.RequestComplete(s,KErrNone); // let client know we've received the message + SyncSemaphore.Wait(); // wait for signal from client + s = (TRequestStatus*)aMessage.Ptr1(); // use second status for later end signal + aMessage.Complete(KErrNone); // complete the message + break; + + case RMyServer::EPing: + TRACE("P"); + aMessage.Complete(KErrNone); + break; + + case RMyServer::EShutdown: + TRACE("D"); + RestartServer = EFalse; + CActiveScheduler::Stop(); + break; + + case RMyServer::ECompleteWhileCopying: + s2 = (TRequestStatus*)aMessage.Ptr1(); + r = copier.Create(_L("Copier"),CopierThread,KDefaultStackSize,&User::Allocator(),(TAny*)&aMessage); + if (r == KErrNone) + { + copier.Logon(logon); + copier.Rendezvous(rendez); + copier.SetPriority(EPriorityLess); + copier.Resume(); + User::WaitForRequest(rendez); + User::AfterHighRes(5000); // 5ms delay to let copy actually start + RDebug::Print(_L("COMPLETING\n")); + aMessage.Complete(KErrNone); + User::WaitForRequest(logon); + copier.Close(); + } + client.RequestComplete(s,r); + s = s2; + break; + + default: + TRACE("?"); + aMessage.Complete(KErrNotSupported); + break; + } + + // let client know we've completed the message... + TRACE("X"); + client.RequestComplete(s,KErrNone); + + client.Close(); + } + + + +// +// RStressThread +// + +class RStressThread + { +public: + RStressThread(TThreadFunction aThreadFunction, const char* aName, TInt aDelay=-1); + ~RStressThread(); + void Start(); + void Restart(); + void Stop(); + // for use by thread... + static RStressThread& Begin(TAny* aInfo); + TBool Loop(); +private: + TThreadFunction iThreadFunction; + const char* iName; + RThread iThread; + TRequestStatus iLogon; + TUint iCount; + TBool iStop; + TInt iDelay; + +private: + static TInt iInstanceCounter; + }; + + +TInt RStressThread::iInstanceCounter = 0; + + +RStressThread::RStressThread(TThreadFunction aThreadFunction, const char* aName, TInt aDelay) + : iThreadFunction(aThreadFunction), iName(aName), iLogon(KErrNone), iDelay(aDelay) + { + iThread.SetHandle(0); + } + + +RStressThread::~RStressThread() + { + Stop(); + } + + +void RStressThread::Start() + { + iStop = false; + iCount = 0; + + TBuf name; + name.Copy(TPtrC8((const TUint8*)iName)); + name.Append((TText)'-'); + name.AppendNum(iInstanceCounter++); + test_Equal(KErrNone,iThread.Create(name,iThreadFunction,KDefaultStackSize,&User::Allocator(),this)); + + iThread.Logon(iLogon); + test_Equal(KRequestPending,iLogon.Int()); + + TRequestStatus rendezvous; + iThread.Rendezvous(rendezvous); + + iThread.Resume(); + + User::WaitForRequest(rendezvous); + test_Equal(KErrNone,rendezvous.Int()); + } + + +void RStressThread::Stop() + { + if(!iThread.Handle()) + return; // thread not running + + iStop = true; + RDebug::Printf("RStressThread::Stop %s (count=%d)",iName,iCount); + if(WaitForRequest(iLogon,10*1000000)!=KErrNone) + test(0); + CLOSE_AND_WAIT(iThread); + } + + +void RStressThread::Restart() + { + if(iThread.Handle()) + { + if(iLogon==KRequestPending) + return; // thread still running + + User::WaitForRequest(iLogon); + CLOSE_AND_WAIT(iThread); + } + + Start(); + } + + +TBool RStressThread::Loop() + { + if(iDelay>=0) + User::AfterHighRes(iDelay); + ++iCount; + return !iStop; + } + + +RStressThread& RStressThread::Begin(TAny* aInfo) + { + RStressThread& t = *(RStressThread*)aInfo; + if(t.iDelay>=0) + RThread().SetPriority(EPriorityMore); // so this preempts threads after delay + RThread::Rendezvous(KErrNone); + return t; + } + +// +// +// + + +RMyServer Session; +RThread ServerThread; + + +void NewSession() + { + RMyServer newSession; + TRACE("o"); + test_Equal(KErrNone,newSession.Connect()); + + RMyServer oldSession(Session); + Session = newSession; + + TRACE("c"); + if(oldSession.Handle()) + CLOSE_AND_WAIT(oldSession); + } + + +TInt SessionCloserThread(TAny* aInfo) + { + RStressThread& t = RStressThread::Begin(aInfo); + do + { + NewSession(); + } + while(t.Loop()); + return KErrNone; + } + + +TInt ServerStopperThread(TAny* aInfo) + { + RStressThread& t = RStressThread::Begin(aInfo); + do + { + TRACE("s"); + TRequestStatus rendezvous; + ServerThread.Rendezvous(rendezvous); + + TRequestStatus s1 = KRequestPending; + TRequestStatus s2; + Session.Send(RMyServer::EStop,TIpcArgs(&s1),s2); + User::WaitForRequest(s1,s2); + if(s2!=KRequestPending) + { + test_Equal(KErrServerTerminated,s2.Int()); + User::WaitForRequest(s1); + } + + User::WaitForRequest(rendezvous); + NewSession(); + } + while(t.Loop()); + return KErrNone; + } + + +TInt SessionPingerThread(TAny* aInfo) + { + RStressThread& t = RStressThread::Begin(aInfo); + do + { + TRACE("p"); + TRequestStatus s1 = KRequestPending; + TRequestStatus s2; + Session.Send(RMyServer::EPing,TIpcArgs(&s1),s2); + User::WaitForRequest(s1,s2); + if(s2.Int()==KErrNone) + { + // message completed OK, wait for servers extra signal + User::WaitForRequest(s1); + } + else if(s2.Int()==KErrServerTerminated) + { + // server died before message processed, there shouldn't be an extra signal + test_Equal(KRequestPending,s1.Int()); + } + else + { + // assume message was completed by server, but we didn't get signalled because session was closed + test_Equal(KRequestPending,s2.Int()); + test_Equal(KErrNone,s1.Int()); + } + } + while(t.Loop()); + return KErrNone; + } + + +void TestInit() + { + RThread().SetPriority(EPriorityMuchMore); // so this main thread is higher priority than workers + + test_Equal(KErrNone,SyncSemaphore.CreateLocal(0,EOwnerProcess)); + + // calculate async cleanup timeout value... + TInt factor = UserSvr::HalFunction(EHalGroupVariant, EVariantHalTimeoutExpansion, 0, 0); + if (factor<=0) + factor = 1; + if (factor>1024) + factor = 1024; + WaitABit = 200000 * (TUint32)factor; + } + + +void StartServer() + { + // start test server... + test_Equal(KErrNone,ServerThread.Create(_L("Server"),MyServerThread,KDefaultStackSize,1<<12,1<<20,0)); + TRequestStatus rendezvous; + ServerThread.Rendezvous(rendezvous); + ServerThread.Resume(); + User::WaitForRequest(rendezvous); + test_Equal(KErrNone,rendezvous.Int()); + test_Equal(EExitPending,ServerThread.ExitType()); + } + + +void StopServer() + { + TRequestStatus logon; + NewSession(); + TRequestStatus s1 = KRequestPending; + TRequestStatus s2; + ServerThread.Logon(logon); + Session.Send(RMyServer::EShutdown,TIpcArgs(&s1),s2); + User::WaitForRequest(s1,s2); + if(s2!=KRequestPending) + { + test_Equal(KErrServerTerminated,s2.Int()); + User::WaitForRequest(s1); + } + CLOSE_AND_WAIT(Session); + User::WaitForRequest(logon); + test_KErrNone(logon.Int()); + test_Equal(EExitKill, ServerThread.ExitType()); + CLOSE_AND_WAIT(ServerThread); + } + + +void TestMessageCompleteOnClosedSession() + { + __KHEAP_MARK; + + test.Start(_L("Start server")); + StartServer(); + + test.Next(_L("Connect")); + test_Equal(KErrNone,Session.Connect()); + + test.Next(_L("Send message")); + TRequestStatus s1 = KRequestPending; + TRequestStatus s2 = KRequestPending; + TRequestStatus s3; + Session.Send(RMyServer::ESync,TIpcArgs(&s1,&s2),s3); + test_Equal(KRequestPending,s3.Int()); + + test.Next(_L("Wait for s1")); + test_Equal(KErrNone,WaitForRequest(s1)); + test_Equal(KErrNone,s1.Int()); + test_Equal(KRequestPending,s2.Int()); + test_Equal(KRequestPending,s3.Int()); + + test.Next(_L("Close session")); + Session.Close(); + test_Equal(KRequestPending,s2.Int()); + test_Equal(KRequestPending,s3.Int()); + + test.Next(_L("Trigger message completion")); + SyncSemaphore.Signal(); + + test.Next(_L("Wait for s2")); + test_Equal(KErrNone,WaitForRequest(s2)); + test_Equal(KErrNone,s2.Int()); + test_Equal(KRequestPending,s3.Int()); + + test.Next(_L("Stop server")); + StopServer(); + + test.End(); + + User::After(WaitABit); // allow asynchronous cleanup to happen + + __KHEAP_MARKEND; + } + + +void TestMessageCompleteWhileCopying() + { + __KHEAP_MARK; + + test.Start(_L("Start server")); + StartServer(); + + test.Next(_L("Connect")); + test_Equal(KErrNone,Session.Connect()); + + test.Next(_L("Create large descriptor")); + HBufC* bigdes = HBufC::NewMax(BigDesLength); + test_NotNull(bigdes); + TPtr ptr = bigdes->Des(); + + test.Next(_L("Send message")); + TRequestStatus s1 = KRequestPending; + TRequestStatus s2 = KRequestPending; + TRequestStatus s3; + Session.Send(RMyServer::ECompleteWhileCopying,TIpcArgs(&s1,&s2,&ptr),s3); + + test.Next(_L("Wait for s3")); + test_Equal(KErrNone,WaitForRequest(s3,10*1000000)); + test_Equal(KErrNone,s3.Int()); + + test.Next(_L("Wait for s2")); + test_Equal(KErrNone,WaitForRequest(s2,10*1000000)); + test_Equal(KErrNone,s2.Int()); + + test.Next(_L("Wait for s1")); + test_Equal(KErrNone,WaitForRequest(s1,10*1000000)); + test_Equal(KErrNone,s1.Int()); + + test.Next(_L("Close session")); + Session.Close(); + + test.Next(_L("Stop server")); + StopServer(); + + test.End(); + + User::After(WaitABit); // allow asynchronous cleanup to happen + + __KHEAP_MARKEND; + } + + +void RunStressThreads(RStressThread& aThread1, RStressThread& aThread2, TInt aTimeout=1000000) + { + __KHEAP_MARK; + + StartServer(); + + NewSession(); + + aThread1.Start(); + aThread2.Start(); + + RTimer timer; + test_Equal(KErrNone,timer.CreateLocal()); + TRequestStatus timeoutStatus; + timer.After(timeoutStatus,aTimeout); + do + { + aThread1.Restart(); + aThread2.Restart(); + WaitForRequest(); + } + while(timeoutStatus==KRequestPending); + User::WaitForRequest(timeoutStatus); + CLOSE_AND_WAIT(timer); + + aThread2.Stop(); + aThread1.Stop(); + + CLOSE_AND_WAIT(Session); + StopServer(); + + User::After(WaitABit); // allow asynchronous cleanup to happen + __KHEAP_MARKEND; + } + + +GLDEF_C TInt E32Main() + { + TInt i; + + test.Title(); + + test.Start(_L("Initialise")); + TestInit(); + + for(UseGlobalMessagePool=0; UseGlobalMessagePool<2; ++UseGlobalMessagePool) + { + if(UseGlobalMessagePool) + test.Next(_L("Tests using global message pool")); + else + test.Next(_L("Tests using local message pool")); + + NumMessageSlots = 1; + + test.Start(_L("Check completing messages on dead session")); + TestMessageCompleteOnClosedSession(); + + for (i=0; i<10; i++) + { + test.Next(_L("Check completing message while IPC copying")); + TestMessageCompleteWhileCopying(); + } + + test.Next(_L("Stress closing session whilst in use")); + RStressThread closer(SessionCloserThread,"SessionCloser",0); + RStressThread pinger1(SessionPingerThread,"Pinger"); + RunStressThreads(closer, pinger1); + + NumMessageSlots = 2; + + test.Next(_L("Stress stopping server whilst in use")); + RStressThread stopper(ServerStopperThread,"ServerStopper",0); + RStressThread pinger2(SessionPingerThread,"Pinger"); + RunStressThreads(stopper, pinger2); + + test.End(); + } + + test.End(); + return(0); + } +