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