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