--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/prime/t_semutx.cpp Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,609 @@
+// Copyright (c) 1995-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\prime\t_semutx.cpp
+// Tests the RSemaphore, RMutex and RCriticalSection classes
+// Overview:
+// Tests the RSemaphore, RMutex and RCriticalSection classes
+// API Information:
+// RSemaphore, RMutex, RCriticalSection
+// Details:
+// - Test RSemaphore and RMutex with the producer/consumer scenario.
+// Create two threads, use signal and wait to coordinate the
+// threads. Verify results are as expected.
+// - Calculate the time required to create, resume and close a thread.
+// - Test RSemaphore::Wait(timeout) in a variety ways and timeout
+// values. Verify results are as expected.
+// - Test RMutex via two threads which write to an array. The writing
+// and updating of the index is wrapped within a mutex pair. Verify
+// results are as expected.
+// - Test RCriticalSection via two threads which write to an array. The
+// writing and updating of the index is wrapped within a critical section
+// pair. Verify results are as expected.
+// Platforms/Drives/Compatibility:
+// All.
+// Assumptions/Requirement/Pre-requisites:
+// Failures and causes:
+// Base Port information:
+//
+//
+
+#include <e32test.h>
+
+const TInt KMaxBufferSize=10;
+const TInt KMaxArraySize=10;
+const TInt KNumProducerItems=100;
+
+enum {EThread1ID=1,EThread2ID};
+
+RTest test(_L("T_SEMUTX"));
+RMutex mutex;
+RCriticalSection criticalSn;
+TInt thread1Count,thread2Count;
+TInt arrayIndex;
+TInt array[KMaxArraySize];
+TInt consumerArray[KNumProducerItems];
+RSemaphore slotAvailable,itemAvailable;
+
+class CStack
+ {
+public:
+ CStack() {iCount=0;};
+ void Push(TInt aItem) {iStack[iCount++]=aItem;};
+ TInt Pop(void) {return(iStack[--iCount]);};
+private:
+ TInt iStack[KMaxBufferSize];
+ TInt iCount;
+ };
+CStack stack;
+
+
+TInt Producer(TAny*)
+ {
+ for(TInt ii=0;ii<KNumProducerItems;ii++)
+ {
+ slotAvailable.Wait();
+ mutex.Wait();
+ stack.Push(ii);
+ mutex.Signal();
+ itemAvailable.Signal();
+ }
+ return(KErrNone);
+ }
+
+TInt Consumer(TAny*)
+ {
+ TInt item;
+ for(TInt ii=0;ii<KNumProducerItems;ii++)
+ {
+ itemAvailable.Wait();
+ mutex.Wait();
+ item=stack.Pop();
+ mutex.Signal();
+ slotAvailable.Signal();
+ consumerArray[item]=item;
+ }
+ return(KErrNone);
+ }
+
+void BusyWait(TInt aMicroseconds)
+ {
+ TTime begin;
+ begin.HomeTime();
+ FOREVER
+ {
+ TTime now;
+ now.HomeTime();
+ TTimeIntervalMicroSeconds iv=now.MicroSecondsFrom(begin);
+ if (iv.Int64()>=TInt64(aMicroseconds))
+ return;
+ }
+ }
+
+TInt MutexThreadEntryPoint1(TAny*)
+//
+// Mutex test thread 1
+//
+ {
+
+ thread1Count=0;
+ TBool running=ETrue;
+ do
+ {
+ mutex.Wait();
+ BusyWait(100000);
+ if (arrayIndex<KMaxArraySize)
+ {
+ array[arrayIndex++]=EThread1ID;
+ thread1Count++;
+ }
+ else
+ running=EFalse;
+ mutex.Signal();
+ } while (running);
+ return(KErrNone);
+ }
+
+TInt MutexThreadEntryPoint2(TAny*)
+//
+// Mutex test thread 2
+//
+ {
+
+ thread2Count=0;
+ TBool running=ETrue;
+ do
+ {
+ mutex.Wait();
+ BusyWait(200000);
+ if (arrayIndex<KMaxArraySize)
+ {
+ array[arrayIndex++]=EThread2ID;
+ thread2Count++;
+ }
+ else
+ running=EFalse;
+ mutex.Signal();
+ } while (running);
+ return(KErrNone);
+ }
+
+TInt CriticalSnThreadEntryPoint1(TAny*)
+//
+// Critical Section test thread 1
+//
+ {
+
+ thread1Count=0;
+ TBool running=ETrue;
+ do
+ {
+ criticalSn.Wait();
+ User::After(100000);
+ if (arrayIndex<KMaxArraySize)
+ {
+ array[arrayIndex++]=EThread1ID;
+ thread1Count++;
+ }
+ else
+ running=EFalse;
+ criticalSn.Signal();
+ } while (running);
+ return(KErrNone);
+ }
+
+TInt CriticalSnThreadEntryPoint2(TAny*)
+//
+// Critical Section test thread 2
+//
+ {
+
+ thread2Count=0;
+ TBool running=ETrue;
+ do
+ {
+ criticalSn.Wait();
+ User::After(200000);
+ if (arrayIndex<KMaxArraySize)
+ {
+ array[arrayIndex++]=EThread2ID;
+ thread2Count++;
+ }
+ else
+ running=EFalse;
+ criticalSn.Signal();
+ } while (running);
+ return(KErrNone);
+ }
+
+struct SWaitSem
+ {
+ RSemaphore iSem;
+ TInt iTimeout;
+ };
+
+TInt WaitSemThread(TAny* a)
+ {
+ SWaitSem& ws = *(SWaitSem*)a;
+ return ws.iSem.Wait(ws.iTimeout);
+ }
+
+void StartWaitSemThread(RThread& aT, SWaitSem& aW, TThreadPriority aP=EPriorityLess)
+ {
+ TInt r = aT.Create(KNullDesC, &WaitSemThread, 0x1000, 0x1000, 0x1000, &aW);
+ test(r==KErrNone);
+ aT.SetPriority(aP);
+ aT.Resume();
+ }
+
+void WaitForWaitSemThread(RThread& aT, TInt aResult)
+ {
+ TRequestStatus s;
+ aT.Logon(s);
+ User::WaitForRequest(s);
+ test(aT.ExitType()==EExitKill);
+ test(aT.ExitReason()==aResult);
+ test(s.Int()==aResult);
+ CLOSE_AND_WAIT(aT);
+ }
+
+TInt DummyThread(TAny*)
+ {
+ return 0;
+ }
+
+void TestSemaphore2()
+ {
+ test.Start(_L("Test semaphore wait with timeout"));
+ SWaitSem ws;
+ RThread t;
+ TTime initial;
+ TTime final;
+ TInt elapsed=0;
+ TInt r = ws.iSem.CreateLocal(0);
+ test(r==KErrNone);
+
+ RThread().SetPriority(EPriorityAbsoluteVeryLow);
+ TInt threadcount=0;
+ initial.HomeTime();
+ while (elapsed<1000000)
+ {
+ r = t.Create(KNullDesC, &DummyThread, 0x1000, NULL, NULL);
+ test(r==KErrNone);
+ t.SetPriority(EPriorityMore);
+ t.Resume();
+ t.Close();
+ ++threadcount;
+ final.HomeTime();
+ elapsed = I64INT(final.Int64()-initial.Int64());
+ }
+ RThread().SetPriority(EPriorityNormal);
+ test.Printf(_L("%d threads in 1 sec\n"),threadcount);
+ TInt overhead = 1000000/threadcount;
+ test.Printf(_L("overhead = %dus\n"),overhead);
+
+ ws.iTimeout=1000000;
+ initial.HomeTime();
+ StartWaitSemThread(t, ws);
+ WaitForWaitSemThread(t, KErrTimedOut);
+ final.HomeTime();
+ elapsed = I64INT(final.Int64()-initial.Int64());
+ test.Printf(_L("Time taken = %dus\n"), elapsed);
+ test(elapsed>=900000+overhead && elapsed<1500000+overhead);
+
+ ws.iTimeout=-1;
+ initial.HomeTime();
+ StartWaitSemThread(t, ws);
+ WaitForWaitSemThread(t, KErrArgument);
+ final.HomeTime();
+ elapsed = I64INT(final.Int64()-initial.Int64());
+ test.Printf(_L("Time taken = %dus\n"), elapsed);
+
+ ws.iTimeout=2000000;
+ initial.HomeTime();
+ StartWaitSemThread(t, ws);
+ User::After(1000000);
+ ws.iSem.Signal();
+ WaitForWaitSemThread(t, KErrNone);
+ final.HomeTime();
+ elapsed = I64INT(final.Int64()-initial.Int64());
+ test.Printf(_L("Time taken = %dus\n"), elapsed);
+ test(elapsed>=900000+overhead && elapsed<1500000+overhead);
+
+ ws.iTimeout=100000;
+ StartWaitSemThread(t, ws, EPriorityMore);
+ t.Suspend();
+ ws.iSem.Signal();
+ User::After(200000);
+ t.Resume();
+ WaitForWaitSemThread(t, KErrTimedOut);
+ test(ws.iSem.Wait(1)==KErrNone);
+
+ ws.iTimeout=100000;
+ StartWaitSemThread(t, ws, EPriorityMore);
+ t.Suspend();
+ ws.iSem.Signal();
+ User::After(50000);
+ t.Resume();
+ WaitForWaitSemThread(t, KErrNone);
+ test(ws.iSem.Wait(1)==KErrTimedOut);
+
+ RThread t2;
+ ws.iTimeout=100000;
+ StartWaitSemThread(t, ws, EPriorityMuchMore);
+ StartWaitSemThread(t2, ws, EPriorityMore);
+ t.Suspend();
+ ws.iSem.Signal();
+ test(t2.ExitType()==EExitKill);
+ test(t.ExitType()==EExitPending);
+ t.Resume();
+ WaitForWaitSemThread(t, KErrTimedOut);
+ WaitForWaitSemThread(t2, KErrNone);
+ test(ws.iSem.Wait(1)==KErrTimedOut);
+
+ ws.iTimeout=1000000;
+ initial.HomeTime();
+ StartWaitSemThread(t2, ws, EPriorityMore);
+ StartWaitSemThread(t, ws, EPriorityMuchMore);
+ ws.iSem.Signal();
+ WaitForWaitSemThread(t, KErrNone);
+ final.HomeTime();
+ elapsed = I64INT(final.Int64()-initial.Int64());
+ test.Printf(_L("Time taken = %dus\n"), elapsed);
+ WaitForWaitSemThread(t2, KErrTimedOut);
+ final.HomeTime();
+ elapsed = I64INT(final.Int64()-initial.Int64());
+ test.Printf(_L("Time taken = %dus\n"), elapsed);
+ test(elapsed>=900000+2*overhead && elapsed<1500000+2*overhead);
+
+ ws.iTimeout=1000000;
+ initial.HomeTime();
+ StartWaitSemThread(t2, ws, EPriorityMore);
+ StartWaitSemThread(t, ws, EPriorityMuchMore);
+ WaitForWaitSemThread(t, KErrTimedOut);
+ final.HomeTime();
+ elapsed = I64INT(final.Int64()-initial.Int64());
+ test.Printf(_L("Time taken = %dus\n"), elapsed);
+ WaitForWaitSemThread(t2, KErrTimedOut);
+ final.HomeTime();
+ elapsed = I64INT(final.Int64()-initial.Int64());
+ test.Printf(_L("Time taken = %dus\n"), elapsed);
+ test(elapsed>=900000+2*overhead && elapsed<1500000+2*overhead);
+
+ ws.iTimeout=1000000;
+ initial.HomeTime();
+ StartWaitSemThread(t2, ws, EPriorityMore);
+ StartWaitSemThread(t, ws, EPriorityMuchMore);
+ t.Kill(299792458);
+ WaitForWaitSemThread(t2, KErrTimedOut);
+ WaitForWaitSemThread(t, 299792458);
+ final.HomeTime();
+ elapsed = I64INT(final.Int64()-initial.Int64());
+ test.Printf(_L("Time taken = %dus\n"), elapsed);
+ test(elapsed>=900000+2*overhead && elapsed<1500000+2*overhead);
+
+ ws.iTimeout=1000000;
+ initial.HomeTime();
+ StartWaitSemThread(t, ws, EPriorityMore);
+ StartWaitSemThread(t2, ws, EPriorityMuchMore);
+ test(t.ExitType()==EExitPending);
+ test(t2.ExitType()==EExitPending);
+ ws.iSem.Close();
+ test(t.ExitType()==EExitKill);
+ test(t2.ExitType()==EExitKill);
+ WaitForWaitSemThread(t2, KErrGeneral);
+ WaitForWaitSemThread(t, KErrGeneral);
+ final.HomeTime();
+ elapsed = I64INT(final.Int64()-initial.Int64());
+ test.Printf(_L("Time taken = %dus\n"), elapsed);
+ test(elapsed<=50000+3*overhead);
+
+ test.End();
+ }
+
+void TestSemaphore()
+ {
+/*********** TO DO ************/
+// Check it panics if the count <0
+
+ test.Start(_L("Create"));
+ RSemaphore semaphore;
+ RThread thread1, thread2;
+
+ semaphore.CreateLocal(0); // creates a DPlatSemaphore but casts it to a pointer to a DSemaphore
+ // sets semaphore count to the value of the parameter,
+ // adds object to the K::Semaphores container, sets iHandle
+ // Local sets DSemaphore.iName to NULL & iOwner to Kern::CurrentProcess()
+ // Global sets iName to that passed and iOwner to NULL
+ // Adds a record into CObjectIx containing a pointer to the semaphore object
+/* test.Next(_L("Find"));
+ fullName=semaphore.FullName();
+ find.Find(fullName); // sets iMatch to fullName (misleadingly named method as it doesn't find anything)
+ test(find.Next(fullName)== KErrNone);
+*/
+ test.Next(_L("Producer/Consumer scenario"));
+ // Test Rsemaphore with the producer/consumer scenario RThread thread1, thread2;
+ TRequestStatus stat1, stat2;
+ test(mutex.CreateLocal()==KErrNone);
+ test(slotAvailable.CreateLocal(KMaxBufferSize)==KErrNone);
+ test(itemAvailable.CreateLocal(0)==KErrNone);
+ test(thread1.Create(_L("Thread1"),Producer,KDefaultStackSize,0x200,0x200,NULL)==KErrNone);
+ test(thread2.Create(_L("Thread2"),Consumer,KDefaultStackSize,0x200,0x200,NULL)==KErrNone);
+ thread1.Logon(stat1);
+ thread2.Logon(stat2);
+ test(stat1==KRequestPending);
+ test(stat2==KRequestPending);
+ thread1.Resume();
+ thread2.Resume();
+ User::WaitForRequest(stat1);
+ User::WaitForRequest(stat2);
+ test(stat1==KErrNone);
+ test(stat2==KErrNone);
+ for(TInt jj=0;jj<KNumProducerItems;jj++)
+ test(consumerArray[jj]==jj);
+
+ test.Next(_L("Close"));
+ mutex.Close();
+ CLOSE_AND_WAIT(thread1);
+ CLOSE_AND_WAIT(thread2);
+ test.End();
+ }
+
+void TestMutex2()
+ {
+ RMutex m;
+ test.Start(_L("Create"));
+ test(m.CreateLocal()==KErrNone);
+
+ // Test RMutex::IsHeld()
+ test.Next(_L("IsHeld ?"));
+ test(!m.IsHeld());
+ test.Next(_L("Wait"));
+ m.Wait();
+ test.Next(_L("IsHeld ?"));
+ test(m.IsHeld());
+ test.Next(_L("Signal"));
+ m.Signal();
+ test.Next(_L("IsHeld ?"));
+ test(!m.IsHeld());
+
+ test.End();
+ }
+
+void TestMutex()
+ {
+ test.Start(_L("Create"));
+ test(mutex.CreateLocal()==KErrNone);
+
+ test.Next(_L("Threads writing to arrays test"));
+//
+// Create two threads which write to two arrays. The arrays and indexs
+// are global and each thread writes an identifier to the arrays. For
+// one array the writing and updating of the index is wrapped in a mutex
+// pair. The other array is a control and is not wrapaped within mutex.
+// Each thread records the number of instances it "thinks" it wrote to
+// each array. For the mutex controlled array the actual instances
+// written to the array should always be the same as the threads think.
+//
+ arrayIndex=0;
+ RThread thread1,thread2;
+ test(thread1.Create(_L("Thread1"),MutexThreadEntryPoint1,KDefaultStackSize,0x2000,0x2000,NULL)==KErrNone);
+ test(thread2.Create(_L("Thread2"),MutexThreadEntryPoint2,KDefaultStackSize,0x2000,0x2000,NULL)==KErrNone);
+ TRequestStatus stat1,stat2;
+ thread1.Logon(stat1);
+ thread2.Logon(stat2);
+ test(stat1==KRequestPending);
+ test(stat2==KRequestPending);
+ thread1.Resume();
+ thread2.Resume();
+ User::WaitForRequest(stat1);
+ User::WaitForRequest(stat2);
+ test(stat1==KErrNone);
+ test(stat2==KErrNone);
+ TInt thread1ActualCount=0;
+ TInt thread2ActualCount=0;
+ TInt ii=0;
+ while(ii<KMaxArraySize)
+ {
+ if (array[ii]==EThread1ID)
+ thread1ActualCount++;
+ if (array[ii]==EThread2ID)
+ thread2ActualCount++;
+ ii++;
+ }
+ test.Printf(_L("T1 %d T1ACT %d T2 %d T2ACT %d"),thread1Count,thread1ActualCount,thread2Count,thread2ActualCount);
+ test(thread1ActualCount==thread1Count);
+ test(thread2ActualCount==thread2Count);
+ test(thread1Count==thread2Count);
+ test(thread1Count==(KMaxArraySize>>1));
+
+ test.Next(_L("Close"));
+ CLOSE_AND_WAIT(thread1);
+ CLOSE_AND_WAIT(thread2);
+ mutex.Close();
+ test.End();
+ }
+
+void TestCriticalSection()
+//
+//As TestMutex, but for RCriticalSection
+//
+ {
+
+ test.Start(_L("Create"));
+ test(criticalSn.CreateLocal()==KErrNone);
+
+/***************** TO DO ***********************
+
+ test.Next(_L("Find"));
+//
+// Test finding the RCriticalSection
+//
+ TFindCriticalSection find;
+ TFullName fullName;
+ fullName=criticalSn.FullName();
+ find.Find(fullName);
+ test(find.Next(fullName)==KErrNone);
+ test(fullName==criticalSn.FullName());
+
+************************************************/
+
+ test.Next(_L("Threads writing to arrays test"));
+//
+// Create two threads which write to two arrays. The arrays and indexs
+// are global and each thread writes an identifier to the arrays. For
+// one array the writing and updating of the index is wrapped in a critical
+// section pair. The other array is a control and is not wrapaped within
+// a critical section. Each thread records the number of instances it
+// "thinks" it wrote to each array. For the mutex controlled array the
+// actual instances written to the array should always be the same as the
+// threads think.
+//
+ arrayIndex=0;
+ RThread thread1,thread2;
+ test(thread1.Create(_L("Thread1"),CriticalSnThreadEntryPoint1,KDefaultStackSize,0x2000,0x2000,NULL)==KErrNone);
+ test(thread2.Create(_L("Thread2"),CriticalSnThreadEntryPoint2,KDefaultStackSize,0x2000,0x2000,NULL)==KErrNone);
+ TRequestStatus stat1,stat2;
+ thread1.Logon(stat1);
+ thread2.Logon(stat2);
+ test(stat1==KRequestPending);
+ test(stat2==KRequestPending);
+ thread1.Resume();
+ thread2.Resume();
+ User::WaitForRequest(stat1);
+ User::WaitForRequest(stat2);
+ test(stat1==KErrNone);
+ test(stat2==KErrNone);
+ TInt thread1ActualCount=0;
+ TInt thread2ActualCount=0;
+ TInt ii=0;
+ while(ii<KMaxArraySize)
+ {
+ if (array[ii]==EThread1ID)
+ thread1ActualCount++;
+ if (array[ii]==EThread2ID)
+ thread2ActualCount++;
+ ii++;
+ }
+ test(thread1ActualCount==thread1Count);
+ test(thread2ActualCount==thread2Count);
+ test(thread1Count==thread2Count);
+ test(thread1Count==(KMaxArraySize>>1));
+
+ test.Next(_L("Close"));
+ CLOSE_AND_WAIT(thread1);
+ CLOSE_AND_WAIT(thread2);
+ criticalSn.Close();
+ test.End();
+ }
+
+
+GLDEF_C TInt E32Main()
+ {
+
+ test.Title();
+ __UHEAP_MARK;
+ test.Start(_L("Test RSemaphore"));
+ TestSemaphore();
+ TestSemaphore2();
+ test.Next(_L("Test RMutex"));
+ TestMutex();
+ TestMutex2();
+ test.Next(_L("Test RCriticalSection"));
+ TestCriticalSection();
+ test.End();
+ __UHEAP_MARKEND;
+ return(KErrNone);
+ }
+
+