phonebookengines/contactsmodel/tsrc/t_IccImportLock.cpp
changeset 0 e686773b3f54
child 24 0ba2181d7c28
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/phonebookengines/contactsmodel/tsrc/t_IccImportLock.cpp	Tue Feb 02 10:12:17 2010 +0200
@@ -0,0 +1,689 @@
+// Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "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:
+//
+
+
+// System includes
+#include <cntdb.h>
+#include <cntdef.h>
+#include <cntitem.h>
+#include <cntfield.h>
+#include <cntfldst.h>
+
+// System includes
+#include <e32std.h>
+#include <e32twin.h>
+#include <e32test.h>
+#include <cntdbobs.h>
+#include <e32math.h>
+
+// User includes
+#include "T_UTILS.H"
+#include "t_utils2.h"
+
+// Constant
+_LIT(KDatabaseFileName, "c:INC016848.cdb");
+
+_LIT(KSemaphoreNameOne,		"One");
+_LIT(KSemaphoreNameTwo,		"Two");
+
+_LIT(KThreadNameOne,		"OneThread ICC");
+_LIT(KThreadNameTwo,		"TwoThread Group");
+
+
+const TInt KNumberOfGroupInserts = 200;
+const TInt KNumberOfInserts = 9*KNumberOfGroupInserts/4;
+
+const TInt KStandardTimeOut = 50000;
+
+
+static RTest					TheTest(_L("INC016848 - DB stressing - Group insert"));
+static RFs						TheFs;
+
+//
+/* CTestActiveScheduler                                                      */
+//  This class has been nicked from the t_currentdb.h / .cpp files
+class CTestActiveScheduler : public CActiveScheduler
+	{
+	public:
+		void Error (TInt aError) const;
+	};
+void CTestActiveScheduler::Error(TInt aError) const
+	{
+	User::Panic(_L("AScheduler"),aError);
+	}
+
+
+LOCAL_C void CleanupFilesL()
+	{
+    // delete the database file
+	if (CContactDatabase::ContactDatabaseExistsL(KDatabaseFileName) )
+		{
+		CContactDatabase::DeleteDatabaseL(KDatabaseFileName);
+		}
+	}
+
+
+//
+/* CConcurrentDatabaseAccessBase - Specification                             */
+// Forms the base class for both threads.
+class CConcurrentDatabaseAccessBase : public CBase
+	{
+	public:
+		CConcurrentDatabaseAccessBase();
+		virtual ~CConcurrentDatabaseAccessBase();
+
+		virtual void ConstructL(const TDesC& aFilename);
+
+		void RunTestL();
+
+
+	public: // data
+		RThread					iThread;
+
+	protected:
+		void OpenSemaphore();
+		void CloseSemaphore();
+		void SyncronizeSignal();
+		void SyncronizeWait();
+		void OpenDatabaseL();
+		void CloseDatabase();
+		void SetUpTestL(const TDesC& aThreadName);
+		void CloseTest();
+
+		void OpenLogFileL(const TDesC& aFilename);
+		void CloseLogFile();
+
+		virtual void RunTestImplementationL() = 0;
+		void MoveWindow(TInt aDx);
+
+	protected:
+		CContactDatabase*		iDb;
+		//CConsoleBase*			iConsole;
+		RTest*					iTest;
+		RFile					iFile;
+
+
+	private:
+		RSemaphore				iSemaphoreSignal;
+		RSemaphore				iSemaphoreWait;
+		TBool					iSemaphoreOpen;
+		HBufC*					iDatabaseName;
+		CTestRegister* 			iFileRegister;
+	};
+
+//
+/* CConcurrentDatabaseAccessBase - Implementation                            */
+CConcurrentDatabaseAccessBase::CConcurrentDatabaseAccessBase()
+	{
+	}
+
+CConcurrentDatabaseAccessBase::~CConcurrentDatabaseAccessBase()
+	{
+	iThread.Close();
+	CloseSemaphore();
+	delete iDatabaseName;
+	if (iFileRegister)
+		delete iFileRegister;
+	}
+
+void CConcurrentDatabaseAccessBase::ConstructL(const TDesC& aFilename)
+	{
+	iDatabaseName = aFilename.AllocL();
+	}
+
+void CConcurrentDatabaseAccessBase::OpenDatabaseL()
+	{
+	iDb = CContactDatabase::OpenL(*iDatabaseName, CContactDatabase::EMultiThread);
+	}
+
+void CConcurrentDatabaseAccessBase::CloseDatabase()
+	{
+	delete iDb;
+	iDb = NULL;
+	}
+
+void CConcurrentDatabaseAccessBase::SetUpTestL(const TDesC& aThreadName)
+	{
+	// Set's up extra resources.
+	CConsoleBase* console = NULL;
+	TSize size;
+
+	// Create and name an RTest
+	iTest = new(ELeave) RTest(aThreadName);
+	iTest->Start(_L("Starting test"));
+
+	// Position our console window
+	size = iTest->Console()->ScreenSize();
+	size.iWidth = size.iWidth / 2 - 2;
+	size.iHeight = size.iHeight - 3;
+
+	console = Console::NewL(aThreadName, size);
+	delete const_cast<RTest*>(iTest)->Console();
+
+	iTest->SetConsole(console);
+	console->ClearScreen();
+	}
+
+void CConcurrentDatabaseAccessBase::CloseTest()
+	{
+	iTest->Close();
+	delete iTest;
+	iTest = NULL;
+	}
+
+void CConcurrentDatabaseAccessBase::OpenLogFileL(const TDesC& aFilename)
+	{
+	if (iFileRegister)
+		{
+		delete iFileRegister;
+		iFileRegister = NULL;
+		}
+	iFileRegister = CTestRegister::NewLC();
+	CleanupStack::Pop(iFileRegister);
+
+	// Open Debug File
+	iFileRegister->CreateLogFileLC(iFile, aFilename);
+	CleanupStack::Pop(&iFile);
+	}
+
+void CConcurrentDatabaseAccessBase::CloseLogFile()
+	{
+	iFile.Close();
+	delete iFileRegister;
+	iFileRegister = NULL;
+	}
+
+
+void CConcurrentDatabaseAccessBase::RunTestL()
+	{
+	// set's up an active scheduler for the thread
+	// and calls the RunTesImplementation function to actually
+	// preform the tests. This function should be implemented in the
+	// child class.
+	CTestActiveScheduler*  scheduler = NULL;
+
+	scheduler = new (ELeave) CTestActiveScheduler;
+	CleanupStack::PushL(scheduler);
+	CActiveScheduler::Install(scheduler);
+
+	OpenDatabaseL();
+
+	RunTestImplementationL();
+
+	CloseDatabase();
+
+	CleanupStack::PopAndDestroy( scheduler );
+	}
+
+void CConcurrentDatabaseAccessBase::OpenSemaphore()
+	{
+	TInt success = KErrNone;
+
+	success = iSemaphoreSignal.OpenGlobal( KSemaphoreNameOne );
+	if ( success == KErrNotFound )
+		{
+		iSemaphoreSignal.CreateGlobal( KSemaphoreNameOne, 0 );
+		success = KErrNone;
+		}
+
+
+	success = iSemaphoreWait.OpenGlobal( KSemaphoreNameTwo );
+	if ( success == KErrNotFound )
+		{
+		iSemaphoreWait.CreateGlobal( KSemaphoreNameTwo, 0 );
+		}
+
+	iSemaphoreOpen = ETrue;
+	}
+
+void CConcurrentDatabaseAccessBase::CloseSemaphore()
+	{
+	if (iSemaphoreOpen)
+		{
+		iSemaphoreSignal.Close();
+		iSemaphoreWait.Close();
+		}
+
+	iSemaphoreOpen = EFalse;
+	}
+
+void CConcurrentDatabaseAccessBase::SyncronizeSignal()
+	{
+	iSemaphoreSignal.Signal();
+	iSemaphoreWait.Wait();
+	}
+
+void CConcurrentDatabaseAccessBase::SyncronizeWait()
+	{
+	iSemaphoreSignal.Wait();
+	iSemaphoreWait.Signal();
+	}
+
+
+void CConcurrentDatabaseAccessBase::MoveWindow(TInt aDx)
+	{
+	const TInt scrHgtChars=iTest->Console()->ScreenSize().iHeight;
+	TMachineInfoV1Buf machineBuf;
+	UserHal::MachineInfo(machineBuf);
+	const TMachineInfoV1& machineInfo=machineBuf();
+	const TSize scrSizePixels=machineInfo.iDisplaySizeInPixels;
+	const TInt xCentre=scrSizePixels.iWidth/2;
+	const TInt xStart=scrSizePixels.iWidth/8;
+	const TInt xEnd=3*scrSizePixels.iWidth/8;
+	const TInt xMoveBy=(xEnd-xStart)/3;
+	const TInt yCentre=scrSizePixels.iHeight/2;
+	const TInt yEnd=yCentre+scrSizePixels.iHeight/scrHgtChars;
+	TRawEvent event;
+	event.Set(TRawEvent::EButton1Down,xCentre+aDx*xStart,yCentre);
+	UserSvr::AddEvent(event);
+	event.Set(TRawEvent::EPointerMove,xCentre+aDx*(xStart+xMoveBy),yEnd);
+	UserSvr::AddEvent(event);
+	event.Set(TRawEvent::EPointerMove,xCentre+aDx*(xEnd-xMoveBy),yEnd);
+	UserSvr::AddEvent(event);
+	/*event.Set(TRawEvent::EPointerMove,xCentre+aDx*xEnd,yEnd);
+	UserSvr::AddEvent(event);*/
+	event.Set(TRawEvent::EButton1Up,xCentre+aDx*xEnd,yEnd);
+	UserSvr::AddEvent(event);
+	}
+
+
+//
+/* CConcurrentDatabaseInserter - Specification                               */
+// This class is the inserter thread. It's thread function should insert
+// contacts into the database using the t_utils CRandomContactGenerator class
+class CConcurrentDatabaseInserter : public CConcurrentDatabaseAccessBase
+	{
+	public:
+		CConcurrentDatabaseInserter();
+		~CConcurrentDatabaseInserter();
+
+		static CConcurrentDatabaseInserter* NewLC(const TDesC& aFilename);
+		virtual void ConstructL(const TDesC& aFilename);
+
+		static TInt ThreadFunction(TAny* aSelf);
+
+
+	protected:
+		void	RunTestImplementationL();
+
+	private:
+	};
+
+//
+/* CConcurrentDatabaseInserter - Implementation                              */
+
+CConcurrentDatabaseInserter* CConcurrentDatabaseInserter::NewLC(const TDesC& aFilename)
+	{
+	CConcurrentDatabaseInserter* self = NULL;
+	self = new (ELeave) CConcurrentDatabaseInserter();
+	CleanupStack::PushL( self );
+	self->ConstructL(aFilename);
+	return self;
+	}
+
+CConcurrentDatabaseInserter::CConcurrentDatabaseInserter()
+	{
+	}
+
+CConcurrentDatabaseInserter::~CConcurrentDatabaseInserter()
+	{
+	}
+
+TInt CConcurrentDatabaseInserter::ThreadFunction(TAny* aSelf)
+	{
+	CConcurrentDatabaseInserter* self = STATIC_CAST(CConcurrentDatabaseInserter*, aSelf);
+
+	// Prepare the stuff required before we start the
+	// active scheduler.
+	TInt error = KErrNone;
+    CTrapCleanup* cleanup = CTrapCleanup::New();
+	if	(!cleanup)
+		return KErrNoMemory;
+
+	// Call virtual handler which does anything that needs doing as
+	// a result of the thread function from being created.
+	TRAP(error, self->RunTestL());
+
+
+    delete cleanup;
+	return error;
+	}
+
+void CConcurrentDatabaseInserter::ConstructL(const TDesC& aFilename)
+	{
+	CConcurrentDatabaseAccessBase::ConstructL(aFilename);
+
+	iThread.Create( KThreadNameOne, CConcurrentDatabaseInserter::ThreadFunction, KDefaultStackSize, 0x2000, 0x20000, this, EOwnerThread );
+	iThread.Resume();
+	iThread.SetPriority(EPriorityLess);
+	}
+
+
+void CConcurrentDatabaseInserter::RunTestImplementationL()
+	{
+	TInt retries = 0;
+	TInt counter = 0;
+	TInt errorCount = 0;
+	TInt bit = 0;
+	TInt leaveCode = KErrNone;
+	HBufC8* fileBuffer = HBufC8::NewL( 256 );
+	CleanupStack::PushL( fileBuffer );
+	TPtr8 ptr = fileBuffer->Des();
+
+	CRandomContactGenerator* generator = NULL;
+
+	SetUpTestL(KThreadNameOne());
+
+	generator = CRandomContactGenerator::NewL();
+	CleanupStack::PushL( generator );
+	generator->SetDbL(*iDb);
+	OpenLogFileL(_L("CConcurrentDatabaseInserter.txt"));
+
+	bit|= CContactDatabase::ESmsable;
+
+	OpenSemaphore();
+	SyncronizeWait(); // pause until corresponding Searcher thread is ready.
+
+	SyncronizeWait(); // Wait while other thread moves there window
+	MoveWindow(1);
+	SyncronizeWait(); // Tell other thread our window has moved
+
+	for (counter = 0; counter < KNumberOfInserts ; counter++)
+		{
+
+		retries = 0;
+
+		do
+			{
+			leaveCode = KErrNone;
+			retries++;
+			generator->AddTypicalContactForFilterL(bit, leaveCode);
+			iTest->Printf(_L("Insert contact %d Error=%d\n"), counter, leaveCode );
+			if ( leaveCode != KErrNone )
+				{
+				errorCount ++;
+				User::After( KStandardTimeOut * retries );
+				}
+			}
+		while ( leaveCode != KErrNone );
+
+		User::After( KStandardTimeOut ); // delay to allow second thread access.
+		}
+
+	iTest->Printf(_L("Insert contacts errorCount (retries) = %d\n"), errorCount );
+
+	CleanupStack::PopAndDestroy( generator );
+	CleanupStack::PopAndDestroy( fileBuffer );
+	ptr.Set(NULL, 0, 0);
+
+	iTest->Printf(_L("Finished, waiting for the other thread to stop\n"));
+	SyncronizeWait();
+	CloseSemaphore();
+	CloseLogFile();
+	CloseTest();
+	}
+
+//
+/* CConcurrentDBGroupInserter                                               */
+class CConcurrentDBGroupInserter : public CConcurrentDatabaseAccessBase
+	{
+	public:
+		CConcurrentDBGroupInserter();
+		~CConcurrentDBGroupInserter();
+
+		static CConcurrentDBGroupInserter* NewLC(const TDesC& aFilename);
+		virtual void ConstructL(const TDesC& aFilename);
+		static TInt ThreadFunction(TAny* aSelf);
+		static TInt FakeFunction(TAny *aParams);
+
+
+
+	protected:
+		void	RunTestImplementationL();
+
+	private:
+	};
+
+CConcurrentDBGroupInserter* CConcurrentDBGroupInserter::NewLC(const TDesC& aFilename)
+	{
+	CConcurrentDBGroupInserter* self = NULL;
+	self = new (ELeave) CConcurrentDBGroupInserter();
+	CleanupStack::PushL( self );
+	self->ConstructL(aFilename);
+	return self;
+	}
+
+CConcurrentDBGroupInserter::CConcurrentDBGroupInserter()
+	{
+	}
+
+CConcurrentDBGroupInserter::~CConcurrentDBGroupInserter()
+	{
+	}
+
+TInt CConcurrentDBGroupInserter::ThreadFunction(TAny* aSelf)
+	{
+	CConcurrentDBGroupInserter* self = static_cast<CConcurrentDBGroupInserter*>(aSelf);
+
+	// Prepare the stuff required before we start the
+	// active scheduler.
+	TInt error = KErrNone;
+
+    CTrapCleanup* cleanup = CTrapCleanup::New();
+	if	(!cleanup)
+		return KErrNoMemory;
+
+	// Call virtual handler which does anything that needs doing as
+	// a result of the thread function from being created.
+	TRAP(error, self->RunTestL());
+
+
+    delete cleanup;
+	return error;
+	}
+
+void CConcurrentDBGroupInserter::ConstructL(const TDesC& aFilename)
+	{
+	CConcurrentDatabaseAccessBase::ConstructL(aFilename);
+
+	iThread.Create( KThreadNameTwo, CConcurrentDBGroupInserter::ThreadFunction, KDefaultStackSize, 0x2000, 0x20000, this, EOwnerThread );
+	iThread.Resume();
+	iThread.SetPriority(EPriorityNormal);
+
+	}
+
+
+TInt CConcurrentDBGroupInserter::FakeFunction(TAny* /*aParams*/)
+	{
+	return(KErrNone);
+	}
+
+void CConcurrentDBGroupInserter::RunTestImplementationL()
+	{
+	// Prep, and get ready to run. Then before starting the search loop
+	// wait for the other thread.
+	_LIT(KGroupName, "GroupName[%d]");
+	TInt counter = 0;
+	TInt errorCount = 0;
+	TInt error = 0;
+	CContactItem* item = NULL;
+	TBuf<80> groupNameBuffer;
+
+	SetUpTestL(KThreadNameTwo());
+	OpenSemaphore();
+	SyncronizeSignal(); // wait for the other thread.
+
+	MoveWindow(-1);
+	SyncronizeSignal();	// Tell other thread our window has moved
+	SyncronizeSignal(); // Wait until other thread has moved it's window
+
+	User::After( KStandardTimeOut ); // simulate user delay,
+
+	const TInt KDelayIncrease=10000;
+	const TInt KDelayMinimum=10000;
+	const TInt KIncreaseEvery=4;
+	TInt delay=KDelayMinimum;
+	for (counter = 0; counter < KNumberOfGroupInserts; counter++)
+		{
+		User::After(delay);
+		groupNameBuffer.Format( KGroupName, counter );
+
+		FOREVER
+			{
+			// Trap the search, and display the error code on the screen
+			TRAP(error,
+				{
+				item = iDb->CreateContactGroupLC( KGroupName, /* t/f ? */ EFalse );
+				CleanupStack::PopAndDestroy( item );
+				item = NULL;
+				});
+
+			iTest->Printf(_L("Insert Group %d Error=%d\n"), counter, error );
+			if( error == KErrNone )
+				break;
+
+			errorCount ++;
+			// bail for any error other than Locked
+			if (error != KErrLocked)
+				{
+				TRAP_IGNORE(CleanupFilesL());
+				}
+			(*iTest)( error == KErrLocked );
+			}
+
+		if (counter%KIncreaseEvery==KIncreaseEvery-1)
+			delay+=KDelayIncrease;
+		}
+
+	iTest->Printf(_L("Create contact groups errorCount (retries) = %d\n"), errorCount );
+
+	iTest->Printf(_L("Finished, waiting for the other thread to stop\n"));
+
+//The errorCount counts the number of times the call to CreateContactGroupLC above returns
+//KErrLocked. On hardware it is expected that KErrLocked will occur when a write is attempted and the
+//other thread has a lock on the database.  Due to the Wins threading model this should be 0 on Wins.
+//This harness can therefore be used as a benchmark on harware and the number of KErrLocked
+//contentions observed manually.
+#ifdef __WINS__
+	if (errorCount != 0)
+		{
+		TRAP_IGNORE(CleanupFilesL());
+		}
+	(*iTest)( errorCount == 0 );
+#endif
+	SyncronizeSignal();
+	CloseSemaphore();
+	CloseTest();
+	}
+
+//
+/* Test Function Prototypes                                                  */
+//
+void CreateTestDatabaseL(const TDesC& aFilename );
+void TestL();
+void doMainL();
+
+//
+/* Test Function Implementations                                             */
+//
+void CreateTestDatabaseL(const TDesC& aFilename )
+	{
+	CContactDatabase* database = NULL;
+
+	database = CContactDatabase::ReplaceL(aFilename);
+	CleanupStack::PushL( database );
+
+	CleanupStack::PopAndDestroy( database );
+	}
+
+
+void TestL()
+	{
+	TRequestStatus groupThread;
+	TRequestStatus inserterThread;
+	CConcurrentDBGroupInserter* groupInserter = NULL;
+	CConcurrentDatabaseInserter* inserter = NULL;
+	TCntProfile profile[1];
+
+	CreateTestDatabaseL( KDatabaseFileName );
+
+	CCntTest::ProfileStart(0);
+
+	groupInserter = CConcurrentDBGroupInserter::NewLC( KDatabaseFileName );
+	inserter = CConcurrentDatabaseInserter::NewLC( KDatabaseFileName );
+
+	groupInserter->iThread.Logon(groupThread);
+	inserter->iThread.Logon(inserterThread);
+
+	User::WaitForRequest(groupThread);
+	User::WaitForRequest(inserterThread);
+
+	CCntTest::ProfileEnd(0);
+	CCntTest::ProfileResult(profile, 0, 1);
+
+	RDebug::Print(_L("Time taken (secs) %f\n"), profile[0].iTime/1000000.0);
+
+	CleanupStack::PopAndDestroy( inserter );
+	CleanupStack::PopAndDestroy( groupInserter );
+
+	}
+
+
+//
+// -------> Static global functions (source)
+//
+void doMainL()
+	{
+	User::LeaveIfError( TheFs.Connect() );
+	CleanupClosePushL(TheFs);
+
+	CTestRegister* TempFiles = CTestRegister::NewLC();
+	TempFiles->RegisterL(KDatabaseFileName, EFileTypeCnt);
+
+	// Delete any existing ini file so that we can be sure this test is ok
+	TestL();
+
+	CleanupStack::PopAndDestroy(2, &TheFs );
+
+	}
+
+/**
+
+@SYMTestCaseID     PIM-T-ICCIMPORTLOCK-0001
+
+*/
+
+GLDEF_C TInt E32Main()
+	{
+	CTestActiveScheduler*  scheduler = new CTestActiveScheduler;
+	CActiveScheduler::Install(scheduler);
+	CTrapCleanup* cleanupStack = NULL;
+	__UHEAP_MARK;
+	TheTest.Start(_L("@SYMTESTCaseID:PIM-T-ICCIMPORTLOCK-0001 Multi session testcode"));
+
+	TheTest.Title();
+	cleanupStack = CTrapCleanup::New();
+	TRAPD(ret, doMainL());
+    // ensure files are cleaned up even if doMainL() leaves
+    TRAP_IGNORE(CleanupFilesL() );
+	TheTest(ret == KErrNone);
+	delete cleanupStack;
+	delete scheduler;
+
+	TheTest.End();
+	TheTest.Close();
+	__UHEAP_MARKEND;
+	return(KErrNone);
+	}