kerneltest/e32test/prime/t_rwlock.cpp
changeset 0 a41df078684a
--- /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 <e32atomics.h>
+#include <e32test.h>
+#include <e32panic.h>
+#include <e32def.h>
+#include <e32def_private.h>
+
+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;
+    }
+
+