diff -r 000000000000 -r e686773b3f54 phonebookengines/contactsmodel/tsrc/t_IccImportLock.cpp --- /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 +#include +#include +#include +#include + +// System includes +#include +#include +#include +#include +#include + +// 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(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(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); + }