--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/dbms/tdbms/t_dbthrd.cpp Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,399 @@
+// Copyright (c) 1998-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:
+//
+
+#include "t_dbstress.h"
+
+const TInt KCashLimit=500;
+const TInt KOverdraftLimit=100;
+
+const TInt KMinHeap=0x2000;
+const TInt KMaxHeap=0x20000;
+#ifdef _DEBUG
+const TInt KAllocFailRate=1000;
+#endif
+
+LOCAL_D TBuf<256> Sql;
+LOCAL_D TBuf8<256> LogBuf;
+
+class CThread : public CBase
+ {
+public:
+ static TInt Entry(TAny*);
+protected:
+ CThread() {}
+ ~CThread();
+private:
+ static void EntryL();
+ void GoL();
+ void ConstructL();
+ void OpenViewsL();
+ void RecoverL();
+ void CompactL();
+ void Rollback();
+ void Reset();
+ void WorkL();
+ void TransactionL();
+ void AccountL(TInt anAccount);
+ void StatementTimeL();
+ void VerifyL();
+//
+ void LogSize();
+private:
+ RFs iFs;
+ RFile iLog;
+ CFileStore* iStore;
+ RDbStoreDatabase iData;
+ TBool iOpen;
+ RDbView iAccs;
+ RDbView iTrans;
+ TInt iError;
+ TInt iPoint;
+ };
+
+GLDEF_C TInt StartThread(RThread& aThread,TRequestStatus& aStat)
+ {
+ TInt r=aThread.Create(_L("Thread"),CThread::Entry,KDefaultStackSize,KMinHeap,KMaxHeap,NULL);
+ if (r==KErrNone)
+ {
+ aThread.SetPriority(EPriorityLess);
+ aThread.Logon(aStat);
+ aThread.Resume();
+ RunTimer.Start();
+ }
+ return r;
+ }
+
+TInt CThread::Entry(TAny*)
+ {
+ CTrapCleanup* cleanup=CTrapCleanup::New();
+ if (cleanup==NULL)
+ return KErrNoMemory;
+ TRAPD(r,EntryL());
+ delete cleanup;
+ return r;
+ }
+
+void CThread::EntryL()
+ {
+ CThread* self=new(ELeave) CThread;
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ self->GoL(); // never returns
+ }
+
+CThread::~CThread()
+ {//The destructor is never ever executed! See CThread::EntryL() for details!
+ iAccs.Close();
+ iTrans.Close();
+ iData.Close();
+ delete iStore;
+ iLog.Close();
+ TInt err = iFs.Delete(KLogFile);
+ if(err != KErrNone)
+ {
+ RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err, &KLogFile);
+ }
+ iFs.Close();
+ }
+
+void CThread::ConstructL()
+ {
+ User::LeaveIfError(iFs.Connect());
+ User::LeaveIfError(iFs.SetSessionPath(KTestDir));
+ User::LeaveIfError(iLog.Replace(iFs,KLogFile,EFileWrite|EFileStreamText));
+ iStore=CFileStore::OpenL(iFs,KTestDatabase,EFileRead|EFileWrite);
+ LogSize();
+ iData.OpenL(iStore,iStore->Root());
+ }
+
+//
+// Never exits
+//
+void CThread::GoL()
+ {
+ __UHEAP_SETFAIL(RHeap::ETrueRandom,KAllocFailRate);
+ for (;;)
+ {
+ TRAPD(r,WorkL());
+ NewCount=OldCount;
+ LogBuf.Format(_L8(" *** Point %d with code %d"),iPoint,r);
+ iLog.Write(LogBuf);
+ LogSize();
+ iError=r;
+ Rollback();
+ LogSize();
+ if (r==KErrDiskFull)
+ User::Leave(r);
+ }
+ }
+
+//
+// Report the file size
+//
+void CThread::LogSize()
+ {
+ TInt size;
+ if (iStore->File().Size(size)==KErrNone)
+ {
+ LogBuf.Format(_L8("\nFile size=%d"),size);
+ iLog.Write(LogBuf);
+ }
+ }
+
+void CThread::Rollback()
+ {
+ if (iOpen)
+ {
+ iLog.Write(_L8("\nCancel"));
+ iAccs.Cancel();
+ iTrans.Cancel();
+ }
+ if (iData.InTransaction())
+ {
+ iLog.Write(_L8("\nRollback"));
+ iData.Rollback();
+ }
+ }
+
+void CThread::Reset()
+ {
+ iLog.Write(_L8("\nReset"));
+ test(iOpen);
+ iAccs.Reset();
+ iTrans.Reset();
+ }
+
+void CThread::RecoverL()
+ {
+ iPoint=70;
+ User::LeaveIfError(iLog.Write(_L8("\nRecovering")));
+ if (iOpen)
+ {
+ iAccs.Close();
+ iTrans.Close();
+ iOpen=EFalse;
+ }
+ User::LeaveIfError(iData.Recover());
+ }
+
+void CThread::CompactL()
+ {
+ iPoint=90;
+ User::LeaveIfError(iLog.Write(_L8("\nCompacting")));
+ TInt b=iStore->ReclaimL();
+ b-=iStore->CompactL();
+ iStore->CommitL();
+ LogBuf.Format(_L8(": %d bytes reclaimed"),b);
+ User::LeaveIfError(iLog.Write(LogBuf));
+ }
+
+void CThread::OpenViewsL()
+ {
+ iPoint=80;
+ User::LeaveIfError(iLog.Write(_L8("\nOpening")));
+ User::LeaveIfError(iAccs.Prepare(iData,_L("select id,balance,statement_balance from accounts")));
+ TInt r=iTrans.Prepare(iData,_L("select t_date,from_id,to_id,amount from transactions"));
+ if (r!=KErrNone)
+ {
+ iAccs.Close();
+ User::Leave(r);
+ }
+ iOpen=ETrue;
+ }
+
+void CThread::WorkL()
+ {
+ iPoint=0;
+ switch (iError)
+ {
+ case KErrDied:
+ Rollback();
+ break;
+ case KErrNotReady:
+ Reset();
+ break;
+ case KErrCorrupt:
+ RecoverL();
+ break;
+ }
+ iPoint=1;
+ for (;;)
+ {
+ LogSize();
+ if (!iOpen)
+ OpenViewsL();
+ switch (Random(100))
+ {
+ case 0: case 1:
+ StatementTimeL();
+ break;
+ case 2: case 3:
+ RecoverL();
+ break;
+ case 4: // case 5: case 6: case 7: case 8: case 9:
+ CompactL();
+ break;
+ default:
+ TransactionL();
+ break;
+ }
+ }
+ }
+
+void CThread::AccountL(TInt anAccount)
+ {
+ Sql.Format(_L("id=%d"),anAccount);
+ iAccs.FirstL();
+ test(iAccs.FindL(iAccs.EForwards,Sql)>=0);
+ }
+
+//
+// generate and add a single transaction
+//
+void CThread::TransactionL()
+ {
+ iPoint=2;
+ User::LeaveIfError(iLog.Write(_L8("\nTransaction")));
+ TInt from;
+ if (Random(3)==0)
+ from=ECash;
+ else
+ from=Random(KAccountIDs);
+ TInt to=(Random(KAccountIDs-1)+from+1)%KAccountIDs;
+ AccountL(from);
+ iAccs.GetL();
+ test(iAccs.ColInt(1)==from);
+ TInt avail=iAccs.ColInt(2)+KOverdraftLimit;
+ TInt amount;
+ if (to==ECash)
+ {
+ if (avail<10)
+ return;
+ amount=10*(1+Random(Min(avail,KCashLimit)/10));
+ }
+ else
+ {
+ amount=1+Random(100);
+ if (Random(100)<5)
+ amount*=10;
+ if (Random(100)<5)
+ amount*=10;
+ while (amount>avail)
+ amount/=4;
+ if (amount==0)
+ return;
+ }
+ iPoint=3;
+ LogBuf.Format(_L8(" %08d: %d -> %d, %5d"),++TransId,from,to,amount);
+ User::LeaveIfError(iLog.Write(LogBuf));
+ iPoint=4;
+ iData.Begin();
+ iTrans.InsertL();
+ iTrans.SetColL(1,TransId);
+ iTrans.SetColL(2,from);
+ iTrans.SetColL(3,to);
+ iTrans.SetColL(4,amount);
+ iPoint=5;
+ iTrans.PutL();
+ iPoint=6;
+ iAccs.UpdateL(); // from
+ TInt frombalance=iAccs.ColInt(2)-amount;
+ iAccs.SetColL(2,frombalance);
+ iPoint=7;
+ iAccs.PutL();
+ iPoint=8;
+ AccountL(to);
+ iPoint=9;
+ iAccs.UpdateL(); // to
+ TInt tobalance=iAccs.ColInt(2)+amount;
+ iAccs.SetColL(2,tobalance);
+ iPoint=10;
+ iAccs.PutL();
+ iPoint=11;
+// this will invoke commit, so update counts now
+ NewCount=OldCount+1;
+ TInt r=iData.Commit();
+ if (r!=KErrNone)
+ {
+ NewCount=OldCount;
+ User::Leave(r);
+ }
+// succeeded
+ OldCount=NewCount;
+ LogBuf.Format(_L8("; [%d,%d]"),frombalance,tobalance);
+ iLog.Write(LogBuf);
+ }
+
+//
+// deliver statements
+//
+void CThread::StatementTimeL()
+ {
+ iPoint=50;
+ User::LeaveIfError(iLog.Write(_L8("\nStatement")));
+ iPoint=51;
+ VerifyL();
+ // discard transactions
+ iPoint=52;
+ iData.Begin();
+ for (iTrans.BeginningL();iTrans.NextL();)
+ iTrans.DeleteL();
+ for (iAccs.BeginningL();iAccs.NextL();)
+ {
+ iAccs.UpdateL();
+ iAccs.SetColL(3,iAccs.ColInt(2)); // set statement balance
+ iAccs.PutL();
+ }
+ iPoint=53;
+ NewCount=0;
+ TInt r=iData.Commit();
+ if (r!=KErrNone)
+ {
+ NewCount=OldCount;
+ User::Leave(r);
+ }
+ OldCount=NewCount;
+ }
+
+void CThread::VerifyL()
+ {
+ TInt balance[KAccountIDs];
+ test(iAccs.CountL()==KAccountIDs);
+ for (iAccs.BeginningL();iAccs.NextL();)
+ {
+ iAccs.GetL();
+ TInt id=iAccs.ColInt(1);
+ balance[id]=iAccs.ColInt(3);
+ }
+ TInt transact=0;
+ for (iTrans.BeginningL();iTrans.NextL();)
+ {
+ ++transact;
+ iTrans.GetL();
+ TInt from=iTrans.ColInt(2);
+ TInt to=iTrans.ColInt(3);
+ TInt amount=iTrans.ColInt(4);
+ balance[from]-=amount;
+ balance[to]+=amount;
+ }
+ test(transact==iTrans.CountL());
+ for (iAccs.BeginningL();iAccs.NextL();)
+ {
+ iAccs.GetL();
+ TInt id=iAccs.ColInt(1);
+ if (balance[id]!=iAccs.ColInt(2))
+ User::Panic(_L("Oh-oh"),4321);
+ }
+ }