Fix for Bug 2984 - [GCCE] Illegal inline assembler in kernel/eka/debug/utrace/src/e32utrace.cpp
// 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 <e32std.h>
#include <e32std_private.h>
#include <e32svr.h>
#include <e32test.h>
#include <e32ldr.h>
#include <e32def.h>
#include <e32def_private.h>
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<TInt>* 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 (iv<d.iTimeoutMs)
++d.iBadCount;
r = KErrNone;
}
}
if (r != KErrNone)
break;
++d.iOuterLoops;
TInt c = d.iA->Count();
for (; i<c; ++i)
d.iTotal += (*d.iA)[i];
}
return r;
}
TInt Thread2(TAny* a)
{
TUint32 t1, t2;
SThreadData& d = *(SThreadData*)a;
TInt r = KErrNone;
d.iM.Wait();
RThread::Rendezvous(KErrNone);
while (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 (iv<d.iTimeoutMs)
++d.iBadCount;
r = KErrNone;
}
}
return r;
}
TInt Thread3(TAny* a)
{
SThreadData2& d = *(SThreadData2*)a;
RMutex m;
RCondVar cv;
TInt r = m.OpenGlobal(TPtrC(d.iMutexName), EOwnerThread);
if (r!=KErrNone)
return r;
r = cv.OpenGlobal(TPtrC(d.iCondVarName), EOwnerThread);
if (r!=KErrNone)
return r;
m.Wait();
while (r==KErrNone)
{
r = cv.Wait(m);
++d.iInnerLoops;
}
return r;
}
TInt Thread4(TAny* a)
{
volatile TInt& count = *(volatile TInt*)a;
TInt r = KErrNone;
M2.Wait();
while (r==KErrNone)
{
r = CV2.Wait(M2);
++count;
}
return r;
}
TInt Thread5(TAny*)
{
FOREVER
{
M2.Wait();
CV2.Signal();
M2.Signal();
}
}
void RunBench()
{
test.Next(_L("Benchmark"));
RThread t4, t5;
TInt count = 0;
TInt r = t4.Create(KNullDesC, &Thread4, 0x1000, 0x1000, 0x1000, &count);
test(r==KErrNone);
t4.SetPriority(EPriorityLess);
r = t5.Create(KNullDesC, &Thread5, 0x1000, 0x1000, 0x1000, NULL);
test(r==KErrNone);
t5.SetPriority(EPriorityMuchLess);
t4.Resume();
t5.Resume();
User::After(500000);
TInt initc = count;
User::After(5000000);
TInt finalc = count;
test.Printf(_L("%d iterations per second\n"), (finalc-initc)/5);
t4.Kill(0);
t5.Kill(0);
CLOSE_AND_WAIT(t4);
CLOSE_AND_WAIT(t5);
}
void CreateThread2(RThread& aThread, SThreadData& aData, TThreadPriority aPri)
{
TInt r = aThread.Create(KNullDesC, &Thread2, 0x1000, 0x1000, 0x1000, &aData);
test(r==KErrNone);
aThread.SetPriority(aPri);
TRequestStatus s;
aThread.Rendezvous(s);
test(s==KRequestPending);
aThread.Resume();
User::WaitForRequest(s);
test(s==KErrNone);
test(aThread.ExitType()==EExitPending);
aData.iM.Wait();
}
void KillThread2(RThread& aThread)
{
TRequestStatus s;
aThread.Logon(s);
test(s==KRequestPending);
aThread.Terminate(0);
User::WaitForRequest(s);
test(aThread.ExitType()==EExitTerminate);
test(aThread.ExitReason()==0);
test(s==0);
CLOSE_AND_WAIT(aThread);
}
void AppendToArray(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.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<TInt> 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;
}