diff -r 000000000000 -r a41df078684a kerneltest/e32test/system/t_condvar.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/system/t_condvar.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,820 @@ +// Copyright (c) 1994-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\system\t_condvar.cpp +// Overview: +// Test the use of the RCondVar & RMutex classes. +// API Information: +// RCondVar, RMutex +// Details: +// - Create some local conditional variables and mutexes and verify results +// are as expected. +// - Create a test thread that waits on conditional variables and mutexes, +// append some items on an array, signal the conditional variable and mutex, +// the thread then counts the number of items on the array and passes the +// result back to the main process. Verify results are as expected. Repeat +// with different array data. +// - Verify that a RCondVar::Wait() panics when the thread does not hold the +// specified mutex (mutex not locked). +// - Test using two mutexes with 1 conditional variable, append some items to +// an array, verify results from the thread are as expected. +// - Create a second thread with higher priority, perform tests similar to +// above, verify results are as expected. +// - Verify the thread timeout values are as expected. +// - Create global conditional variables and global mutexes, using two threads +// test the RCondVar::Signal() and RMutex::Wait() results are as expected. +// - Test various combinations of creating a thread, suspending and killing it +// and signalling a conditional variable and mutex. Verify results are as +// expected. +// - Create a secondary process along with a global chunk, conditional variable +// and mutex. Signal the conditional variable and verify the results are as +// expected. +// - Using two threads, benchmark the number of conditional variable/mutex Signal +// and Wait iterations that can be completed per second. +// Platforms/Drives/Compatibility: +// All. +// Assumptions/Requirement/Pre-requisites: +// Failures and causes: +// Base Port information: +// +// + +#include +#include +#include +#include +#include +#include +#include + +RTest test(_L("T_CONDVAR")); +RMutex M1; +RMutex M2; +RCondVar CV1; +RCondVar CV2; + +#define __TRACE_LINE__ test.Printf(_L("Line %d\n"),__LINE__) + +struct SThreadData + { + SThreadData(); + RMutex iM; + RCondVar iV; + RArray* iA; + TInt iTotal; + TInt iInnerLoops; + TInt iOuterLoops; + TInt iTimeoutMs; + TInt iTimeouts; + TInt iBadCount; + }; + +struct SThreadData2 + { + SThreadData2(); + const TText* iMutexName; + const TText* iCondVarName; + TInt iInnerLoops; + }; + +SThreadData::SThreadData() + { + memset(this, 0, sizeof(*this)); + } + +SThreadData2::SThreadData2() + { + memset(this, 0, sizeof(*this)); + } + +TInt Thread0(TAny*) + { + return CV1.Wait(M1); + } + +TInt Thread1(TAny* a) + { + TUint32 t1, t2; + SThreadData& d = *(SThreadData*)a; + TInt r = KErrNone; + TInt i = 0; + d.iM.Wait(); + FOREVER + { + while (d.iA->Count()<=i && r==KErrNone) + { + t1 = User::NTickCount(); + if (d.iTimeoutMs) + r = d.iV.TimedWait(d.iM, d.iTimeoutMs*1000); + else + r = d.iV.Wait(d.iM); + t2 = User::NTickCount(); + ++d.iInnerLoops; + if (r == KErrTimedOut) + { + ++d.iTimeouts; + TInt iv = (TInt)(t2-t1); + if (ivCount(); + for (; i=0) + { + test(aD.iA->Append(VA_ARG(list,TInt))==KErrNone); + } + aD.iV.Signal(); + aD.iM.Signal(); + } + +void AppendToArrayB(SThreadData& aD, TInt aCount, ...) + { + VA_LIST list; + VA_START(list,aCount); + aD.iM.Wait(); + while(--aCount>=0) + { + test(aD.iA->Append(VA_ARG(list,TInt))==KErrNone); + } + aD.iV.Broadcast(); + aD.iM.Signal(); + } + +void AppendToArrayB2(SThreadData& aD, TInt aCount, ...) + { + VA_LIST list; + VA_START(list,aCount); + aD.iM.Wait(); + while(--aCount>=0) + { + test(aD.iA->Append(VA_ARG(list,TInt))==KErrNone); + } + aD.iM.Signal(); + aD.iV.Broadcast(); + } + +void Thread2Test() + { + test.Next(_L("Thread2Test")); + RCondVar cv2; + RMutex m3; + TInt r = cv2.CreateLocal(); + test(r==KErrNone); + r = m3.CreateLocal(); + test(r==KErrNone); + SThreadData d1; + d1.iM = m3; + d1.iV = cv2; + RThread t1; + + CreateThread2(t1, d1, EPriorityLess); + cv2.Signal(); + m3.Signal(); + User::After(100000); + test(d1.iInnerLoops == 1); + KillThread2(t1); + + CreateThread2(t1, d1, EPriorityLess); + KillThread2(t1); + m3.Signal(); + test(d1.iInnerLoops == 1); + + CreateThread2(t1, d1, EPriorityLess); + m3.Signal(); + User::After(10000); + KillThread2(t1); + test(d1.iInnerLoops == 1); + + CreateThread2(t1, d1, EPriorityLess); + cv2.Signal(); + User::After(10000); + KillThread2(t1); + m3.Signal(); + test(d1.iInnerLoops == 1); + + CreateThread2(t1, d1, EPriorityLess); + t1.Suspend(); + KillThread2(t1); + m3.Signal(); + test(d1.iInnerLoops == 1); + + CreateThread2(t1, d1, EPriorityLess); + User::After(10000); + t1.Suspend(); + KillThread2(t1); + m3.Signal(); + test(d1.iInnerLoops == 1); + + CreateThread2(t1, d1, EPriorityLess); + cv2.Signal(); + t1.Suspend(); + KillThread2(t1); + m3.Signal(); + test(d1.iInnerLoops == 1); + + CreateThread2(t1, d1, EPriorityLess); + cv2.Signal(); + User::After(10000); + t1.Suspend(); + KillThread2(t1); + m3.Signal(); + test(d1.iInnerLoops == 1); + + cv2.Close(); + m3.Close(); + } + +const TText* KMutex1Name = _S("mtx1"); +const TText* KMutex2Name = _S("mtx2"); +const TText* KCondVar1Name = _S("cv1"); +const TText* KCondVar2Name = _S("cv2"); + +void TestGlobal() + { + test.Next(_L("Test Global")); + RMutex mg1, mg2; + RCondVar cvg1, cvg2; + TInt r = mg1.CreateGlobal(TPtrC(KMutex1Name)); + test(r==KErrNone); + r = mg2.CreateGlobal(TPtrC(KMutex2Name)); + test(r==KErrNone); + r = cvg1.CreateGlobal(TPtrC(KCondVar1Name)); + test(r==KErrNone); + r = cvg2.CreateGlobal(TPtrC(KCondVar2Name)); + test(r==KErrNone); + SThreadData2 d1, d2; + d1.iMutexName = KMutex1Name; + d1.iCondVarName = KCondVar1Name; + d2.iMutexName = KMutex2Name; + d2.iCondVarName = KCondVar2Name; + + RThread t1, t2; + r = t1.Create(KNullDesC, &Thread3, 0x1000, 0x1000, 0x1000, &d1); + test(r==KErrNone); + t1.SetPriority(EPriorityMore); + TRequestStatus s1; + t1.Logon(s1); + t1.Resume(); + r = t2.Create(KNullDesC, &Thread3, 0x1000, 0x1000, 0x1000, &d2); + test(r==KErrNone); + t2.SetPriority(EPriorityMore); + TRequestStatus s2; + t2.Logon(s2); + t2.Resume(); + + test(s1==KRequestPending); + test(s2==KRequestPending); + test(d1.iInnerLoops == 0); + test(d2.iInnerLoops == 0); + cvg1.Signal(); + test(d1.iInnerLoops == 1); + test(d2.iInnerLoops == 0); + cvg2.Signal(); + test(d1.iInnerLoops == 1); + test(d2.iInnerLoops == 1); + + cvg1.Close(); + cvg2.Close(); + test(s1==KRequestPending); + test(s2==KRequestPending); + test(d1.iInnerLoops == 1); + test(d2.iInnerLoops == 1); + + t1.Kill(0); + t2.Kill(0); + User::WaitForRequest(s1); + User::WaitForRequest(s2); + test(t1.ExitType()==EExitKill); + test(t1.ExitReason()==0); + test(t2.ExitType()==EExitKill); + test(t2.ExitReason()==0); + CLOSE_AND_WAIT(t1); + CLOSE_AND_WAIT(t2); + r = cvg1.OpenGlobal(TPtrC(KCondVar1Name)); + test(r==KErrNotFound); + test(cvg1.Handle()==0); + mg1.Close(); + mg2.Close(); + } + +void TestSecondaryProcess() + { + test.Next(_L("Test Secondary Process")); + + RProcess p; + RChunk c; + RMutex m; + RCondVar cv; + + //cancel lazy dll unloading + RLoader loader; + TInt r = loader.Connect(); + test(r==KErrNone); + r = loader.CancelLazyDllUnload(); + test(r==KErrNone); + loader.Close(); + + r = c.CreateGlobal(KNullDesC, 0x1000, 0x1000); + test(r==KErrNone); + volatile TInt& x = *(volatile TInt*)c.Base(); + x = 0; + r = m.CreateGlobal(KNullDesC); + test(r==KErrNone); + r = cv.CreateGlobal(KNullDesC); + test(r==KErrNone); + r = p.Create(RProcess().FileName(), KNullDesC); + test(r==KErrNone); + p.SetPriority(EPriorityHigh); + r = p.SetParameter(1, cv); + test(r==KErrNone); + r = p.SetParameter(2, m); + test(r==KErrNone); + r = p.SetParameter(3, c); + test(r==KErrNone); + TRequestStatus s; + p.Logon(s); + p.Resume(); + test(s==KRequestPending); + test(x==0); + TInt i; + for (i=0; i<10; ++i) + { + cv.Signal(); + test(x == i+1); + } + cv.Close(); + test(s==KRequestPending); + test(x==10); + p.Terminate(0); + User::WaitForRequest(s); + test(p.ExitType()==EExitTerminate); + test(p.ExitReason()==0); + CLOSE_AND_WAIT(p); + m.Close(); + c.Close(); + } + +TInt SecondaryProcess(RCondVar aCV) + { + RDebug::Print(_L("SecProc")); + RMutex mp; + RChunk cp; + TInt r = mp.Open(2); + if (r!=KErrNone) + return r; + r = cp.Open(3); + if (r!=KErrNone) + return r; + volatile TInt& x = *(volatile TInt*)cp.Base(); + mp.Wait(); + r = KErrNone; + while (r==KErrNone) + { + r = aCV.Wait(mp); + ++x; + RDebug::Print(_L("SecProc r=%d x=%d"), r, x); + } + return r; + } + +TInt E32Main() + { + __KHEAP_MARK; + __UHEAP_MARK; + + TInt r; + RCondVar cvp; + r = cvp.Open(1); + if (r==KErrNone) + return SecondaryProcess(cvp); + test.Title(); + test.Start(_L("Create condition variable")); + r = CV1.CreateLocal(); + test(r==KErrNone); + r = CV2.CreateLocal(); + test(r==KErrNone); + + test.Next(_L("Signal with no-one waiting")); + CV1.Signal(); + + test.Next(_L("Broadcast with no-one waiting")); + CV1.Broadcast(); + + test.Next(_L("Create mutexes")); + r = M1.CreateLocal(); + test(r==KErrNone); + r = M2.CreateLocal(); + test(r==KErrNone); + + RArray array; + SThreadData d0; + d0.iM = M2; + d0.iV = CV1; + d0.iA = &array; + test.Next(_L("Create thread to use mutex 2")); + RThread t0; + r = t0.Create(KNullDesC, &Thread1, 0x1000, 0x1000, 0x1000, &d0); + test(r==KErrNone); + t0.SetPriority(EPriorityMore); + TRequestStatus s0; + t0.Logon(s0); + t0.Resume(); + __TRACE_LINE__; + AppendToArray(d0, 1, 4); + test(d0.iTotal==4); + __TRACE_LINE__; + AppendToArray(d0, 2, -3, 17); + test(d0.iTotal==18); + t0.Terminate(11); + User::WaitForRequest(s0); + test(t0.ExitType()==EExitTerminate); + test(t0.ExitReason()==11); + CLOSE_AND_WAIT(t0); + array.Reset(); + + SThreadData d; + d.iM = M1; + d.iV = CV1; + d.iA = &array; + test.Next(_L("Create thread to use mutex 1")); + RThread t; + r = t.Create(KNullDesC, &Thread1, 0x1000, 0x1000, 0x1000, &d); + test(r==KErrNone); + t.SetPriority(EPriorityMore); + TRequestStatus s; + t.Logon(s); + t.Resume(); + + test.Next(_L("Test wait with mutex unlocked")); + r = t0.Create(KNullDesC, &Thread0, 0x1000, 0x1000, 0x1000, NULL); + test(r==KErrNone); + t0.SetPriority(EPriorityMore); + t0.Logon(s0); + TBool jit = User::JustInTime(); + User::SetJustInTime(EFalse); + t0.Resume(); + User::WaitForRequest(s0); + User::SetJustInTime(jit); + test(t0.ExitType()==EExitPanic); + test(t0.ExitCategory()==_L("KERN-EXEC")); + test(t0.ExitReason()==ECondVarWaitMutexNotLocked); + CLOSE_AND_WAIT(t0); + + test.Next(_L("Test trying to use two mutexes with 1 condition variable")); + M2.Wait(); + r = CV1.Wait(M2); + M2.Signal(); + test(r==KErrInUse); + + test(d.iTotal==0); + __TRACE_LINE__; + AppendToArray(d, 1, 3); + test(d.iTotal==3); + __TRACE_LINE__; + AppendToArray(d, 2, 3, 19); + test(d.iTotal==25); + __TRACE_LINE__; + AppendToArray(d, 4, 15, -1, -2, -30); + test(d.iTotal==7); + test(d.iInnerLoops==3); + test(d.iOuterLoops==3); + __TRACE_LINE__; + t.Suspend(); + __TRACE_LINE__; + t.Resume(); + test(d.iTotal==7); + test(d.iInnerLoops==4); + test(d.iOuterLoops==3); + __TRACE_LINE__; + t.SetPriority(EPriorityLess); + test(d.iTotal==7); + test(d.iInnerLoops==4); + test(d.iOuterLoops==3); + __TRACE_LINE__; + t.SetPriority(EPriorityMore); + test(d.iTotal==7); + test(d.iInnerLoops==5); + test(d.iOuterLoops==3); + __TRACE_LINE__; + t.Suspend(); + __TRACE_LINE__; + AppendToArray(d, 1, 4); + test(d.iTotal==7); + test(d.iInnerLoops==5); + test(d.iOuterLoops==3); + __TRACE_LINE__; + t.Resume(); + test(d.iTotal==11); + test(d.iInnerLoops==6); + test(d.iOuterLoops==4); + + SThreadData d2; + d2.iM = M1; + d2.iV = CV1; + d2.iA = &array; + + test.Next(_L("Create 2nd thread")); + RThread t2; + r = t2.Create(KNullDesC, &Thread1, 0x1000, NULL, &d2); + test(r==KErrNone); + t2.SetPriority(EPriorityMuchMore); + TRequestStatus s2; + t2.Logon(s2); + __TRACE_LINE__; + t2.Resume(); + + test(d2.iTotal == 11); + test(d2.iInnerLoops == 0); + test(d2.iOuterLoops == 1); + __TRACE_LINE__; + AppendToArray(d, 2, 9, 10); + test(d2.iTotal == 30); + test(d2.iInnerLoops == 1); + test(d2.iOuterLoops == 2); + test(d.iTotal==11); + test(d.iInnerLoops==6); + test(d.iOuterLoops==4); + __TRACE_LINE__; + AppendToArrayB(d, 2, 20, 30); + test(d2.iTotal == 80); + test(d2.iInnerLoops == 2); + test(d2.iOuterLoops == 3); + test(d.iTotal == 80); + test(d.iInnerLoops == 7); + test(d.iOuterLoops == 5); + __TRACE_LINE__; + AppendToArrayB2(d, 2, -10, -6); + test(d2.iTotal == 64); + test(d2.iInnerLoops == 3); + test(d2.iOuterLoops == 4); + test(d.iTotal == 64); + test(d.iInnerLoops == 8); + test(d.iOuterLoops == 6); + __TRACE_LINE__; + t2.Suspend(); + __TRACE_LINE__; + AppendToArray(d, 2, -8, -8); + test(d2.iTotal == 64); + test(d2.iInnerLoops == 3); + test(d2.iOuterLoops == 4); + test(d.iTotal == 48); + test(d.iInnerLoops == 9); + test(d.iOuterLoops == 7); + __TRACE_LINE__; + t2.Resume(); + test(d2.iTotal == 48); + test(d2.iInnerLoops == 4); + test(d2.iOuterLoops == 5); + test(d.iTotal == 48); + test(d.iInnerLoops == 9); + test(d.iOuterLoops == 7); + + // test timeouts + d.iTimeoutMs = 1000; + __TRACE_LINE__; + t.Suspend(); + __TRACE_LINE__; + t.Resume(); + test(d2.iTotal == 48); + test(d2.iInnerLoops == 4); + test(d2.iOuterLoops == 5); + test(d2.iTimeouts == 0); + test(d.iTotal == 48); + test(d.iInnerLoops == 10); + test(d.iOuterLoops == 7); + test(d.iTimeouts == 0); + test(array.Append(1)==0); + TInt nt = 0; + do { + if (d.iTimeouts > nt) + { + test(d.iTimeouts-nt == 1); + nt = d.iTimeouts; + test.Printf(_L("Timeout %d\n"), nt); + test(d2.iTotal == 48); + test(d2.iInnerLoops == 4); + test(d2.iOuterLoops == 5); + test(d2.iTimeouts == 0); + test(d.iTotal == 48+nt); + test(d.iInnerLoops == 10+nt); + test(d.iOuterLoops == 7+nt); + test(array.Append(1)==0); + } + } while (nt<10); + + d.iTimeoutMs = 0; + AppendToArrayB(d, 0); + test(d2.iTotal == 59); + test(d2.iInnerLoops == 5); + test(d2.iOuterLoops == 6); + test(d2.iTimeouts == 0); + test(d.iTotal == 59); + test(d.iInnerLoops == 21); + test(d.iOuterLoops == 18); + test(d.iTimeouts == 10); + + __TRACE_LINE__; + t.SetPriority(EPriorityLess); + __TRACE_LINE__; + AppendToArrayB(d, 1, 11); + test(d2.iTotal == 70); + test(d2.iInnerLoops == 6); + test(d2.iOuterLoops == 7); + test(d2.iTimeouts == 0); + test(d.iTotal == 59); + test(d.iInnerLoops == 21); + test(d.iOuterLoops == 18); + test(d.iTimeouts == 10); + User::After(50000); + test(d2.iTotal == 70); + test(d2.iInnerLoops == 6); + test(d2.iOuterLoops == 7); + test(d2.iTimeouts == 0); + test(d.iTotal == 70); + test(d.iInnerLoops == 22); + test(d.iOuterLoops == 19); + test(d.iTimeouts == 10); + + + + __TRACE_LINE__; + CV1.Close(); + User::WaitForRequest(s); + test(t.ExitType()==EExitKill); + test(t.ExitReason()==KErrGeneral); + User::WaitForRequest(s2); + test(t2.ExitType()==EExitKill); + test(t2.ExitReason()==KErrGeneral); + CLOSE_AND_WAIT(t); + CLOSE_AND_WAIT(t2); + + + M1.Close(); + + TestGlobal(); + + Thread2Test(); + + TestSecondaryProcess(); + + RunBench(); + M2.Close(); + CV2.Close(); + array.Close(); + + test.End(); + test.Close(); + + __UHEAP_MARKEND; + __KHEAP_MARKEND; + return KErrNone; + } +