diff -r 000000000000 -r 96e5fb8b040d kerneltest/f32test/server/t_fsched.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/f32test/server/t_fsched.cpp Thu Dec 17 09:24:54 2009 +0200 @@ -0,0 +1,1530 @@ +// Copyright (c) 2006-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: +// f32test\server\t_fsched.cpp +// +// + +/** + @file + @internalTechnology +*/ + +#define __E32TEST_EXTENSION__ + +#include +#include +#include +#include +#include "t_server.h" +#include +#include + +//---------------------------------------------------------------------------------------------- +//! @SYMTestCaseID PBASE-T_FSCHED-0191 +//! @SYMTestType CIT +//! @SYMPREQ PREQ914 +//! @SYMTestCaseDesc This test case is exercising the Fair Scheduling functionality added to +//! the File Server. There are negative and positive tests. +//! @SYMTestActions 0 setup the environment to execute the tests and benchmarks the time +//! taken to write the two files that are going to be used during the test +//! 1 TestReadingWhileWriting uses two threads (sync case) to write a big file/ +//! read a small file simultaneously and verifies that the timing is correct. +//! Same test in the async case. +//! 2 TestWritingWhileWriting same test as before, but the two operations are +//! writes. +//! 3 TestTwoBigOnes does the same test as 1, but with two big files, ensuring +//! that the first starting is the first finishing. +//! 4 TestReadingWhileWritingSameFile reads a file while it is being written. +//! 5 TestClientDies starts a file write and kills it, then checks the integrity +//! of the volume. +//! 6 Finally it performs the benchmark of writing the two files again, to see +//! if it has been degraded during the execution +//! +//! @SYMTestExpectedResults finishes if the fair scheduling behaves as expected, panics otherwise +//! @SYMTestPriority High +//! @SYMTestStatus Implemented +//---------------------------------------------------------------------------------------------- + + +class RTestThreadSemaphore : public RSemaphore + { +public: + inline void Signal(TInt aError = KErrNone) {iError = aError; RSemaphore::Signal();}; +public: + TInt iError; + }; + +// macro to be used in place of test(). Signals main thread if assertion fails +#define TEST(x) \ +if (!(x)) \ + { \ + RThread t; \ + if (t.Id() != gMainThreadId) \ + client.Signal(KErrGeneral); \ + test(x); \ + } +#define TESTERROR(x) \ + if (x != KErrNone) \ + { \ + RThread t; \ + if (t.Id() != gMainThreadId) \ + RDebug::Print(_L("error %d\n"), x); \ + else \ + test.Printf(_L("error %d\n"), x); \ + TEST(x==KErrNone); \ + } + +#define CLIENTWAIT() {client.Wait(); test(client.iError == KErrNone);} + +//////////////////////////////////////////////////////////// +// Template functions encapsulating ControlIo magic +// +GLDEF_D template +GLDEF_C TInt controlIo(RFs &fs, TInt drv, TInt fkn, C &c) +{ + TPtr8 ptrC((TUint8 *)&c, sizeof(C), sizeof(C)); + + TInt r = fs.ControlIo(drv, fkn, ptrC); + + return r; +} + + +GLDEF_D RTest test(_L("T_FSCHED")); + +GLDEF_D RFs TheFs; +GLDEF_D TInt gDrive; +GLDEF_D TFileName gSessionPath; +GLDEF_D TChar gDriveToTest; +TThreadId gMainThreadId; + +HBufC8* gBuf = NULL; +TPtr8 gBufReadPtr(NULL, 0); +HBufC8* gBufSec = NULL; +TPtr8 gBufWritePtr(NULL, 0); + +const TInt KuStomS = 1000; +const TInt KOneK = 1024; +const TInt KOneMeg = KOneK * 1024; +const TInt KBigBlockSize = KOneMeg ; +const TInt KBlockSize = KOneK * 129 ; +const TInt KWaitRequestsTableSize = 70; + +TInt gBigFileSize = 0; +TInt gSmallFileSize = 0; +TInt64 gMediaSize = 0; +TInt gTotalTimeSync[2]; +TInt gTotalTimeAsync[2]; + +TTimeIntervalMicroSeconds32 gTimeTakenBigFile(0); +TTimeIntervalMicroSeconds32 gTimeTakenBigBlock(0); +TBuf16<45> gSmallFile, gBigFile; +TInt gNextFile=0; +TTime gTime1; +TTime gTime2; + +RSemaphore gSync; + +// Concurrent Threads +RThread gBig; +RThread gSmall; +RTestThreadSemaphore client; +const TInt KHeapSize = 0x4000; +const TInt KMaxHeapSize = 0x200000; +TRequestStatus gStatus[KWaitRequestsTableSize]; + +enum TTestState + { + EThreadWait, + EThreadSignal, + ENoThreads + }; + +/** Generates a file name of the form FFFFF*.TXT (aLong.3) + + @param aBuffer The filename will be returned here + @param aLong Defines the longitude of the file name + @param aPos Defines the number that will be attached to the filename +*/ +void FileNameGen(TDes16& aBuffer, TInt aLong, TInt aPos) +{ + TInt padding; + TInt i=0; + TBuf16<10> tempbuf; + + _LIT(KNumber,"%d"); + tempbuf.Format(KNumber,aPos); + padding=aLong-tempbuf.Size()/2; + aBuffer=_L(""); + while(i cmd; + User::CommandLine(cmd); + TLex lex(cmd); + TPtrC token=lex.NextToken(); + TInt r=0; + + if(token.Length()!=0) + { + gDriveToTest = token[0]; + gDriveToTest.UpperCase(); + } + else + { + gDriveToTest='C'; + } + + r = TheFs.CharToDrive(gDriveToTest,gDrive); + TESTERROR(r); + gSessionPath = _L("?:\\F32-TST\\"); + gSessionPath[0] = (TText)(gDrive+'A'); + test.Printf(_L("\nCLP=%C\n"), (TInt)gDriveToTest); +} + +/** Verifies the content of a buffer (all the letters are like the first one) + + @param aBuffer Buffer to be verified + + @return KErrNone if all the letters are the same, KErrCorrupt otherwise +*/ +TInt VerifyBuffer(TDes8& aBuffer, TChar aExpectedChar) + { + TChar c = aExpectedChar; + + for(TInt i = 1; i= aLength); + for(TInt i=0; i0); + + r = fileWrite.Replace(fs,aFile,EFileShareAny|EFileWrite|EFileWriteDirectIO); + TESTERROR(r); + + if(aState==EThreadWait) + { + gSync.Wait(); + User::After(gTimeTakenBigBlock); + } + + TInt j = 0; + while(j < aSize) + { + if((j == 0) && (aState == EThreadSignal)) + { + gSync.Signal(); + } + + if((aSize - j) >= aBlockSize) // All the blocks but the last one + r = fileWrite.Write(gBufWritePtr, aBlockSize); + else // The last block + r = fileWrite.Write(gBufWritePtr, (aSize - j)); + + TESTERROR(r); + j += aBlockSize; + } + + fileWrite.Close(); + + return KErrNone; + } + +/** Read File in blocks of size aBlockSize + + @param fs RFs object + @param aFile File name + @param aBlockSize Size of the blocks to be used in bytes + @param aState Determines if the operation is being done in the main process + or in a thread + + @return Returns KErrNone if everything ok, otherwise it panics +*/ +TInt ReadFile(RFs& fs, TDes16& aFile, TInt aBlockSize, TTestState aState) + { + RTest test(_L("T_FSCHED")); + TInt r = 0, size = 0; + RFile fileRead; + + TEST(aBlockSize > 0); + + r = fileRead.Open(fs, aFile, EFileShareAny|EFileRead|EFileReadDirectIO); + TESTERROR(r); + + r = fileRead.Size(size); + TESTERROR(r); + + if(aState == EThreadWait) + { + gSync.Wait(); + User::After(gTimeTakenBigBlock); + } + + TInt j = 0; + while(j < size) + { + if((j == 0)&&(aState == EThreadSignal)) + { + gSync.Signal(); + } + r = fileRead.Read(gBufReadPtr, aBlockSize); + TESTERROR(r); + r = VerifyBuffer(gBufReadPtr, 'B'); + TESTERROR(r); + + j += aBlockSize; + r = fileRead.Size(size); + TESTERROR(r); + } + + fileRead.Close(); + + return KErrNone; + } + + +/** Write a file asynchronously in blocks of aBlockSize size + + @param fs RFs object + @param aFileWrite RFile object, needs to exist beyond the scope of this function + @param aFile File name + @param aSize Size of the file in bytes + @param aBlockSize Size of the blocks to be used in bytes + @param aStatus TRequestStatus array for all the requests +*/ +void WriteFileAsync(RFs& fs, RFile& aFileWrite, TDes16& aFile, TInt aSize, TInt aBlockSize, TRequestStatus aStatus[]) + { + + TInt r = 0; + + TEST(aBlockSize > 0); // Block size must be greater than 0 + + r = aFileWrite.Replace(fs,aFile,EFileShareAny|EFileWrite|EFileWriteDirectIO); + TESTERROR(r); + + TInt j = 0; + TInt i = 0; + while(j < aSize) + { + if((aSize - j) >= aBlockSize) // All the blocks but the last one + aFileWrite.Write(gBufWritePtr, aBlockSize, aStatus[i]); + else // The last block + aFileWrite.Write(gBufWritePtr, (aSize - j), aStatus[i]); + + TInt status = aStatus[i].Int(); + if (status != KErrNone && status != KRequestPending) + { + test.Printf(_L("RFile::Write() returned %d\n"), aStatus[i].Int()); + } + + test (status == KErrNone || status == KRequestPending); + i++; + j += aBlockSize; + } + } + +/** Read a file asynchronously in blocks of aBlockSize size + + @param fs RFs object + @param aFileRead RFile object, needs to exist beyond the scope of this function + @param aFile File name + @param aSize Size of the file in bytes + @param aBlockSize Size of the blocks to be used in bytes + @param aStatus TRequestStatus array for all the requests + + @return KErrNone +*/ +TInt ReadFileAsync(RFs& fs, RFile& aFileRead, TDes16& aFile, TInt aBlockSize, TRequestStatus aStatus[]) + { + TInt r = 0, size = 0; + + TEST(aBlockSize > 0); // Block size must be greater than 0 + + + r = aFileRead.Open(fs, aFile, EFileShareAny|EFileRead|EFileReadDirectIO); + TESTERROR(r); + + r = aFileRead.Size(size); + TESTERROR(r); + + TInt j = 0, i = 0; + while(j < size) + { + aFileRead.Read(gBufReadPtr, aBlockSize, aStatus[i]); + test (aStatus[i].Int() == KErrNone || aStatus[i].Int() == KRequestPending); + i++; + TESTERROR(r); + j += aBlockSize; + } + + return KErrNone; + } + + +/** +Write a pattern to 2 files simultaneously in blocks of aBlockSize size and verify that fair scheduling +does not interfere with the write order by reading both files back and verifying the contents. + +aBlockSize should be slightly bigger that the fair scheduling size + +@param aFs RFs object +@param aSize Size of the file in bytes +@param aBlockSize Size of the blocks to be used in bytes, should be slightly bigger that the fair scheduling size +*/ +void WriteOrderTest(RFs& aFs, TInt aSize, TInt aBlockSize) + { + TFileName fileName1, fileName2; + FileNameGen(fileName1, 8, 99); + FileNameGen(fileName2, 8, 100); + + RFile file1, file2; + + TRequestStatus status1[KWaitRequestsTableSize]; + TRequestStatus status2[KWaitRequestsTableSize]; + + TInt r = 0; + + TEST(aBlockSize > 0); // Block size must be greater than 0 + + r = file1.Replace(aFs, fileName1,EFileShareAny|EFileWrite|EFileWriteDirectIO); + TESTERROR(r); + + r = file2.Replace(aFs, fileName2,EFileShareAny|EFileWrite|EFileWriteDirectIO); + TESTERROR(r); + + + HBufC8* buf1 = NULL; + TRAP(r,buf1 = HBufC8::NewL(KBigBlockSize)); + TEST(r == KErrNone && buf1 != NULL); + + TInt maxBlockNum = KBigBlockSize / aBlockSize; + TInt blockNum = 0; + TInt blocksFree = maxBlockNum; + + TInt blockHead = 0; // ha ha + TInt blockTail = 0; + + + TPtrC8* writeBuffer = new TPtrC8[maxBlockNum]; + for (TInt n=0; nDes().Ptr() + (n * aBlockSize); + writeBuffer[n].Set(addr, aBlockSize); + } + + TInt j = 0; + TInt i = 0; + while(j < aSize) + { + + // fill the buffer with a char based on the block number + TPtr8 bufWritePtr((TUint8*) writeBuffer[blockHead].Ptr(), 0, writeBuffer[blockHead].Length()); + TUint8* addr = (TUint8*) buf1->Des().Ptr() + (blockHead * aBlockSize); + bufWritePtr.Set(addr, 0, aBlockSize); + FillBuffer(bufWritePtr, aBlockSize, 'A'+blockNum); + +//test.Printf(_L("j %d, addr %08X, aSize %d, blockNum %d, blockTail %d, blockHead %d, maxBlockNum %d, blocksFree %d\n"), +// j, writeBuffer[blockHead].Ptr(), aSize, blockNum, blockTail, blockHead, maxBlockNum, blocksFree); + + if((aSize - j) >= aBlockSize) // All the blocks but the last one + file1.Write(writeBuffer[blockHead], aBlockSize, status1[blockHead]); + else // The last block + file1.Write(writeBuffer[blockHead], (aSize - j), status1[blockHead]); + + if((aSize - j) >= aBlockSize) // All the blocks but the last one + file2.Write(writeBuffer[blockHead], aBlockSize, status2[blockHead]); + else // The last block + file2.Write(writeBuffer[blockHead], (aSize - j), status2[blockHead]); + + TInt status = status1[blockHead].Int(); + if (status != KErrNone && status != KRequestPending) + test.Printf(_L("RFile::Write() returned %d\n"), status1[blockHead].Int()); + test (status == KErrNone || status == KRequestPending); + + status = status2[blockHead].Int(); + if (status != KErrNone && status != KRequestPending) + test.Printf(_L("RFile::Write() returned %d\n"), status1[blockHead].Int()); + test (status == KErrNone || status == KRequestPending); + + i++; + j += aBlockSize; + blockNum++; + blockHead = (blockHead + 1) % maxBlockNum; + blocksFree--; + + if (blocksFree == 0) + { +//test.Printf(_L("Waiting for block %d\n"), blockTail); + User::WaitForRequest(status1[blockTail]); + User::WaitForRequest(status2[blockTail]); + blockTail = (blockTail + 1) % maxBlockNum; + blocksFree++; + } + + } + + while (blockTail != blockHead) + { +//test.Printf(_L("Waiting for block %d\n"), blockTail); + User::WaitForRequest(status1[blockTail]); + User::WaitForRequest(status2[blockTail]); + blockTail = (blockTail + 1) % maxBlockNum; + blocksFree++; + } + + file1.Close(); + file2.Close(); + + // Verify file1 + r = file1.Open(aFs, fileName1, EFileShareAny|EFileRead|EFileReadDirectIO); + TESTERROR(r); + TInt size; + r = file1.Size(size); + TESTERROR(r); + blockNum = 0; + j = 0; + while(j < size) + { + TPtr8 readPtr((TUint8*) writeBuffer[0].Ptr(), aBlockSize, aBlockSize); + r = file1.Read(readPtr); + TESTERROR(r); + r = VerifyBuffer(readPtr, 'A' + blockNum); + TESTERROR(r); + j += aBlockSize; + blockNum++; + } + + // Verify file2 + r = file2.Open(aFs, fileName2, EFileShareAny|EFileRead|EFileReadDirectIO); + TESTERROR(r); + r = file2.Size(size); + TESTERROR(r); + blockNum = 0; + j = 0; + while(j < size) + { + TPtr8 readPtr((TUint8*) writeBuffer[0].Ptr(), aBlockSize, aBlockSize); + r = file2.Read(readPtr); + TESTERROR(r); + r = VerifyBuffer(readPtr, 'A' + blockNum); + TESTERROR(r); + j += aBlockSize; + blockNum++; + } + + file2.Close(); + file1.Close(); + + delete [] writeBuffer; + delete buf1; + + r = aFs.Delete(fileName1); + TEST(r == KErrNone); + r = aFs.Delete(fileName2); + TEST(r == KErrNone); + } + + + +/** Measure the time taken for a big block to be written synchronously +*/ +void TimeTakenToWriteBigBlock() +{ + TTime startTime; + TTime endTime; + RFile fileWrite; + + TInt r = fileWrite.Replace(TheFs,gBigFile,EFileShareAny|EFileWrite|EFileWriteDirectIO); + TESTERROR(r); + + // Calculate how long it takes to write a big block to be able to issue at the right time the concurrent writes + startTime.HomeTime(); + + r = fileWrite.Write(gBufWritePtr, KBigBlockSize); + + endTime.HomeTime(); + + fileWrite.Close(); + + TESTERROR(r); + + gTimeTakenBigBlock = I64LOW(endTime.MicroSecondsFrom(startTime).Int64()/3); + + test.Printf(_L("\nTime spent to write the big block in isolation: %d ms\n"), gTimeTakenBigBlock.Int() / KuStomS); +} + + +/** Measure the time taken for this file to be written synchronously +*/ +void TimeTakenToWriteBigFile(TInt aPos) +{ + TTime startTime; + TTime endTime; + + test((aPos >= 0) && (aPos <= 1)); + startTime.HomeTime(); + + WriteFile(TheFs,gBigFile, gBigFileSize, KBigBlockSize, ENoThreads); + + endTime.HomeTime(); + + gTimeTakenBigFile = I64LOW(endTime.MicroSecondsFrom(startTime).Int64()); + + + test.Printf(_L("\nTime spent to write the big file in isolation: %d ms\n"), gTimeTakenBigFile.Int() / KuStomS); + + gTotalTimeSync[aPos] = Max((gTimeTakenBigFile.Int()/KuStomS), gTotalTimeSync[aPos]) ; +} + +/** Measure the time taken for this file to be written asynchronously +*/ +void TimeTakenToWriteBigFileAsync(TInt aPos) +{ + TTime startTime; + TTime endTime; + TTime endTime2; + TRequestStatus status[KWaitRequestsTableSize]; + RFile bigFile; + + test((aPos >= 0) && (aPos <= 1)); + + startTime.HomeTime(); + WriteFileAsync(TheFs, bigFile, gBigFile, gBigFileSize,KBigBlockSize,status); + endTime.HomeTime(); + + + WaitForAll(status, gBigFileSize, KBigBlockSize); + + endTime2.HomeTime(); + + gTimeTakenBigFile=I64LOW(endTime.MicroSecondsFrom(startTime).Int64()); + bigFile.Close(); + test.Printf(_L("\nTime to queue the blocks in isolation asynchronously: %d ms, "), gTimeTakenBigFile.Int() / KuStomS); + gTimeTakenBigFile=I64LOW(endTime2.MicroSecondsFrom(startTime).Int64()); + test.Printf(_L("to actually write it: %d ms\n"),gTimeTakenBigFile.Int() / KuStomS); + gTotalTimeAsync[aPos] = Max((gTimeTakenBigFile.Int() / KuStomS), gTotalTimeAsync[aPos]) ; +} + +/** Delete content of directory + + @param aDir Target directory + + @return Error returned if any, otherwise KErrNone +*/ +TInt DeleteAll(TDes16& aDir) + { + TBuf16<100> dir; + CFileMan* fMan=CFileMan::NewL(TheFs); + TInt r=0; + + dir = aDir; + dir.Append(_L("F*.*")); + r = fMan->Delete(dir); + + delete fMan; + return r; + } + +/** Read a small file sync, in a different thread + + @return ETrue +*/ +TInt ReadSmallFile(TAny* ) + { + RTest test(_L("T_FSCHED")); + RFs fs; + TInt r=fs.Connect(); + TESTERROR(r); + + r = fs.SetSessionPath(gSessionPath); + TESTERROR(r); + + ReadFile(fs,gSmallFile,KBlockSize, EThreadWait); + gTime2.HomeTime(); + + client.Signal(); + + fs.Close(); + test.Close(); + + return ETrue; + } + +/** Read the big file sync, in a different thread + + @return ETrue +*/ +TInt ReadBigFile(TAny* ) + { + RTest test(_L("T_FSCHED")); + RFs fs; + TInt r=fs.Connect(); + TESTERROR(r); + + r=fs.SetSessionPath(gSessionPath); + TESTERROR(r); + + ReadFile(fs,gBigFile,KBlockSize, EThreadWait); + gTime2.HomeTime(); + + client.Signal(); + + fs.Close(); + test.Close(); + + return ETrue; + } + +/** Write a small file sync, in a different thread + + @return ETrue +*/ +TInt WriteSmallFile(TAny* ) + { + RTest test(_L("T_FSCHED")); + RFs fs; + TInt r=fs.Connect(); + TESTERROR(r); + + r=fs.SetSessionPath(gSessionPath); + TESTERROR(r); + + WriteFile(fs,gSmallFile,gSmallFileSize, KBlockSize, EThreadWait); + gTime2.HomeTime(); + + client.Signal(); + + fs.Close(); + test.Close(); + + return ETrue; + } + +/** Write a big file sync, in a different thread + + @return ETrue +*/ +TInt WriteBigFile(TAny* ) + { + RTest test(_L("T_FSCHED")); + RFs fs; + TInt r=fs.Connect(); + TESTERROR(r); + + r=fs.SetSessionPath(gSessionPath); + TESTERROR(r); + + WriteFile(fs, gBigFile, gBigFileSize, KBigBlockSize, EThreadSignal); + gTime1.HomeTime(); + + client.Signal(); + + fs.Close(); + test.Close(); + + return ETrue; + } + +/** Write big file after a signalling thread has been scheduled, in a different thread + + @return ETrue +*/ +TInt WriteBigFile2(TAny* ) + { + RTest test(_L("T_FSCHED")); + RFs fs; + TInt r=fs.Connect(); + TESTERROR(r); + + r = fs.SetSessionPath(gSessionPath); + TESTERROR(r); + + WriteFile(fs, gSmallFile, gBigFileSize, KBigBlockSize, EThreadWait); + gTime2.HomeTime(); + + client.Signal(); + + fs.Close(); + test.Close(); + + return ETrue; + } + +/** Write a big file async, in a different thread + + @return ETrue +*/ +TInt WriteBigFileAsync(TAny* ) + { + RTest test(_L("T_FSCHED")); + RFs fs; + TInt r = fs.Connect(); + TESTERROR(r); + + r=fs.SetSessionPath(gSessionPath); + TESTERROR(r); + + + RFile bigFile; + + WriteFileAsync(fs, bigFile, gSmallFile, gBigFileSize, KBlockSize,gStatus); + gSync.Signal(); + WaitForAll(gStatus, gBigFileSize, KBlockSize); + + fs.Close(); + test.Close(); + + return ETrue; + } + +/** Delete a big file, in a different thread + + @return ETrue +*/ +TInt DeleteBigFile(TAny* ) + { + RTest test(_L("T_FSCHED")); + RFs fs; + TInt r = fs.Connect(); + TESTERROR(r); + + r = fs.SetSessionPath(gSessionPath); + TESTERROR(r); + + gSync.Wait(); + + r = fs.Delete(gBigFile); +#if defined(__WINS__) + TEST(r == KErrInUse || r == KErrNone); +#else + test_Equal(KErrInUse, r); +#endif + + client.Signal(); + + fs.Close(); + test.Close(); + + return ETrue; + } + +/** Reads a small file while writing a big one + +*/ +void TestReadingWhileWriting() +{ + TInt r = 0; + TTime time1; + TTime time2; + + time1.HomeTime(); + + // Write the small file and take the appropriate measures + WriteFile(TheFs,gSmallFile,KBlockSize, KBlockSize, ENoThreads); + + // Sync test + TBuf<20> buf=_L("Big Write"); + r = gBig.Create(buf, WriteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL); + TEST(r == KErrNone); + + buf = _L("Small Read"); + r = gSmall.Create(buf, ReadSmallFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL); + TEST(r == KErrNone); + + gBig.Resume(); + gSmall.Resume(); + + CLIENTWAIT(); + CLIENTWAIT(); + + gBig.Close(); + gSmall.Close(); + + + TTimeIntervalMicroSeconds timeTaken = gTime1.MicroSecondsFrom(gTime2); + test.Printf(_L("\nSync read done %d ms before the write ended\n"),I64LOW(timeTaken.Int64() / KuStomS)); + TReal time=I64LOW(timeTaken.Int64() / KuStomS); + #if !defined(__WINS__) + // If this condition fails, means that writing the sync file while fairscheduling a small sync read takes too long + test.Printf(_L("time: %f\n"), time); +// test((time > 0) && (((gTotalTimeSync[0]-time)>0) || ((gTotalTimeSync[1]-time)>0)) ); + test(time > 0); + #endif + + // Async test + TRequestStatus status[KWaitRequestsTableSize]; + TRequestStatus status2[2]; + RFile bigFile, smallFile; + + WriteFileAsync(TheFs, bigFile, gBigFile, gBigFileSize, KBigBlockSize ,status); + ReadFileAsync(TheFs, smallFile, gSmallFile, KBlockSize, status2 ); + + WaitForAll(status2,KBlockSize , KBlockSize ); + time1.HomeTime(); + + WaitForAll(status,gBigFileSize, KBigBlockSize); + + time2.HomeTime(); + bigFile.Close(); + smallFile.Close(); + + timeTaken = time2.MicroSecondsFrom(time1); + + test.Printf(_L("\nAsync read done %d ms before the write ended\n"),I64LOW(timeTaken.Int64() / KuStomS)); + time = I64LOW(timeTaken.Int64() / KuStomS); + + #if !defined(__WINS__) + // If this condition fails, means that writing the async file while fairscheduling a small async read takes too long + test.Printf(_L("time: %f\n"), time); + test.Printf(_L("gTotalTimeAsync[0] = %d , gTotalTimeAsync[1] = %d\n"),gTotalTimeAsync[0],gTotalTimeAsync[1] ); +// test((time > 0) && (((gTotalTimeAsync[0]-time)>0) || ((gTotalTimeAsync[1]-time)>0)) ); + test(time > 0); + #endif +} + +/** Writes a small file while writing a big one + +*/ +void TestWritingWhileWriting() +{ + TInt r = 0; + TTime time1; + TTime time2; + + // Sync test + TBuf<20> buf = _L("Big Write II"); + r = gBig.Create(buf, WriteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL); + TEST(r == KErrNone); + + buf = _L("Small Write II"); + r = gSmall.Create(buf, WriteSmallFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL); + TEST(r==KErrNone); + + gBig.Resume(); + gSmall.Resume(); + + CLIENTWAIT(); + CLIENTWAIT(); + + gBig.Close(); + gSmall.Close(); + + TTimeIntervalMicroSeconds timeTaken = gTime1.MicroSecondsFrom(gTime2); + test.Printf(_L("\nSync write done %d ms before the big write ended\n"),I64LOW(timeTaken.Int64() / KuStomS)); + TReal time=I64LOW(timeTaken.Int64() / KuStomS); + #if !defined(__WINS__) + // If this condition fails, means that writing the sync file while fairscheduling a small sync write takes too long + test.Printf(_L("time: %f\n"), time); +// test((time > 0) && (((gTotalTimeSync[0]-time)>0) || ((gTotalTimeSync[1]-time)>0)) ); + test(time > 0); + #endif + + // Async test + TRequestStatus status[KWaitRequestsTableSize]; + TRequestStatus status2[1]; + RFile bigFile, smallFile; + + WriteFileAsync(TheFs, bigFile, gBigFile, gBigFileSize, KBigBlockSize, status); + WriteFileAsync(TheFs,smallFile, gSmallFile,gSmallFileSize,KBlockSize,status2); + WaitForAll(status2,gSmallFileSize, KBlockSize); + time1.HomeTime(); + WaitForAll(status, gBigFileSize, KBigBlockSize); + time2.HomeTime(); + + timeTaken = time2.MicroSecondsFrom(time1); + test.Printf(_L("\nAsync write done %d ms before the big write ended\n"),I64LOW(timeTaken.Int64() / KuStomS)); + time=I64LOW(timeTaken.Int64() / KuStomS); + #if !defined(__WINS__) + // If this condition fails, means that writing the async file while fairscheduling a small async write takes too long + test.Printf(_L("time: %f\n"), time); + test.Printf(_L("gTotalTimeAsync[0] = %d , gTotalTimeAsync[1] = %d\n"),gTotalTimeAsync[0],gTotalTimeAsync[1] ); +// test((time > 0) && (((gTotalTimeAsync[0]-time)>0) || ((gTotalTimeAsync[1]-time)>0)) ); + test(time > 0); + #endif + bigFile.Close(); + smallFile.Close(); +} + +/** Writes two big files, ensuring the one that started to be written later ends the last one + +*/ +void TestTwoBigOnes() +{ + TInt r = 0; + TTime time1; + TTime time2; + + // Sync test + TBuf<20> buf = _L("Big Write IIII"); + r = gBig.Create(buf, WriteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL); + TEST(r == KErrNone); + + buf = _L("Big Write The Second"); + r = gSmall.Create(buf, WriteBigFile2, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL); + TEST(r == KErrNone); + + gBig.Resume(); + gSmall.Resume(); + + CLIENTWAIT(); + CLIENTWAIT(); + + gBig.Close(); + gSmall.Close(); + + TTimeIntervalMicroSeconds timeTaken = gTime2.MicroSecondsFrom(gTime1); + test.Printf(_L("\nSync first write ended %d ms before the second write ended (same file size)\n"),I64LOW(timeTaken.Int64() / KuStomS)); + + // Async test + TRequestStatus status[KWaitRequestsTableSize]; + TRequestStatus status2[KWaitRequestsTableSize]; + RFile bigFile, bigFile2; + + WriteFileAsync(TheFs, bigFile, gBigFile, gBigFileSize, KBigBlockSize, status); + WriteFileAsync(TheFs, bigFile2, gSmallFile, gBigFileSize, KBigBlockSize, status2); + WaitForAll(status, gBigFileSize, KBigBlockSize); + time1.HomeTime(); + WaitForAll(status2, gBigFileSize, KBigBlockSize); + time2.HomeTime(); + + timeTaken = time2.MicroSecondsFrom(time1); + test.Printf(_L("\nAsync first write ended %d ms before the second write ended (same file size)\n"),I64LOW(timeTaken.Int64() / KuStomS)); + bigFile.Close(); + bigFile2.Close(); +} + +/** Reads the file that is being written + +*/ +void TestReadingWhileWritingSameFile() +{ + TInt r = 0; + TTime time1; + + time1.HomeTime(); + + // Sync test + TBuf<20> buf = _L("Big Write IV"); + r = gBig.Create(buf, WriteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL); + TEST(r == KErrNone); + + buf = _L("Read IV"); + r = gSmall.Create(buf, ReadBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL); + TEST(r == KErrNone); + + gBig.Resume(); + gSmall.Resume(); + + CLIENTWAIT(); + CLIENTWAIT(); + + CLOSE_AND_WAIT(gBig); + CLOSE_AND_WAIT(gSmall); + + TTimeIntervalMicroSeconds timeTaken = gTime2.MicroSecondsFrom(gTime1); + test.Printf(_L("\nSync write finished %d ms before the read ended\n"),I64LOW(timeTaken.Int64() / KuStomS)); + +} + +/** Client dying uncleanly before the operation is finished + +*/ +void TestClientDies() +{ + TInt r = 0; + + + TBuf<20> drive = _L("?:\\"); + TVolumeInfo volInfo; + drive[0]=(TText)(gDrive+'A'); + + r = TheFs.CheckDisk(drive); + TEST(r == KErrNone || r == KErrNotSupported); + + // Sync test + TBuf<20> buf = _L("Big Write V"); + r = gBig.Create(buf, WriteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL); + TEST(r == KErrNone); + + TRequestStatus status; + gBig.Logon(status); + gBig.Resume(); + gSync.Wait(); + + // Kill the writing thread and wait for it to die. + + if(status.Int() == KRequestPending) + {// the people who wrote this test did not consider the case when the file write finishes before they try to kill the thread. + gBig.Kill(KErrGeneral); + User::WaitForRequest(status); + TEST(gBig.ExitReason() == KErrGeneral); + TEST(gBig.ExitType() == EExitKill); + } + + + // Make sure the thread is destroyed and the handles it owned and IPCs + // it executed are closed/cancelled. + CLOSE_AND_WAIT(gBig); + + + r = TheFs.Volume(volInfo, gDrive); + TESTERROR(r); + + r = TheFs.CheckDisk(drive); + TEST(r == KErrNone || r == KErrNotSupported); + + r = TheFs.ScanDrive(drive); + TEST(r == KErrNone || r == KErrNotSupported); + + test.Printf(_L("Sync operation stopped\n")); + + // Async test + buf = _L("Big Write VI"); + r = gSmall.Create(buf, WriteBigFileAsync, KDefaultStackSize * 2, KHeapSize, KMaxHeapSize, NULL); + TEST(r == KErrNone); + + gSmall.Logon(status); + gSmall.Resume(); + gSync.Wait(); + + + if(status.Int() == KRequestPending) + { + // Kill the writing thread and wait for it to die. + gSmall.Kill(KErrGeneral); + User::WaitForRequest(status); + TEST(gSmall.ExitReason() == KErrGeneral); + TEST(gSmall.ExitType() == EExitKill); + } + + + // Make sure the thread is destroyed and the handles it owned and IPCs + // it executed are closed/cancelled. + CLOSE_AND_WAIT(gSmall); + + r = TheFs.CheckDisk(drive); + TEST(r == KErrNone || r == KErrNotSupported); + + r=TheFs.ScanDrive(drive); + TEST(r == KErrNone || r == KErrNotSupported); + + test.Printf(_L("Async operation stopped\n")); +} + +/** Reads a small file while writing a big one + +*/ +void TestDeletionWhileWriting() +{ + TInt r = 0; + + // Sync test + TBuf<20> buf = _L("Big Write V"); + r = gBig.Create(buf, WriteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL); + TEST(r == KErrNone); + + buf = _L("Deletion"); + r = gSmall.Create(buf, DeleteBigFile, KDefaultStackSize, KHeapSize, KMaxHeapSize, NULL); + TEST(r == KErrNone); + + gSmall.Resume(); + gBig.Resume(); + + CLIENTWAIT(); + CLIENTWAIT(); + + gBig.Close(); + gSmall.Close(); + + test.Printf(_L("The file was properly blocked when writing sync, not deleted\n")); + + // Async test + TRequestStatus status[KWaitRequestsTableSize]; + RFile bigFile; + RFs fs; + + r = fs.Connect(); + TESTERROR(r); + r = fs.SetSessionPath(gSessionPath); + TESTERROR(r); + + WriteFileAsync(TheFs, bigFile, gBigFile, gBigFileSize, KBigBlockSize, status); + + r = fs.Delete(gBigFile); + TEST(r == KErrInUse); + + WaitForAll(status, gBigFileSize, KBigBlockSize); + + bigFile.Close(); + fs.Close(); + + test.Printf(_L("The file was properly blocked when writing async, not deleted\n")); + +} + + +void TestWriteOrder() + { + RFs fs; + + TInt r = fs.Connect(); + TESTERROR(r); + r = fs.SetSessionPath(gSessionPath); + TESTERROR(r); + + WriteOrderTest(TheFs, gBigFileSize, KBlockSize); + + fs.Close(); + } + + +/** Main tests function +*/ +void CallTestsL() + { + TBuf16<45> dir; + TInt r = 0; + + +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) + test.Printf(_L("Disabling Lock Fail simulation ...\n")); + // turn OFF lock failure mode (if cache is enabled) + + TBool simulatelockFailureMode = EFalse; + r = controlIo(TheFs, gDrive, KControlIoSimulateLockFailureMode, simulatelockFailureMode); + test (r == KErrNone || r == KErrNotSupported); +#endif + + // FileNames/File generation + test.Start(_L("Preparing the environment\n")); + + FileNameGen(gSmallFile, 8, gNextFile++); + FileNameGen(gBigFile, 8, gNextFile++); + + dir = gSessionPath; + dir.Append(gSmallFile); + + gSmallFile = dir; + dir = gSessionPath; + dir.Append(gBigFile); + gBigFile = dir; + + TRAPD(res,gBuf = HBufC8::NewL(KBigBlockSize)); + TEST(res == KErrNone && gBuf != NULL); + + gBufWritePtr.Set(gBuf->Des()); + FillBuffer(gBufWritePtr, KBigBlockSize, 'B'); + + TRAPD(res2, gBufSec = HBufC8::NewL(KBlockSize)); + TEST(res2 == KErrNone && gBufSec != NULL); + gBufReadPtr.Set(gBufSec->Des()); + + test.Next(_L("Benchmarking\n")); + TimeTakenToWriteBigFile(0); + TimeTakenToWriteBigFileAsync(0); + + test.Printf(_L("second try, second timings account for the last comparison\n")); + TimeTakenToWriteBigFile(0); + TimeTakenToWriteBigFileAsync(0); + + TimeTakenToWriteBigBlock(); + + test.Next(_L("Big file sync written, small file read from the media at the same time\n")); + TestReadingWhileWriting(); + + test.Next(_L("Big file written, small file written at the same time\n")); + TestWritingWhileWriting(); + + test.Next(_L("Big file written async, deletion in the meantime\n")); + TestDeletionWhileWriting(); + + test.Next(_L("Two big files written at the same time\n")); + TestTwoBigOnes(); + + test.Next(_L("Big file being written, start reading\n")); + TestReadingWhileWritingSameFile(); + + test.Next(_L("Client dying unexpectedly\n")); + TestClientDies(); + + test.Next(_L("Ensure write order is preserved\n")); + TestWriteOrder(); + + // Format the drive to make sure no blocks are left to be erased in LFFS + #if !defined(__WINS__) + Format(gDrive); + #endif + r = TheFs.MkDirAll(gSessionPath); + + TimeTakenToWriteBigFile(1); + TimeTakenToWriteBigFileAsync(1); + + // Make sure that the difference between the first time and the last time the files are written doesn't + // differ more than 3% + test.Printf(_L("Abs(gTotalTimeSync[0]-gTotalTimeSync[1]) :%d\n"), Abs(gTotalTimeSync[0]-gTotalTimeSync[1])); + test.Printf(_L("Abs(gTotalTimeAsync[0]-gTotalTimeAsync[1]) :%d\n"), Abs(gTotalTimeAsync[0]-gTotalTimeAsync[1])); + test.Printf(_L("gTotalTimeSync[0] :%d\n"), gTotalTimeSync[0]); + test.Printf(_L("gTotalTimeAsync[0] :%d\n"), gTotalTimeAsync[0]); + + #if !defined(__WINS__) + test((Abs(gTotalTimeSync[0]-gTotalTimeSync[1])/gTotalTimeSync[0]) < 0.03); + test((Abs(gTotalTimeAsync[0]-gTotalTimeAsync[1])/gTotalTimeAsync[0]) < 0.03); + #endif + + r = DeleteAll(gSessionPath); + TESTERROR(r); + + delete gBuf; + delete gBufSec; + +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) + // turn lock failure mode back ON (if cache is enabled) + simulatelockFailureMode = ETrue; + r = controlIo(TheFs, gDrive, KControlIoSimulateLockFailureMode, simulatelockFailureMode); + test (r == KErrNone || r == KErrNotSupported); +#endif + + test.End(); + } + +/** Initialises semaphores and call the tests +*/ +void DoTests() + { + TInt r = 0; + + r = client.CreateLocal(0); + TESTERROR(r); + + r = gSync.CreateLocal(0); + TESTERROR(r); + + r = TheFs.SetSessionPath(gSessionPath); + TESTERROR(r); + + r = TheFs.MkDirAll(gSessionPath); + if (r != KErrNone && r != KErrAlreadyExists) + { + TESTERROR(r); + } + TheFs.ResourceCountMarkStart(); + TRAP(r,CallTestsL()); + if (r == KErrNone) + TheFs.ResourceCountMarkEnd(); + else + { + TESTERROR(r); + } + } + +/** Determines the space that can be used for the files + +*/ +TBool CheckForDiskSize() +{ + TVolumeInfo volInfo; + TInt r = TheFs.Volume(volInfo, gDrive); + TESTERROR(r); + + gMediaSize = volInfo.iSize; + gSmallFileSize = KBlockSize; + gBigFileSize = KBlockSize*20; + + while(((2*gBigFileSize)+KOneMeg) > gMediaSize ) + { + gBigFileSize -= (2*KBlockSize); + } + + TReal32 small = (TReal32)(gSmallFileSize/KOneK); + TReal32 big = (TReal32)(gBigFileSize/KOneK); + + + test.Printf(_L("Small File Size: %.2f KB\n"), small); + test.Printf(_L("Big File Size: %.2f KB (%.2f MB)\n"), big, big / KOneK); + + if(gBigFileSize< (3*gSmallFileSize)) + return EFalse; + else + return ETrue; +} + +/** Formats the drive + + @param aDrive Drive to be formatted +*/ +void Format(TInt aDrive) + { + + test.Next(_L("Format")); + TBuf<4> driveBuf = _L("?:\\"); + driveBuf[0] = (TText)(aDrive+'A'); + RFormat format; + TInt count, prevcount = 0; + TInt r = format.Open(TheFs, driveBuf, EQuickFormat, count); + TESTERROR(r); + + while(count) + { + TInt r = format.Next(count); + if(count != prevcount) + { + test.Printf(_L(".")); + prevcount = count; + } + TESTERROR(r); + } + + format.Close(); + } + +/** Main function + + @return KErrNone if everything was ok, panics otherwise +*/ +TInt E32Main() + { + RThread t; + gMainThreadId = t.Id(); + + CTrapCleanup* cleanup; + cleanup = CTrapCleanup::New(); + + __UHEAP_MARK; + test.Start(_L("Starting tests... T_FSCHED")); + parseCommandLine(); + + TInt r = TheFs.Connect(); + TESTERROR(r); + + TDriveInfo info; + TVolumeInfo volInfo; + r = TheFs.Drive(info, gDrive); + TESTERROR(r); + + if(info.iMediaAtt&KMediaAttVariableSize) + { + test.Printf(_L("Tests skipped in RAM drive\n")); + goto out; + } + + r = TheFs.Volume(volInfo, gDrive); + if (r == KErrNotReady) + { + if (info.iType == EMediaNotPresent) + test.Printf(_L("%c: Medium not present - cannot perform test.\n"), (TUint)gDrive + 'A'); + else + test.Printf(_L("medium found (type %d) but drive %c: not ready\nPrevious test may have hung; else, check hardware.\n"), (TInt)info.iType, (TUint)gDrive + 'A'); + } + else if (r == KErrCorrupt) + { + test.Printf(_L("%c: Media corruption; previous test may have aborted; else, check hardware\n"), (TUint)gDrive + 'A'); + } + TESTERROR(r); +#if !defined(__WINS__) + if ((volInfo.iDrive.iMediaAtt & KMediaAttFormattable)) + Format(gDrive); +#endif + + if(CheckForDiskSize()) + { + DoTests(); + } + else + { + test.Printf(_L("Skipping tests due to lack of space to perform them in this drive\n")); + } +out: + test.End(); + + TheFs.Close(); + test.Close(); + + __UHEAP_MARKEND; + delete cleanup; + return(KErrNone); + }