sl@0: // Copyright (c) 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_rwlock.cpp sl@0: // Overview: sl@0: // Test the RReadWriteLock type. sl@0: // API Information: sl@0: // RReadWriteLock sl@0: // Details: sl@0: // Test all functions individually and in combination. 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: //! @SYMTestCaseID KBASE-T_RWLOCK-2444 sl@0: //! @SYMTestType UT sl@0: //! @SYMTestCaseDesc Verify correct operation of RReadWriteLock sl@0: //! @SYMPREQ PREQ2094 sl@0: //! @SYMTestPriority High sl@0: //! @SYMTestActions Call all functions of RReadWriteLock in a variety sl@0: //! of circumstances and verify correct results sl@0: //! @SYMTestExpectedResults All tests pass sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: RTest Test(_L("T_RWLOCK")); sl@0: RReadWriteLock TheLock; sl@0: volatile TInt ThreadsRunning; sl@0: TInt LogIndex; sl@0: TBool LogReaders[20]; sl@0: sl@0: // Check creating, using and closing a lock doesn't leak memory sl@0: void TestCreation() sl@0: { sl@0: Test.Next(_L("Creation")); sl@0: sl@0: __KHEAP_MARK; sl@0: __UHEAP_MARK; sl@0: sl@0: Test(TheLock.CreateLocal() == KErrNone); sl@0: TheLock.ReadLock(); sl@0: TheLock.Unlock(); sl@0: TheLock.WriteLock(); sl@0: TheLock.Unlock(); sl@0: TheLock.Close(); sl@0: sl@0: __UHEAP_MARKEND; sl@0: __KHEAP_MARKEND; sl@0: } sl@0: sl@0: TInt ReadEntryPoint(TAny* aArg) sl@0: { sl@0: *(TBool*)aArg = ETrue; sl@0: __e32_atomic_add_ord32(&ThreadsRunning, 1); sl@0: TheLock.ReadLock(); sl@0: const TInt index = __e32_atomic_add_ord32(&LogIndex, 1); sl@0: LogReaders[index] = ETrue; sl@0: TheLock.Unlock(); sl@0: __e32_atomic_add_ord32(&ThreadsRunning, TUint32(-1)); sl@0: return KErrNone; sl@0: } sl@0: sl@0: TInt WriteEntryPoint(TAny* aArg) sl@0: { sl@0: *(TBool*)aArg = ETrue; sl@0: __e32_atomic_add_ord32(&ThreadsRunning, 1); sl@0: TheLock.WriteLock(); sl@0: const TInt index = __e32_atomic_add_ord32(&LogIndex, 1); sl@0: LogReaders[index] = EFalse; sl@0: TheLock.Unlock(); sl@0: __e32_atomic_add_ord32(&ThreadsRunning, TUint32(-1)); sl@0: return KErrNone; sl@0: } sl@0: sl@0: void Init() sl@0: { sl@0: __e32_atomic_store_ord32(&ThreadsRunning, 0); sl@0: __e32_atomic_store_ord32(&LogIndex, 0); sl@0: } sl@0: sl@0: void CreateThread(TBool aReader) sl@0: { sl@0: RThread newThread; sl@0: TBool threadStarted = EFalse; sl@0: TInt ret = newThread.Create(KNullDesC, aReader ? ReadEntryPoint : WriteEntryPoint, KDefaultStackSize, KMinHeapSize, KMinHeapSize, &threadStarted, EOwnerProcess); sl@0: Test(ret == KErrNone, __LINE__); sl@0: newThread.SetPriority(EPriorityMore); sl@0: newThread.Resume(); sl@0: while (!threadStarted) sl@0: User::After(1000); sl@0: newThread.Close(); sl@0: } sl@0: sl@0: void WaitForThreadsToClose(TInt aThreads = 0) sl@0: { sl@0: while (ThreadsRunning > aThreads) sl@0: { sl@0: User::After(1000); sl@0: } sl@0: } sl@0: sl@0: // Check that queuing multiple reads and writes on a lock with writer priority sl@0: // results in the correct type of client being released in the correct order sl@0: // (can' predict exact client order on multi-processor systems though) sl@0: void TestWriterPriority() sl@0: { sl@0: Test.Next(_L("Writer Priority")); sl@0: TInt ret = TheLock.CreateLocal(RReadWriteLock::EWriterPriority); sl@0: Test(ret == KErrNone, __LINE__); sl@0: TheLock.WriteLock(); sl@0: sl@0: Init(); sl@0: CreateThread(ETrue); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: sl@0: TheLock.Unlock(); sl@0: WaitForThreadsToClose(); sl@0: TheLock.ReadLock(); sl@0: sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: sl@0: TheLock.Unlock(); sl@0: WaitForThreadsToClose(); sl@0: sl@0: TheLock.Close(); sl@0: sl@0: Test(LogIndex == 15, __LINE__); sl@0: const TBool expected[] = { EFalse, EFalse, EFalse, EFalse, ETrue, ETrue, ETrue, ETrue, ETrue, ETrue, EFalse, EFalse, ETrue, ETrue, ETrue }; sl@0: for (TInt index = 0; index < LogIndex; index++) sl@0: { sl@0: Test(LogReaders[index] == expected[index], __LINE__); sl@0: } sl@0: } sl@0: sl@0: // Check that queuing multiple reads and writes on a lock with alternate priority sl@0: // results in the correct type of client being released in the correct order sl@0: // (can' predict exact client order on multi-processor systems though) sl@0: void TestAlternatePriority() sl@0: { sl@0: Test.Next(_L("Alternate Priority")); sl@0: TInt ret = TheLock.CreateLocal(RReadWriteLock::EAlternatePriority); sl@0: Test(ret == KErrNone, __LINE__); sl@0: TheLock.WriteLock(); sl@0: sl@0: Init(); sl@0: CreateThread(ETrue); sl@0: CreateThread(ETrue); sl@0: CreateThread(ETrue); sl@0: CreateThread(ETrue); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(EFalse); sl@0: CreateThread(EFalse); sl@0: CreateThread(EFalse); sl@0: CreateThread(EFalse); sl@0: sl@0: TheLock.Unlock(); sl@0: WaitForThreadsToClose(); sl@0: TheLock.ReadLock(); sl@0: sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: sl@0: TheLock.Unlock(); sl@0: WaitForThreadsToClose(); sl@0: sl@0: TheLock.Close(); sl@0: sl@0: Test(LogIndex == 15, __LINE__); sl@0: const TInt expected[] = { ETrue, EFalse, ETrue, EFalse, ETrue, EFalse, ETrue, EFalse, ETrue, EFalse, EFalse, ETrue, EFalse, ETrue, ETrue }; sl@0: for (TInt index = 0; index < LogIndex; index++) sl@0: { sl@0: Test(LogReaders[index] == expected[index], __LINE__); sl@0: } sl@0: } sl@0: sl@0: // Check that queuing multiple reads and writes on a lock with reader priority sl@0: // results in the correct type of client being released in the correct order sl@0: // (can' predict exact client order on multi-processor systems though) sl@0: void TestReaderPriority() sl@0: { sl@0: Test.Next(_L("Reader Priority")); sl@0: TInt ret = TheLock.CreateLocal(RReadWriteLock::EReaderPriority); sl@0: Test(ret == KErrNone, __LINE__); sl@0: TheLock.WriteLock(); sl@0: sl@0: Init(); sl@0: CreateThread(ETrue); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: sl@0: TheLock.Unlock(); sl@0: WaitForThreadsToClose(); sl@0: TheLock.WriteLock(); sl@0: sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: sl@0: TheLock.Unlock(); sl@0: WaitForThreadsToClose(); sl@0: sl@0: TheLock.Close(); sl@0: sl@0: Test(LogIndex == 15, __LINE__); sl@0: const TInt expected[] = { ETrue, ETrue, ETrue, ETrue, ETrue, ETrue, EFalse, EFalse, EFalse, EFalse, ETrue, ETrue, ETrue, EFalse, EFalse }; sl@0: for (TInt index = 0; index < LogIndex; index++) sl@0: { sl@0: Test(LogReaders[index] == expected[index], __LINE__); sl@0: } sl@0: } sl@0: sl@0: void DoTestTryLock(TBool aWriterFirst) sl@0: { sl@0: TheLock.ReadLock(); sl@0: sl@0: TBool tryLock = TheLock.TryWriteLock(); sl@0: Test(!tryLock, __LINE__); sl@0: sl@0: tryLock = TheLock.TryReadLock(); sl@0: Test(tryLock, __LINE__); sl@0: TheLock.Unlock(); sl@0: sl@0: Init(); sl@0: CreateThread(EFalse); sl@0: tryLock = TheLock.TryReadLock(); sl@0: if (tryLock) sl@0: { sl@0: Test(!aWriterFirst, __LINE__); sl@0: TheLock.Unlock(); sl@0: } sl@0: else sl@0: { sl@0: Test(aWriterFirst, __LINE__); sl@0: } sl@0: tryLock = TheLock.TryWriteLock(); sl@0: Test(!tryLock, __LINE__); sl@0: sl@0: TheLock.Unlock(); sl@0: WaitForThreadsToClose(); sl@0: sl@0: TheLock.WriteLock(); sl@0: sl@0: tryLock = TheLock.TryReadLock(); sl@0: Test(!tryLock, __LINE__); sl@0: tryLock = TheLock.TryWriteLock(); sl@0: Test(!tryLock, __LINE__); sl@0: sl@0: TheLock.Unlock(); sl@0: TheLock.Close(); sl@0: } sl@0: sl@0: // Check that the TryReadLock and TryWriteLock functions block only when they sl@0: // should for the different types of priority sl@0: void TestTryLock() sl@0: { sl@0: Test.Next(_L("Try Lock")); sl@0: sl@0: TInt ret = TheLock.CreateLocal(RReadWriteLock::EWriterPriority); sl@0: Test(ret == KErrNone, __LINE__); sl@0: DoTestTryLock(ETrue); sl@0: sl@0: ret = TheLock.CreateLocal(RReadWriteLock::EAlternatePriority); sl@0: Test(ret == KErrNone, __LINE__); sl@0: DoTestTryLock(ETrue); sl@0: sl@0: ret = TheLock.CreateLocal(RReadWriteLock::EReaderPriority); sl@0: Test(ret == KErrNone, __LINE__); sl@0: DoTestTryLock(EFalse); sl@0: sl@0: TheLock.Close(); sl@0: } sl@0: sl@0: void DoTestUpgrade(RReadWriteLock::TReadWriteLockPriority aPriority) sl@0: { sl@0: TInt ret = TheLock.CreateLocal(aPriority); sl@0: Test(ret == KErrNone, __LINE__); sl@0: TheLock.ReadLock(); sl@0: sl@0: TBool success = TheLock.TryUpgradeReadLock(); sl@0: Test(success, __LINE__); sl@0: TheLock.Unlock(); sl@0: sl@0: TheLock.ReadLock(); sl@0: TheLock.ReadLock(); sl@0: success = TheLock.TryUpgradeReadLock(); sl@0: Test(!success, __LINE__); sl@0: TheLock.Unlock(); sl@0: TheLock.Unlock(); sl@0: sl@0: TheLock.ReadLock(); sl@0: Init(); sl@0: CreateThread(EFalse); sl@0: success = TheLock.TryUpgradeReadLock(); sl@0: Test(success || !(aPriority == RReadWriteLock::EReaderPriority), __LINE__); sl@0: sl@0: TheLock.Unlock(); sl@0: WaitForThreadsToClose(); sl@0: TheLock.Close(); sl@0: } sl@0: sl@0: // Check that upgrading a lock succeeds only when it should sl@0: void TestUpgrade() sl@0: { sl@0: Test.Next(_L("Upgrade Lock")); sl@0: sl@0: DoTestUpgrade(RReadWriteLock::EWriterPriority); sl@0: DoTestUpgrade(RReadWriteLock::EAlternatePriority); sl@0: DoTestUpgrade(RReadWriteLock::EReaderPriority); sl@0: } sl@0: sl@0: void DoTestDowngrade(RReadWriteLock::TReadWriteLockPriority aPriority) sl@0: { sl@0: TInt ret = TheLock.CreateLocal(aPriority); sl@0: Test(ret == KErrNone, __LINE__); sl@0: TheLock.WriteLock(); sl@0: sl@0: Init(); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: sl@0: TheLock.DowngradeWriteLock(); sl@0: sl@0: switch (aPriority) sl@0: { sl@0: case RReadWriteLock::EWriterPriority: sl@0: case RReadWriteLock::EAlternatePriority: sl@0: { sl@0: Test(LogIndex == 0, __LINE__); sl@0: break; sl@0: } sl@0: case RReadWriteLock::EReaderPriority: sl@0: { sl@0: WaitForThreadsToClose(2); sl@0: Test(LogIndex == 2, __LINE__); sl@0: Test(LogReaders[0], __LINE__); sl@0: Test(LogReaders[1], __LINE__); sl@0: break; sl@0: } sl@0: }; sl@0: sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: CreateThread(ETrue); sl@0: CreateThread(EFalse); sl@0: sl@0: TheLock.Unlock(); sl@0: WaitForThreadsToClose(); sl@0: TheLock.Close(); sl@0: sl@0: Test(LogIndex == 8, __LINE__); sl@0: sl@0: switch (aPriority) sl@0: { sl@0: case RReadWriteLock::EWriterPriority: sl@0: { sl@0: const TInt expected[] = { EFalse, EFalse, EFalse, EFalse, ETrue, ETrue, ETrue, ETrue }; sl@0: for (TInt index = 0; index < LogIndex; index++) sl@0: { sl@0: Test(LogReaders[index] == expected[index], __LINE__); sl@0: } sl@0: break; sl@0: } sl@0: case RReadWriteLock::EAlternatePriority: sl@0: { sl@0: const TInt expected[] = { EFalse, ETrue, EFalse, ETrue, EFalse, ETrue, EFalse, ETrue }; sl@0: for (TInt index = 0; index < LogIndex; index++) sl@0: { sl@0: Test(LogReaders[index] == expected[index], __LINE__); sl@0: } sl@0: break; sl@0: } sl@0: case RReadWriteLock::EReaderPriority: sl@0: { sl@0: const TInt expected[] = { ETrue, ETrue, ETrue, ETrue, EFalse, EFalse, EFalse, EFalse }; sl@0: for (TInt index = 0; index < LogIndex; index++) sl@0: { sl@0: Test(LogReaders[index] == expected[index], __LINE__); sl@0: } sl@0: break; sl@0: } sl@0: }; sl@0: } sl@0: sl@0: // Check that downgrading a lock succeeds only when it should sl@0: void TestDowngrade() sl@0: { sl@0: Test.Next(_L("Downgrade Lock")); sl@0: sl@0: DoTestDowngrade(RReadWriteLock::EWriterPriority); sl@0: DoTestDowngrade(RReadWriteLock::EAlternatePriority); sl@0: DoTestDowngrade(RReadWriteLock::EReaderPriority); sl@0: } sl@0: sl@0: TInt PanicEntryPoint(TAny* aArg) sl@0: { sl@0: switch (TInt(aArg)) sl@0: { sl@0: case 0: // Check priority lower bound sl@0: TheLock.CreateLocal(RReadWriteLock::TReadWriteLockPriority(RReadWriteLock::EWriterPriority-1)); sl@0: break; sl@0: case 1: // Check priority upper bound sl@0: TheLock.CreateLocal(RReadWriteLock::TReadWriteLockPriority(RReadWriteLock::EReaderPriority+1)); sl@0: break; sl@0: case 2: // Check close while holding read lock sl@0: TheLock.CreateLocal(RReadWriteLock::TReadWriteLockPriority(RReadWriteLock::EAlternatePriority)); sl@0: TheLock.ReadLock(); sl@0: TheLock.Close(); sl@0: break; sl@0: case 3: // Check close while holding write lock sl@0: TheLock.CreateLocal(RReadWriteLock::TReadWriteLockPriority(RReadWriteLock::EAlternatePriority)); sl@0: TheLock.WriteLock(); sl@0: TheLock.Close(); sl@0: break; sl@0: case 4: // Check max readers sl@0: TheLock.CreateLocal(RReadWriteLock::TReadWriteLockPriority(RReadWriteLock::EReaderPriority)); sl@0: { sl@0: for (TInt count = 0; count < RReadWriteLock::EReadWriteLockClientCategoryLimit; count++) sl@0: TheLock.ReadLock(); sl@0: } sl@0: TheLock.ReadLock(); sl@0: break; sl@0: case 5: // Check max pending readers sl@0: TheLock.CreateLocal(RReadWriteLock::TReadWriteLockPriority(RReadWriteLock::EReaderPriority)); sl@0: TheLock.WriteLock(); sl@0: { sl@0: TUint16* hackLock = (TUint16*)&TheLock; sl@0: hackLock[2] = KMaxTUint16; // Hack readers pending field sl@0: } sl@0: TheLock.ReadLock(); sl@0: break; sl@0: case 6: // Check max pending writers sl@0: TheLock.CreateLocal(RReadWriteLock::TReadWriteLockPriority(RReadWriteLock::EReaderPriority)); sl@0: TheLock.ReadLock(); sl@0: { sl@0: TUint16* hackLock = (TUint16*)&TheLock; sl@0: hackLock[3] = KMaxTUint16; // Hack writers pending field sl@0: } sl@0: TheLock.WriteLock(); sl@0: break; sl@0: case 7: // Check lock held when unlocking sl@0: TheLock.CreateLocal(RReadWriteLock::TReadWriteLockPriority(RReadWriteLock::EAlternatePriority)); sl@0: TheLock.Unlock(); sl@0: break; sl@0: case 8: // Check lock held when unlocking after read lock/unlock sl@0: TheLock.CreateLocal(RReadWriteLock::TReadWriteLockPriority(RReadWriteLock::EAlternatePriority)); sl@0: TheLock.ReadLock(); sl@0: TheLock.Unlock(); sl@0: TheLock.Unlock(); sl@0: break; sl@0: case 9: // Check lock held when unlocking after write lock/unlock sl@0: TheLock.CreateLocal(RReadWriteLock::TReadWriteLockPriority(RReadWriteLock::EAlternatePriority)); sl@0: TheLock.WriteLock(); sl@0: TheLock.Unlock(); sl@0: TheLock.Unlock(); sl@0: break; sl@0: default: sl@0: return KErrNone; sl@0: }; sl@0: sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: TBool CreatePanicThread(TInt aTest) sl@0: { sl@0: User::SetJustInTime(EFalse); sl@0: TBool finished = EFalse; sl@0: sl@0: RThread panicThread; sl@0: TInt ret = panicThread.Create(KNullDesC, PanicEntryPoint, KDefaultStackSize, KMinHeapSize, KMinHeapSize, (TAny*)aTest, EOwnerThread); sl@0: Test(ret == KErrNone, __LINE__); sl@0: panicThread.Resume(); sl@0: sl@0: TRequestStatus stat; sl@0: panicThread.Logon(stat); sl@0: User::WaitForRequest(stat); sl@0: User::SetJustInTime(ETrue); sl@0: sl@0: if (panicThread.ExitType() == EExitPanic) sl@0: { sl@0: TInt panicValue = 0; sl@0: switch (aTest) sl@0: { sl@0: case 0: sl@0: case 1: sl@0: panicValue = EReadWriteLockInvalidPriority; sl@0: break; sl@0: case 2: sl@0: case 3: sl@0: panicValue = EReadWriteLockStillPending; sl@0: break; sl@0: case 4: sl@0: case 5: sl@0: case 6: sl@0: panicValue = EReadWriteLockTooManyClients; sl@0: break; sl@0: case 7: sl@0: case 8: sl@0: case 9: sl@0: panicValue = EReadWriteLockBadLockState; sl@0: break; sl@0: default: sl@0: Test(0, __LINE__); sl@0: break; sl@0: }; sl@0: sl@0: Test(stat == panicValue, __LINE__); sl@0: Test(panicThread.ExitReason() == panicValue, __LINE__); sl@0: } sl@0: else sl@0: { sl@0: Test(stat == KErrNone, __LINE__); sl@0: finished = ETrue; sl@0: } sl@0: sl@0: RTest::CloseHandleAndWaitForDestruction(panicThread); sl@0: sl@0: switch (aTest) sl@0: { sl@0: case 2: // Check close while holding read lock sl@0: case 3: // Check close while holding write lock sl@0: TheLock.Unlock(); sl@0: TheLock.Close(); sl@0: break; sl@0: case 4: // Check max readers sl@0: { sl@0: for (TInt count = 0; count < RReadWriteLock::EReadWriteLockClientCategoryLimit; count++) sl@0: TheLock.Unlock(); sl@0: } sl@0: TheLock.Close(); sl@0: break; sl@0: case 5: // Check max pending readers sl@0: case 6: // Check max pending writers sl@0: { sl@0: TUint16* hackLock = (TUint16*)&TheLock; sl@0: hackLock[2] = 0; // Reset readers pending field sl@0: hackLock[3] = 0; // Reset writers pending field sl@0: } sl@0: TheLock.Unlock(); sl@0: TheLock.Close(); sl@0: break; sl@0: case 7: // Check lock held when unlocking sl@0: case 8: // Check lock held when unlocking after read lock/unlock sl@0: case 9: // Check lock held when unlocking after write lock/unlock sl@0: TheLock.Close(); sl@0: break; sl@0: default: sl@0: break; sl@0: }; sl@0: return finished; sl@0: } sl@0: sl@0: // Check that the various asserts guarding invalid conditions can be reached sl@0: void TestPanics() sl@0: { sl@0: Test.Next(_L("Panics")); sl@0: sl@0: for (TInt testIndex = 0; !CreatePanicThread(testIndex); testIndex++) ; sl@0: } sl@0: sl@0: TInt E32Main() sl@0: { sl@0: Test.Title(); sl@0: Test.Start(_L("RReadWriteLock Testing")); sl@0: sl@0: TestCreation(); sl@0: TestWriterPriority(); sl@0: TestAlternatePriority(); sl@0: TestReaderPriority(); sl@0: TestTryLock(); sl@0: TestUpgrade(); sl@0: TestDowngrade(); sl@0: TestPanics(); sl@0: sl@0: Test.End(); sl@0: return KErrNone; sl@0: } sl@0: sl@0: