searcher/tsrc/cpixsearchertest/src/multithreadtester.cpp
changeset 0 671dee74050a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/searcher/tsrc/cpixsearchertest/src/multithreadtester.cpp	Mon Apr 19 14:40:16 2010 +0300
@@ -0,0 +1,487 @@
+/*
+* Copyright (c) 2010 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: 
+*
+*/
+#include "multithreadtester.h"
+#include "RSearchServerSession.h"
+#include "CCPixIndexer.h"
+#include "CCPixSearcher.h"
+#include "CSearchDocument.h"
+#include <e32math.h>
+
+#define KIndexingWorkerCount 4
+#define KSearchingWorkerCount 16
+#define KTermDeviation 10 // The bigger this word, the more different term 
+#define KTermCountDeviation 10 // The bigger this word, the more terms per document
+_LIT( KContentsField, "Contents " ); 
+
+class CWorker : public CBase 
+    {
+public: 
+    CWorker() 
+    : iWorkerId( iNextFreeWorkerId++ ), 
+      iActive( EFalse ), 
+      iThread(), 
+      iSemaphore()
+        {
+        }
+
+    void ConstructL() 
+        {
+        User::LeaveIfError( iSemaphore.CreateLocal( 0 ) ); 
+        }
+    
+    ~CWorker() 
+        {
+        if ( iActive ) 
+            {
+            Finish(); 
+            }
+        iSemaphore.Close(); 
+        }
+    
+    TInt WorkerId() 
+        {
+        return iWorkerId; 
+        }
+
+    TInt Error() 
+        {
+        return iError; 
+        }
+
+    virtual const TDesC& Name() = 0;
+    
+    void StartL() 
+        {
+        TThreadFunction function = CWorker::ThreadFunction; 
+        
+        User::LeaveIfError( iThread.Create( Name(), function, 2048, 0, this ) ); 
+         
+        iActive = ETrue;
+        iThread.Resume(); 
+        // Go thread go
+    }
+    
+    void Finish() 
+        {
+        iActive = EFalse;
+        DoCancel(); 
+        iSemaphore.Wait(); 
+        iThread.Close(); 
+        }
+    
+protected: 
+
+    /**
+     * For doing any required preparations in the original thread. 
+     */
+    virtual void DoPrepareL() = 0;
+    virtual void DoCancel() = 0;
+    virtual void DoRunL() = 0;
+
+    void RunL() 
+        {
+        CActiveScheduler* scheduler = new (ELeave) CActiveScheduler();
+        CleanupStack::PushL(scheduler);
+        CActiveScheduler::Install(scheduler);
+
+        TRAP(iError, DoRunL());
+        iSemaphore.Signal(); 
+        
+        CleanupStack::PopAndDestroy(scheduler);
+        }
+    
+public: 
+    
+    static TInt ThreadFunction(void *aThis) 
+        {
+        CTrapCleanup* cleanup = CTrapCleanup::New();
+    
+        TRAP_IGNORE
+            ( 
+            (reinterpret_cast<CWorker*>(aThis))->RunL();
+            ); 
+
+        delete cleanup;
+        return KErrNone;
+        }
+    
+private: 
+    
+    TInt iWorkerId;     
+    TInt iError; 
+    static TInt iNextFreeWorkerId;  
+    TBool iActive; 
+    RThread iThread; 
+    RSemaphore iSemaphore; 
+    };
+
+
+TInt CWorker::iNextFreeWorkerId = 0;  
+
+/**
+ * Returns random numbers so, that small numbers
+ * are most common. Used to approximate the distribution
+ * of real world words. In the distribution the probability that n is 
+ * one of the numbers following i is 9 times as probable as that n is i. 
+ * 
+ * NOTE: Doesn't really follow the real distribution of words, because
+ *       the word distribution follows Zipf's law and is not logarithmic.
+ *       These things won't probably matter too much in any case. 
+ */
+TInt LogRand( TInt aDeviation ) 
+    {
+    TInt rv = 0; 
+    while ( Math::Random() % aDeviation ) rv++;
+    return rv; 
+    }
+
+/**
+ * Worker, which continuously adds items to the index. Operates on its own
+ * thread. 
+ */
+class CIndexingWorker : public CWorker
+    {
+public:
+    CIndexingWorker() 
+    :   iStop( EFalse ),
+        iNextFreeUid( 0 ),
+        iQualAppClass( 0 ),
+        iAppClass( 0 ) 
+        {
+        }
+    
+    static CIndexingWorker* NewL( const TDesC& aAppClass ) 
+        {
+        CIndexingWorker* self = new ( ELeave ) CIndexingWorker(); 
+        CleanupStack::PushL( self ); 
+        self->ConstructL( aAppClass ); 
+        CleanupStack::Pop( self ); 
+        return self; 
+        }
+     
+    void ConstructL( const TDesC& aQualAppClass ) 
+        {
+        CWorker::ConstructL(); 
+        iQualAppClass = aQualAppClass.AllocL();
+        iAppClass = aQualAppClass.Right( aQualAppClass.Length() - aQualAppClass.Find( _L( ":" ) ) - 1 ).AllocL();
+        
+        iName = HBufC::NewL( 512 );
+        iName->Des().Append( _L( "indexer") ); 
+        iName->Des().AppendNum( WorkerId() ); 
+        iName->Des().Append( _L( "_") ); 
+        iName->Des().Append( *iAppClass ); 
+        }
+
+    ~CIndexingWorker() 
+        {
+        delete iName; 
+        delete iAppClass;
+        delete iQualAppClass; 
+        }
+    
+public: // From Cworker
+
+    virtual void DoPrepareL() {
+        iStop = EFalse; 
+    }
+
+    virtual void DoCancel() {
+        iStop = ETrue; 
+    }
+    
+    virtual void DoRunL() 
+        {
+        RSearchServerSession session;
+        User::LeaveIfError( session.Connect() );
+        CleanupClosePushL( session ); 
+        CCPixIndexer* indexer = CCPixIndexer::NewLC( session );
+        indexer->OpenDatabaseL( *iQualAppClass );
+        
+        HBufC* buf = HBufC::NewLC( 1024*8 ); 
+        
+        while ( !iStop ) 
+            {
+            TBuf<64> docuid; 
+            docuid.Append( _L("iw") ); 
+            docuid.AppendNum( WorkerId() ); 
+            docuid.Append( _L("_d") );  
+            docuid.AppendNum( iNextFreeUid++ ); 
+
+            CSearchDocument* doc = CSearchDocument::NewLC( docuid,
+                                                           *iAppClass );
+            
+            buf->Des().Zero(); 
+            TInt n = LogRand( KTermCountDeviation ) + 1; 
+            for ( TInt i = 0; i < n; i++ ) 
+                {
+                buf->Des().AppendNum( LogRand( KTermDeviation ) ); 
+                if ( i + 1 < n ) 
+                    {
+                    buf->Des().Append( _L( " " ) );
+                    }
+                }
+                
+            doc->AddFieldL( KContentsField, *buf, CDocumentField::EStoreYes | 
+                                                  CDocumentField::EIndexTokenized | 
+                                                  CDocumentField::EAggregateNo ); 
+            doc->AddExcerptL( *buf ); 
+            
+            indexer->AddL( *doc ); 
+            
+            CleanupStack::PopAndDestroy( doc ); 
+            }
+        CleanupStack::PopAndDestroy( buf ); 
+        CleanupStack::PopAndDestroy( indexer ); 
+        CleanupStack::PopAndDestroy(); // session 
+        }
+
+    virtual const TDesC& Name() {
+        return *iName; 
+    }
+    
+private: 
+    
+    TBool iStop; 
+    
+    TInt iNextFreeUid; 
+
+    HBufC* iQualAppClass;
+
+    HBufC* iAppClass;
+
+    HBufC* iName;
+
+    };
+
+
+/**
+ * A worker that commits normal searches to the given  search domain. 
+ * Worker operates within its own thread.  
+ */
+class CSearchingWorker : public CWorker
+    {
+public: 
+    CSearchingWorker() 
+    :   iStop( EFalse ),
+        iAppClass( 0 ), 
+        iName( 0 )
+        {
+        }
+    
+    static CSearchingWorker* NewL( const TDesC& aAppClass ) 
+        {
+        CSearchingWorker* self = new ( ELeave ) CSearchingWorker(); 
+        CleanupStack::PushL( self ); 
+        self->ConstructL( aAppClass ); 
+        CleanupStack::Pop( self ); 
+        return self; 
+        }
+
+    
+    void ConstructL( const TDesC& aAppClass ) 
+        {   
+        CWorker::ConstructL(); 
+        
+        iAppClass = aAppClass.AllocL(); 
+
+        iName = HBufC::NewL( 512 );
+        iName->Des().Append( _L( "searcher") ); 
+        iName->Des().AppendNum( WorkerId() ); 
+/*      iName->Des().Append( _L( "_") ); 
+        iName->Des().Append( aAppClass ); */
+        }
+    
+    ~CSearchingWorker() 
+        {
+        delete iName;
+        delete iAppClass; 
+        }
+
+public: // From Cworker
+
+    virtual void DoPrepareL() 
+        {
+        iStop = EFalse; 
+        }
+
+    virtual void DoCancel() 
+        {
+        iStop = ETrue; 
+        }
+    
+    virtual void DoRunL() 
+        {
+        RSearchServerSession session;
+        User::LeaveIfError( session.Connect() );
+        CleanupClosePushL( session ); 
+        CCPixSearcher* searcher = CCPixSearcher::NewLC( session, KContentsField );
+        
+        searcher->OpenDatabaseL( *iAppClass );
+        
+        while ( !iStop ) 
+            {
+            TBuf<64> num; 
+            num.AppendNum( LogRand( KTermDeviation ) );  
+            TInt n = searcher->SearchL( num ); 
+            for ( TInt i = 0; i < n; i++ ) 
+                {
+                TRAP_IGNORE( delete searcher->GetDocumentL( i++ ); ); 
+                }
+            }
+        CleanupStack::PopAndDestroy( searcher ); // session
+        CleanupStack::PopAndDestroy(); // session
+        }
+        
+    virtual const TDesC& Name() 
+        {
+        return *iName; 
+        }
+    
+private:
+    
+    TBool iStop; 
+    HBufC* iAppClass; 
+    HBufC* iName; 
+    };
+
+
+void CTestMultiThreading::setUp() 
+    {
+    }
+
+void CTestMultiThreading::tearDown() 
+    {
+    }
+
+void CTestMultiThreading::PrepareL() 
+    {
+    RSearchServerSession session;
+    User::LeaveIfError( session.Connect() ); 
+    CleanupClosePushL( session ); 
+    
+    // First database has one reader and one or two searchers, what 
+    // is something that can be expected in the actual device
+    User::LeaveIfError
+        (
+        session.DefineVolume( _L( "@df:root test expected" ), 
+                              _L( "c:\\data\\indexing\\indexdb\\root\\test\\expected" ) )
+        ); 
+    
+    // Second database has one reader and one or two searchers, what 
+    // is quite uncommon example and mainly used to stress the search server
+    User::LeaveIfError
+        (
+        session.DefineVolume( _L( "@df:root test busy" ),
+                              _L( "c:\\data\\indexing\\indexdb\\root\\test\\busy" ) )
+        );
+        
+    CCPixIndexer* indexer = CCPixIndexer::NewLC( session );
+    indexer->OpenDatabaseL( _L( "@df:root test expected" ) );
+    indexer->ResetL(); 
+    indexer->OpenDatabaseL( _L( "@df:root test busy" ) );
+    indexer->ResetL(); 
+    CleanupStack::PopAndDestroy( indexer); 
+    CleanupStack::PopAndDestroy(); // session  
+
+    // 
+    // Indexers
+    // 
+    
+    // Indexer with one's own index
+    iWorkers->Append( CIndexingWorker::NewL( _L( "@df:root test expected" ) ) );
+    
+    // 3 indexers in the same index
+    iWorkers->Append( CIndexingWorker::NewL( _L( "@df:root test busy" ) ) );
+    iWorkers->Append( CIndexingWorker::NewL( _L( "@df:root test busy" ) ) );
+    iWorkers->Append( CIndexingWorker::NewL( _L( "@df:root test busy" ) ) );
+    
+    // = 4 indexers in total 
+    
+    // 
+    // Searchers
+    // 
+    
+    // Multisearcher
+    iWorkers->Append( CSearchingWorker::NewL( _L( "root test" ) ) );
+
+    // Search qualified index
+    iWorkers->Append( CSearchingWorker::NewL( _L( "@df:root test expected") ) );
+
+    // Search big the busy index
+    iWorkers->Append( CSearchingWorker::NewL( _L( "@df:root test busy" ) ) );
+    iWorkers->Append( CSearchingWorker::NewL( _L( "@df:root test busy" ) ) );
+    iWorkers->Append( CSearchingWorker::NewL( _L( "root test busy" ) ) );
+    
+    // = 5 searchers in total
+    }
+
+void CTestMultiThreading::Wait(TInt aSeconds) 
+    {
+    RTimer timer; 
+    TRequestStatus timerStatus; 
+    timer.CreateLocal();        
+    CleanupClosePushL( timer ); 
+    
+    timer.After( timerStatus, aSeconds * 1000 * 1000 ); 
+    User::WaitForRequest(timerStatus); 
+    
+    CleanupStack::PopAndDestroy(); // timer 
+    }
+
+void CTestMultiThreading::RunL() 
+    {
+    for ( TInt i = 0; i < iWorkers->Count(); i++ ) 
+        {
+        (*iWorkers)[i]->StartL(); 
+        }
+    
+    Wait( 5 ); // Wait for 5 seconds
+    
+    for ( TInt i = 0; i < iWorkers->Count(); i++ ) 
+        {
+        (*iWorkers)[i]->Finish(); 
+        if ( (*iWorkers)[i]->Error() != KErrNone ) 
+            {
+            TBuf8<512> msg; 
+            const TDesC& name = (*iWorkers)[i]->Name();
+            for ( TInt c = 0; c < name.Length(); c++ ) {
+                msg.Append( name[c] ); 
+            }
+            msg.Append( _L8( " failed with" ) );
+            msg.AppendNum( (*iWorkers)[i]->Error() );
+            //TS_FAIL( reinterpret_cast<const char*>( msg.PtrZ() ) ); 
+            }
+        }
+    }
+
+void CTestMultiThreading::Finish() 
+    {
+    iWorkers->ResetAndDestroy();
+    }
+
+void CTestMultiThreading::DoTestMultiThreadingL() 
+    {
+    iWorkers = new ( ELeave ) RPointerArray<CWorker>(); 
+    CleanupStack::PushL( iWorkers ); 
+    CleanupClosePushL( *iWorkers ); 
+    PrepareL(); 
+    TRAPD( err, RunL() ); 
+    Finish(); 
+    User::LeaveIfError( err ); 
+    CleanupStack::PopAndDestroy( iWorkers ); 
+    CleanupStack::PopAndDestroy(); // iWorkers  
+    }