sl@0: // Copyright (c) 1995-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\prime\t_semutx2.cpp sl@0: // Test RSemaphore and RMutex sl@0: // Overview: sl@0: // Tests the RSemaphore and RMutex sl@0: // API Information: sl@0: // RSemaphore, RMutex sl@0: // Details: sl@0: // - Test and verify that thread priorities work as expected. sl@0: // - Test and verify that signalling an RMutex from the wrong sl@0: // thread fails as expected. sl@0: // - Test and verify that mutex priority inheritance works as sl@0: // expected. sl@0: // - Perform an exhaustive state transition test using mutexs sl@0: // and up to ten threads. Verify priorities, order of execution, sl@0: // mutex signalling, suspend, resume, kill and close. Verify sl@0: // results are as expected. sl@0: // - Test semaphore speed by counting how many Wait/Signal sl@0: // operations can be completed in one second. sl@0: // Platforms/Drives/Compatibility: sl@0: // All. sl@0: // Assumptions/Requirement/Pre-requisites: sl@0: // Failures and causes: sl@0: // Base Port information: sl@0: // sl@0: // sl@0: sl@0: #include sl@0: sl@0: RMutex M1; sl@0: RMutex M2; sl@0: RSemaphore S; sl@0: sl@0: const TInt KBufferSize=4096; sl@0: TUint ThreadId[KBufferSize]; sl@0: TInt PutIx; sl@0: TInt GetIx; sl@0: TInt Count; sl@0: RThread Main; sl@0: sl@0: sl@0: RTest test(_L("T_SEMUTX2")); sl@0: sl@0: /***************************************************************************** sl@0: * Utility functions / macros sl@0: *****************************************************************************/ sl@0: #define TRACE_ON User::SetDebugMask(0xffdfffff); sl@0: #define TRACE_OFF User::SetDebugMask(0x80000000); sl@0: sl@0: //#define MCOUNT(m,c) test((m).Count() ==(c)) sl@0: // mutex count value is not visible for user any more sl@0: #define MCOUNT(m,c) (void)(1) sl@0: #define IDCHECK(x) test(GetNextId()==(x)) sl@0: #define NUMCHECK(x) test(NumIdsPending()==(x)) sl@0: sl@0: #define id0 id[0] sl@0: #define id1 id[1] sl@0: #define id2 id[2] sl@0: #define id3 id[3] sl@0: #define id4 id[4] sl@0: #define id5 id[5] sl@0: #define id6 id[6] sl@0: #define id7 id[7] sl@0: #define id8 id[8] sl@0: #define id9 id[9] sl@0: #define id10 id[10] sl@0: sl@0: TBool Exists(const TDesC& aName) sl@0: { sl@0: TFullName n(RProcess().Name()); sl@0: n+=_L("::"); sl@0: n+=aName; sl@0: TFindThread ft(n); sl@0: TFullName fn; sl@0: return ft.Next(fn)==KErrNone; sl@0: } sl@0: sl@0: TBool Exists(TInt aNum) sl@0: { sl@0: TBuf<4> b; sl@0: b.Num(aNum); sl@0: return Exists(b); sl@0: } sl@0: sl@0: void BusyWait(TInt aMicroseconds) sl@0: { sl@0: TTime begin; sl@0: begin.HomeTime(); sl@0: FOREVER sl@0: { sl@0: TTime now; sl@0: now.HomeTime(); sl@0: TTimeIntervalMicroSeconds iv=now.MicroSecondsFrom(begin); sl@0: if (iv.Int64()>=TInt64(aMicroseconds)) sl@0: return; sl@0: } sl@0: } sl@0: sl@0: void Kick(RThread& t) sl@0: { sl@0: TRequestStatus s; sl@0: TRequestStatus* pS=&s; sl@0: t.RequestComplete(pS,0); sl@0: } sl@0: sl@0: TUint GetNextId() sl@0: { sl@0: if (GetIx b; sl@0: b.Num(n); sl@0: TInt r=t.Create(b,ThreadFunction,0x1000,NULL,aPtr); sl@0: test(r==KErrNone); sl@0: t.Resume(); sl@0: TUint id=t.Id(); sl@0: test.Printf(_L("id=%d\n"),id); sl@0: return id; sl@0: } sl@0: sl@0: /* sl@0: Possible thread relationships with mutex: sl@0: Holding sl@0: Waiting sl@0: Waiting + suspended sl@0: Hold Pending sl@0: sl@0: Need to verify correct behaviour when the following actions occur for each of these states: sl@0: Suspend thread sl@0: Resume thread sl@0: Change thread priority sl@0: Thread exits sl@0: Thread is killed sl@0: Mutex deleted sl@0: */ sl@0: sl@0: PFV HoldExtra; sl@0: void KickMain() sl@0: { sl@0: RThread me; sl@0: Kick(Main); sl@0: User::WaitForAnyRequest(); sl@0: me.SetPriority(EPriorityMuchMore); sl@0: MutexSignal(); // this should wake up t8 sl@0: MutexWait(); sl@0: MutexSignal(); // this should wake up t9 sl@0: MutexWait(); sl@0: Kick(Main); sl@0: User::WaitForAnyRequest(); sl@0: if (HoldExtra) sl@0: HoldExtra(); sl@0: } sl@0: sl@0: void RackEmUp(RThread* t, PFV* f, TUint* id) sl@0: { sl@0: // set up t4 holding sl@0: // t1, t2, t5, t10 waiting sl@0: // t3, t6, t7 waiting+suspended sl@0: // t8, t9 pending sl@0: MCOUNT(M1,1); // check mutex free sl@0: Kick(t[4]); sl@0: f[4]=&KickMain; sl@0: User::WaitForAnyRequest(); sl@0: MCOUNT(M1,0); // check mutex now held sl@0: TInt i; sl@0: for (i=1; i<=10; ++i) sl@0: if (i!=4) sl@0: Kick(t[i]); // wake up threads sl@0: User::After(50000); // let threads wait sl@0: MCOUNT(M1,-9); // check 9 threads waiting sl@0: Kick(t[4]); sl@0: User::WaitForAnyRequest(); sl@0: MCOUNT(M1,-7); // check 7 threads waiting sl@0: NUMCHECK(3); sl@0: IDCHECK(id4); // from the initial wait sl@0: IDCHECK(id4); // now have t8, t9 pending, t4 holding, rest waiting sl@0: IDCHECK(id4); // now have t8, t9 pending, t4 holding, rest waiting sl@0: t[4].SetPriority(EPriorityNormal); sl@0: t[7].Resume(); // test resume when not suspended sl@0: MCOUNT(M1,-7); // check 7 threads waiting sl@0: t[3].Suspend(); sl@0: t[6].Suspend(); sl@0: t[7].Suspend(); // now have required state sl@0: t[3].Suspend(); // suspend and resume t3 again for good measure sl@0: t[3].Resume(); sl@0: MCOUNT(M1,-7); // check 7 threads waiting sl@0: HoldExtra=NULL; sl@0: } sl@0: sl@0: void SimpleCheck(TInt n, const TUint* id, ...) sl@0: { sl@0: VA_LIST list; sl@0: VA_START(list,id); sl@0: User::After(50000); // let stuff happen sl@0: NUMCHECK(n); sl@0: TInt i; sl@0: for (i=0; i b; sl@0: b.Num(n); sl@0: TInt r=t.Create(b,SemThreadFunction,0x1000,NULL,aPtr); sl@0: test(r==KErrNone); sl@0: t.Resume(); sl@0: TUint id=t.Id(); sl@0: return id; sl@0: } sl@0: sl@0: /* sl@0: Possible thread relationships with semaphore: sl@0: Waiting sl@0: Waiting + suspended sl@0: sl@0: Need to verify correct behaviour when the following actions occur for each of these states: sl@0: Suspend thread sl@0: Resume thread sl@0: Change thread priority sl@0: Thread exits sl@0: Thread is killed sl@0: Semaphore deleted sl@0: */ sl@0: sl@0: void RackEmUp2(RThread* t, PFV* f, TUint* id) sl@0: { sl@0: // set up sl@0: // t1, t2, t4, t5, t6, t8, t9 waiting sl@0: // t3, t7, t10 waiting+suspended sl@0: (void)f; sl@0: MCOUNT(S,2); // check semaphore level = 2 sl@0: SemWait(); sl@0: SemWait(); sl@0: MCOUNT(S,0); // check semaphore level = 0 sl@0: NUMCHECK(2); sl@0: IDCHECK(id0); sl@0: IDCHECK(id0); sl@0: TInt i; sl@0: for (i=1; i<=10; ++i) sl@0: Kick(t[i]); // wake up threads sl@0: User::After(50000); // let threads wait sl@0: MCOUNT(S,-10); // check 10 threads waiting sl@0: t[7].Resume(); // test resume when not suspended sl@0: MCOUNT(S,-10); // check 7 threads waiting sl@0: t[3].Suspend(); sl@0: t[7].Suspend(); sl@0: t[10].Suspend(); // now have required state sl@0: t[3].Suspend(); // suspend and resume t3 again for good measure sl@0: t[3].Resume(); sl@0: MCOUNT(S,-7); // check 7 threads waiting sl@0: } sl@0: sl@0: void Resurrect2(TInt n, TThreadPriority aPriority, RThread* t, PFV* f, TUint* id) sl@0: { sl@0: f[n]=NULL; sl@0: id[n]=CreateSemThread(t[n],n,f+n); sl@0: t[n].SetPriority(EPriorityRealTime); sl@0: t[n].SetPriority(aPriority); sl@0: NUMCHECK(1); sl@0: IDCHECK(id[n]); sl@0: } sl@0: sl@0: /***************************************************************************** sl@0: * Semaphore exhaustive state transition test sl@0: *****************************************************************************/ sl@0: void TestSemaphore() sl@0: { sl@0: test.Start(_L("Test semaphore state transitions")); sl@0: RThread t[11]; sl@0: TUint id[11]; sl@0: PFV f[11]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; sl@0: id[0]=(TUint)RThread().Id(); sl@0: PutIx=0; sl@0: GetIx=0; sl@0: Count=0; sl@0: test.Next(_L("Create semaphore")); sl@0: TInt r=S.CreateLocal(2); sl@0: test(r==KErrNone); sl@0: MCOUNT(S,2); sl@0: SemWait(); sl@0: MCOUNT(S,1); sl@0: SemSignal(); sl@0: MCOUNT(S,2); sl@0: SemWait(); sl@0: SemWait(); sl@0: MCOUNT(S,0); sl@0: S.Signal(2); sl@0: MCOUNT(S,2); sl@0: NUMCHECK(3); sl@0: IDCHECK(id0); sl@0: IDCHECK(id0); sl@0: IDCHECK(id0); sl@0: test.Next(_L("Create threads")); sl@0: TInt i; sl@0: for (i=1; i<=10; ++i) sl@0: { sl@0: id[i]=CreateSemThread(t[i],i,f+i); sl@0: f[i]=&Wait; sl@0: } sl@0: t[8].SetPriority(EPriorityMore); // t1-t3=less, t4-t7=normal, t8-t10 more, t0 much more sl@0: t[9].SetPriority(EPriorityMore); sl@0: t[10].SetPriority(EPriorityMore); sl@0: t[1].SetPriority(EPriorityLess); sl@0: t[2].SetPriority(EPriorityLess); sl@0: t[3].SetPriority(EPriorityLess); sl@0: User::After(50000); sl@0: MCOUNT(S,-8); // check 8 waiting sl@0: NUMCHECK(2); sl@0: IDCHECK(id8); sl@0: IDCHECK(id9); // check t8,t9 got through sl@0: t[8].SetPriority(EPriorityRealTime); sl@0: Kick(t[8]); // let t8 run and signal sl@0: t[8].SetPriority(EPriorityMore); sl@0: MCOUNT(S,-7); // check 7 waiting sl@0: User::After(50000); // let next thread obtain semaphore sl@0: MCOUNT(S,-7); // check 7 waiting sl@0: NUMCHECK(1); sl@0: IDCHECK(id10); // check t10 got it sl@0: Kick(t[10]); // let t10 run and signal sl@0: User::After(50000); // let next thread obtain semaphore sl@0: MCOUNT(S,-6); // check 6 waiting sl@0: NUMCHECK(1); sl@0: IDCHECK(id4); // check t4 got it sl@0: t[1].SetPriority(EPriorityRealTime); // boost t1 sl@0: MCOUNT(S,-6); // check 6 still waiting sl@0: User::After(50000); // let next thread obtain semaphore sl@0: MCOUNT(S,-6); // check 6 still waiting sl@0: NUMCHECK(0); sl@0: Kick(t[9]); // make t9 ready to run and signal sl@0: MCOUNT(S,-6); // check 6 still waiting sl@0: User::After(50000); // let next thread obtain semaphore sl@0: MCOUNT(S,-5); // check 5 waiting sl@0: NUMCHECK(1); sl@0: IDCHECK(id1); // check t1 got it sl@0: t[1].SetPriority(EPriorityLess); sl@0: Kick(t[1]); // kick all remaining threads sl@0: Kick(t[2]); sl@0: Kick(t[3]); sl@0: Kick(t[4]); sl@0: Kick(t[5]); sl@0: Kick(t[6]); sl@0: Kick(t[7]); sl@0: User::After(50000); // let them run and obtain/signal the semaphore sl@0: MCOUNT(S,2); // check semaphore now back to initial level sl@0: SimpleCheck(5,id,5,6,7,2,3); sl@0: sl@0: for (i=1; i<=10; ++i) sl@0: f[i]=NULL; sl@0: RackEmUp2(t,f,id); // set up threads waiting on semaphore again sl@0: S.Signal(); sl@0: SimpleCheck(7,id,8,9,4,5,6,1,2); // let them go sl@0: MCOUNT(S,1); sl@0: S.Wait(); sl@0: t[3].SetPriority(EPriorityRealTime); // change suspended thread priority sl@0: t[7].Resume(); sl@0: SimpleCheck(0,id); // t7 should wait for signal sl@0: S.Signal(); sl@0: SimpleCheck(1,id,7); sl@0: MCOUNT(S,1); sl@0: t[3].Resume(); sl@0: t[10].Resume(); sl@0: NUMCHECK(1); sl@0: IDCHECK(id3); // t3 should have grabbed semaphore as soon as we resumed it sl@0: SimpleCheck(1,id,10); sl@0: t[3].SetPriority(EPriorityLess); sl@0: S.Signal(); // put level back to 2 sl@0: sl@0: RackEmUp2(t,f,id); // set up threads waiting on semaphore again sl@0: S.Signal(); sl@0: SimpleCheck(7,id,8,9,4,5,6,1,2); // let them go sl@0: MCOUNT(S,1); sl@0: S.Wait(); sl@0: t[3].SetPriority(EPriorityRealTime); // change suspended thread priority sl@0: t[7].Resume(); sl@0: SimpleCheck(0,id); // t7 should wait for signal sl@0: S.Signal(); sl@0: SimpleCheck(1,id,7); sl@0: MCOUNT(S,1); sl@0: t[10].Resume(); sl@0: t[3].Resume(); // t3 not woken up here since t10 has already been given the semaphore sl@0: NUMCHECK(0); sl@0: SimpleCheck(2,id,10,3); sl@0: t[3].SetPriority(EPriorityLess); sl@0: S.Signal(); // put level back to 2 sl@0: sl@0: RackEmUp2(t,f,id); // set up threads waiting on semaphore again sl@0: S.Signal(); sl@0: SimpleCheck(7,id,8,9,4,5,6,1,2); // let them go sl@0: MCOUNT(S,1); sl@0: S.Wait(); sl@0: t[3].SetPriority(EPriorityRealTime); // change suspended thread priority sl@0: t[7].Resume(); sl@0: SimpleCheck(0,id); // t7 should wait for signal sl@0: S.Signal(); sl@0: S.Signal(); // put level back to 2 sl@0: SimpleCheck(1,id,7); sl@0: MCOUNT(S,2); sl@0: t[10].Resume(); sl@0: t[3].Resume(); // t3 and t10 both woken up here, t3 should run and signal sl@0: MCOUNT(S,1); sl@0: NUMCHECK(1); sl@0: IDCHECK(id3); sl@0: SimpleCheck(1,id,10); sl@0: t[3].SetPriority(EPriorityLess); sl@0: sl@0: RackEmUp2(t,f,id); // set up threads waiting on semaphore again sl@0: t[9].Kill(0); // kill waiting thread sl@0: t[9].Close(); sl@0: test(!Exists(9)); sl@0: t[10].Kill(0); // kill suspended thread sl@0: t[10].Close(); sl@0: test(!Exists(10)); sl@0: MCOUNT(S,-6); sl@0: f[5]=&Exit; // get t5 to exit after acquiring semaphore sl@0: S.Signal(); sl@0: SimpleCheck(3,id,8,4,5); // let them go sl@0: MCOUNT(S,-3); // check one signal has been lost due to t5 exiting sl@0: t[5].Close(); sl@0: test(!Exists(5)); sl@0: t[3].Resume(); sl@0: t[7].Resume(); sl@0: MCOUNT(S,-5); sl@0: S.Signal(); sl@0: SimpleCheck(5,id,6,7,1,2,3); // let them go sl@0: MCOUNT(S,1); sl@0: Resurrect2(9,EPriorityMore,t,f,id); sl@0: Resurrect2(10,EPriorityMore,t,f,id); sl@0: Resurrect2(5,EPriorityNormal,t,f,id); sl@0: S.Signal(); sl@0: sl@0: RackEmUp2(t,f,id); // set up threads waiting on semaphore again sl@0: f[5]=&Exit; // get t5 to exit after acquiring semaphore sl@0: S.Close(); // close semaphore - threads should panic except for 5 sl@0: sl@0: TBool jit = User::JustInTime(); sl@0: User::SetJustInTime(EFalse); sl@0: User::After(1000000); sl@0: User::SetJustInTime(jit); sl@0: for (i=1; i<=10; ++i) sl@0: { sl@0: if (i==3 || i==7 || i==10) sl@0: { sl@0: test(t[i].ExitType()==EExitPending); sl@0: } sl@0: else if (i!=5) sl@0: { sl@0: test(t[i].ExitType()==EExitPanic); sl@0: test(t[i].ExitReason()==EBadHandle); sl@0: test(t[i].ExitCategory()==_L("KERN-EXEC")); sl@0: t[i].Close(); sl@0: test(!Exists(i)); sl@0: } sl@0: else sl@0: { sl@0: test(t[i].ExitType()==EExitKill); sl@0: test(t[i].ExitReason()==0); sl@0: t[i].Close(); sl@0: test(!Exists(i)); sl@0: } sl@0: } sl@0: t[3].Resume(); sl@0: t[7].Resume(); sl@0: t[10].Resume(); sl@0: User::SetJustInTime(EFalse); sl@0: User::After(1000000); sl@0: User::SetJustInTime(jit); sl@0: for (i=1; i<=10; ++i) sl@0: { sl@0: if (i==3 || i==7 || i==10) sl@0: { sl@0: test(t[i].ExitType()==EExitPanic); sl@0: test(t[i].ExitReason()==EBadHandle); sl@0: test(t[i].ExitCategory()==_L("KERN-EXEC")); sl@0: t[i].Close(); sl@0: test(!Exists(i)); sl@0: } sl@0: } sl@0: sl@0: test.End(); sl@0: } sl@0: sl@0: /***************************************************************************** sl@0: * Semaphore benchmarks sl@0: *****************************************************************************/ sl@0: TInt SemSpeed(TAny* aPtr) sl@0: { sl@0: TInt& count=*(TInt*)aPtr; sl@0: RThread().SetPriority(EPriorityMore); sl@0: FOREVER sl@0: { sl@0: S.Wait(); sl@0: S.Signal(); sl@0: ++count; sl@0: } sl@0: } sl@0: sl@0: void TestSemSpeed() sl@0: { sl@0: test.Start(_L("Test semaphore speed")); sl@0: TInt count=0; sl@0: TInt r=S.CreateLocal(1); sl@0: test(r==KErrNone); sl@0: sl@0: RThread t; sl@0: r=t.Create(_L("SemSpeed"),SemSpeed,0x1000,NULL,&count); sl@0: test(r==KErrNone); sl@0: t.SetPriority(EPriorityRealTime); sl@0: t.Resume(); sl@0: User::AfterHighRes(1000000); sl@0: t.Kill(0); sl@0: t.Close(); sl@0: test(!Exists(_L("SemSpeed"))); sl@0: test.Printf(_L("%d wait/signal in 1 second\n"),count); sl@0: sl@0: S.Close(); sl@0: test.End(); sl@0: } sl@0: sl@0: sl@0: GLDEF_C TInt E32Main() sl@0: { sl@0: test.Title(); sl@0: sl@0: test.Start(_L("Test mutexes and semaphores")); sl@0: RThread().SetPriority(EPriorityMuchMore); sl@0: TInt r=Main.Duplicate(RThread()); sl@0: test(r==KErrNone); sl@0: sl@0: Test0(); sl@0: Test1(); sl@0: TestMutex1(); sl@0: TestMutex2(); sl@0: TestSemaphore(); sl@0: sl@0: TestMutexSpeed(); sl@0: TestSemSpeed(); sl@0: sl@0: Main.Close(); sl@0: test.End(); sl@0: return KErrNone; sl@0: } sl@0: