kerneltest/e32test/misc/t_svrstress.cpp
changeset 9 96e5fb8b040d
--- /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 <e32base.h>
+#include <e32base_private.h>
+#define __E32TEST_EXTENSION__
+#include <e32test.h>
+#include <e32svr.h>
+#include "u32std.h"
+#include <e32atomics.h>
+#include <e32panic.h>
+#include <e32def.h>
+#include <e32def_private.h>
+
+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<KMaxKernelName> 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);
+	}
+