os/kernelhwsrv/userlibandfileserver/fileserver/smassstorage/rwdrivethread.cpp
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/kernelhwsrv/userlibandfileserver/fileserver/smassstorage/rwdrivethread.cpp	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,583 @@
     1.4 +// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
     1.5 +// All rights reserved.
     1.6 +// This component and the accompanying materials are made available
     1.7 +// under the terms of the License "Eclipse Public License v1.0"
     1.8 +// which accompanies this distribution, and is available
     1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.10 +//
    1.11 +// Initial Contributors:
    1.12 +// Nokia Corporation - initial contribution.
    1.13 +//
    1.14 +// Contributors:
    1.15 +//
    1.16 +// Description:
    1.17 +//
    1.18 +
    1.19 +#include "scsiprot.h"
    1.20 +#include "usbmsshared.h"
    1.21 +#include "rwdrivethread.h"
    1.22 +#include "massstoragedebug.h"
    1.23 +
    1.24 +// ---
    1.25 +
    1.26 +#ifdef PRINT_MSDC_MULTITHREADED_READ_INFO
    1.27 +#define __MT_READ_PRINT(t) {RDebug::Print(t);}
    1.28 +#define __MT_READ_PRINT1(t,a) {RDebug::Print(t,a);}
    1.29 +#define __MT_READ_PRINT2(t,a,b) {RDebug::Print(t,a,b);}
    1.30 +#else
    1.31 +#define __MT_READ_PRINT(t)
    1.32 +#define __MT_READ_PRINT1(t,a)
    1.33 +#define __MT_READ_PRINT2(t,a,b)
    1.34 +#endif // PRINT_MSDC_MULTITHREADED_READ_INFO
    1.35 +
    1.36 +
    1.37 +#ifdef MSDC_MULTITHREADED 
    1.38 +
    1.39 +TBlockDesc::TBlockDesc()
    1.40 +	:iBuf((TUint8 *)NULL,0,0)
    1.41 +	{
    1.42 +	}
    1.43 +
    1.44 +void TBlockDesc::SetPtr(TPtr8& aDes)
    1.45 +	{
    1.46 +	iBuf.Set(aDes);
    1.47 +	}
    1.48 +
    1.49 +
    1.50 +TBlockDescBuffer::TBlockDescBuffer()
    1.51 +	{ 
    1.52 +	iDescReadPtr = &iDesc1;
    1.53 +	iDescWritePtr = &iDesc2;
    1.54 +	}
    1.55 +
    1.56 +void TBlockDescBuffer::SetUpReadBuf(TPtr8& aDes1, TPtr8& aDes2)
    1.57 +	{
    1.58 +	iDesc1.SetPtr(aDes1);
    1.59 +	iDesc2.SetPtr(aDes2);
    1.60 +	iDescReadPtr = &iDesc1;
    1.61 +	iDescWritePtr = &iDesc2;
    1.62 +	}
    1.63 +
    1.64 +
    1.65 +//-----------------------------------------------
    1.66 +
    1.67 +/**
    1.68 +Construct a CThreadContext object.
    1.69 +
    1.70 +@param aName The name to be assigned to this thread.
    1.71 +@param aThreadFunction Function to be called when thread is initially scheduled.
    1.72 +@param aOwner Pointer to the object owning the thread. Used as the parameter to aThreadFunction.
    1.73 +*/
    1.74 +CThreadContext* CThreadContext::NewL(const TDesC& aName,
    1.75 +									 TThreadFunction aThreadFunction,
    1.76 +									 TAny* aOwner)
    1.77 +	{
    1.78 +	__FNLOG("CThreadContext::NewL");
    1.79 +	CThreadContext* self = new (ELeave) CThreadContext();
    1.80 +	CleanupStack::PushL(self);
    1.81 +	self->ConstructL(aName, aThreadFunction, aOwner);
    1.82 +	CleanupStack::Pop();
    1.83 +	return self;
    1.84 +	}
    1.85 +
    1.86 +/**
    1.87 +Construct a CThreadContext object
    1.88 +
    1.89 +@param aName The name to be assigned to this thread.
    1.90 +@param aThreadFunction Function to be called when thread is initially scheduled.
    1.91 +@param aOwner Pointer to the object owning the thread. Used as the parameter to aThreadFunction.
    1.92 +*/
    1.93 +void CThreadContext::ConstructL(const TDesC& aName,
    1.94 +								TThreadFunction aThreadFunction,
    1.95 +								TAny* aOwner)
    1.96 +	{
    1.97 +	__FNLOG("CThreadContext::ConstructL");
    1.98 +	__PRINT(_L("Creating Critical Section"));
    1.99 +	User::LeaveIfError(iCritSect.CreateLocal());
   1.100 +	__PRINT(_L("Creating RThread"));
   1.101 +
   1.102 +	TUint serial(0); // Used to retry creation of a thread in case
   1.103 +					 // one with the same name already exists
   1.104 +
   1.105 +	RBuf threadName;
   1.106 +	threadName.CreateMaxL(aName.Length() + 8);
   1.107 +	CleanupClosePushL(threadName);
   1.108 +	threadName = aName;
   1.109 +
   1.110 +	TInt err;
   1.111 +	for (;;)
   1.112 +		{
   1.113 +		err = iThread.Create(threadName, aThreadFunction, 0x1000, NULL, aOwner);
   1.114 +		__PRINT2(_L("CThreadContext::ConstructL Created thread %S err=%d"), &threadName, err);
   1.115 +
   1.116 +        // for a restart wait and retry until old thread is gone
   1.117 +		if (err == KErrAlreadyExists)
   1.118 +			{
   1.119 +			User::After(10 * 1000);     // 10 mS
   1.120 +			threadName = aName;
   1.121 +			threadName.AppendNumFixedWidth(serial, EDecimal, 8);
   1.122 +			++serial;
   1.123 +			}
   1.124 +		else
   1.125 +			{
   1.126 +			break;
   1.127 +			}
   1.128 +		}
   1.129 +
   1.130 +    User::LeaveIfError(err);
   1.131 +    CleanupStack::Pop(); // threadName
   1.132 +    threadName.Close();
   1.133 +
   1.134 +	// set priority
   1.135 +	iThread.SetPriority(EPriorityMore);
   1.136 +	}
   1.137 +
   1.138 +
   1.139 +/**
   1.140 +Construct a CThreadContext object
   1.141 +*/
   1.142 +CThreadContext::CThreadContext()
   1.143 +	:
   1.144 +	iError(KErrNone)
   1.145 +	{
   1.146 +	__FNLOG("CThreadContext::CThreadContext");
   1.147 +	}
   1.148 +
   1.149 +/**
   1.150 +Destructor
   1.151 +*/
   1.152 +CThreadContext::~CThreadContext()
   1.153 +	{
   1.154 +	__FNLOG("CThreadContext::~CThreadContext");
   1.155 +	__PRINT(_L("Closing Critical Section"));
   1.156 +	iCritSect.Close();
   1.157 +	__PRINT(_L("Killing ThreadContext"));
   1.158 +	iThread.Kill(0);
   1.159 +	__PRINT(_L("Closing ThreadContext"));
   1.160 +	iThread.Close();
   1.161 +	}
   1.162 +
   1.163 +//-----------------------------------------------
   1.164 +
   1.165 +/**
   1.166 +Construct a CWriteDriveThread object
   1.167 +*/
   1.168 +CWriteDriveThread* CWriteDriveThread::NewL()
   1.169 +	{
   1.170 +	__FNLOG("CWriteDriveThread::NewL");
   1.171 +	CWriteDriveThread* self = new (ELeave) CWriteDriveThread();
   1.172 +	CleanupStack::PushL(self);
   1.173 +	self->ConstructL();
   1.174 +	CleanupStack::Pop();
   1.175 +	return self;
   1.176 +	}
   1.177 +
   1.178 +/**
   1.179 +Construct a CWriteDriveThread object
   1.180 +*/
   1.181 +void CWriteDriveThread::ConstructL()
   1.182 +	{
   1.183 +	__FNLOG("CWriteDriveThread::ConstructL");
   1.184 +	TBuf<16> name = _L("MassStorageWrite");
   1.185 +	iThreadContext = CThreadContext::NewL(name, ThreadFunction, this);
   1.186 +	// There are two free pointers to start with so initialise the semaphore with 1
   1.187 +	User::LeaveIfError(iProducerSem.CreateLocal(1));
   1.188 +	User::LeaveIfError(iConsumerSem.CreateLocal(0));
   1.189 +	
   1.190 +	iThreadContext->Resume();
   1.191 +	}
   1.192 +
   1.193 +/**
   1.194 +Construct a CWriteDriveThread object
   1.195 +*/
   1.196 +CWriteDriveThread::CWriteDriveThread() 
   1.197 +	: iIsCommandWrite10(EFalse)
   1.198 +	{
   1.199 +	__FNLOG("CWriteDriveThread::CWriteDriveThread");
   1.200 +	}
   1.201 +
   1.202 +/**
   1.203 +Destructor
   1.204 +*/
   1.205 +CWriteDriveThread::~CWriteDriveThread()
   1.206 +	{
   1.207 +	__FNLOG("CWriteDriveThread::~CWriteDriveThread");
   1.208 +	delete iThreadContext;
   1.209 +	}
   1.210 +
   1.211 +/**
   1.212 +This function is called when the thread is initially scheduled.
   1.213 +
   1.214 +@param aSelf Pointer to self to facilitate call to member method.
   1.215 +*/
   1.216 +TInt CWriteDriveThread::ThreadFunction(TAny* aSelf)
   1.217 +	{
   1.218 +	__FNLOG("CWriteDriveThread::ThreadFunction");
   1.219 +	CWriteDriveThread* self = static_cast<CWriteDriveThread*>(aSelf);
   1.220 +	return self->WriteToDrive();
   1.221 +	}
   1.222 +
   1.223 +/**
   1.224 +Writes the data pointed to by iDescWritePtr to the drive.
   1.225 +*/
   1.226 +TInt CWriteDriveThread::WriteToDrive()
   1.227 +	{
   1.228 +	__FNLOG("\tCWriteDriveThread::WriteToDrive");
   1.229 +
   1.230 +	// One-off convenience variable assignment
   1.231 +	TBlockDesc* &desc = iThreadContext->iBuffer.iDescWritePtr;
   1.232 +	
   1.233 +	for(;;)
   1.234 +		{
   1.235 +		iConsumerSem.Wait();
   1.236 +		__PRINT(_L("\tWaiting on Write CS..."));
   1.237 +		iThreadContext->iCritSect.Wait();
   1.238 +		// +++ WRITE CS STARTS HERE +++
   1.239 +		__PRINT1(_L("\tNow using as write buffer: iBuf%d"), iThreadContext->iBuffer.GetBufferNumber(&desc->iBuf));
   1.240 +#ifdef MEASURE_AND_DISPLAY_WRITE_TIME
   1.241 +		RDebug::Print(_L("\tSCSI: writing %d bytes\n"), desc->iBuf.Length());
   1.242 +		TTime t0, t1;
   1.243 +		t0.HomeTime();
   1.244 +#else
   1.245 +		__PRINT1(_L("\tSCSI: writing %d bytes\n"), desc->iBuf.Length());
   1.246 +#endif
   1.247 +		// Write buffer to disk
   1.248 +
   1.249 +#ifdef INJECT_ERROR
   1.250 +		if (desc->iBuf[0] == '2')
   1.251 +		{
   1.252 +			desc->iBuf[0] = 'x';
   1.253 +			RDebug::Printf("Injecting error");
   1.254 +		}
   1.255 +
   1.256 +		
   1.257 +		RDebug::Printf("%08lx %x [%x] [%x]", desc->iByteOffset, desc->iBuf.Length(), 
   1.258 +			desc->iBuf[0],
   1.259 +			desc->iBuf[desc->iBuf.Length()-1]);
   1.260 +#endif
   1.261 +
   1.262 +		iThreadContext->iError = iThreadContext->iDrive->Write(desc->iByteOffset, desc->iBuf,iThreadContext->iDrive->IsWholeMediaAccess());
   1.263 +#ifdef INJECT_ERROR
   1.264 +		if (desc->iBuf[0] == 'x')
   1.265 +		{
   1.266 +			iThreadContext->iError = KErrUnknown;
   1.267 +		}
   1.268 +#endif
   1.269 +
   1.270 +#ifdef MEASURE_AND_DISPLAY_WRITE_TIME
   1.271 +		t1.HomeTime();
   1.272 +		const TTimeIntervalMicroSeconds time = t1.MicroSecondsFrom(t0);
   1.273 +		const TUint time_ms = I64LOW(time.Int64() / 1000);
   1.274 +		RDebug::Print(_L("SCSI: write took %d ms\n"), time_ms);
   1.275 +#endif
   1.276 +		iCallback((TUint8*) (desc->iBuf.Ptr()), iCallbackParameter);
   1.277 +		iWriteCounter--;
   1.278 +		ASSERT(iWriteCounter >= 0);
   1.279 +
   1.280 +		__PRINT(_L("\tSignalling Write CS"));
   1.281 +		iThreadContext->iCritSect.Signal();
   1.282 +		// +++ WRITE CS ENDS HERE +++
   1.283 +		iProducerSem.Signal();
   1.284 +		}
   1.285 +	}
   1.286 +
   1.287 +/**
   1.288 +Initiates writing data pointed to by iReadBuf to the drive and resumes the thread. Writing 
   1.289 +is completed by the ThreadFunction when the thread is resumed.
   1.290 +
   1.291 +@param aDrive Drive to write to.
   1.292 +@param aOffset Write offset.
   1.293 +*/
   1.294 +TInt CWriteDriveThread::WriteDriveData(CMassStorageDrive* aDrive, const TInt64& aOffset, TPtrC8& aDes, ProcessWriteCompleteFunc aFunc, TAny* aPtr)
   1.295 +	{
   1.296 +	// Check error code from previous write
   1.297 +	const TInt r = iThreadContext->iError;
   1.298 +	if (r != KErrNone)
   1.299 +		{
   1.300 +		__PRINT1(_L("Error after previous write = 0x%x \n"), r);
   1.301 +		return KErrAbort;
   1.302 +        }
   1.303 +
   1.304 +	// Swap the two buffer pointers
   1.305 +	iProducerSem.Wait();
   1.306 +	__PRINT(_L("Waiting on Write CS..."));
   1.307 +	// +++ WRITE CS STARTS HERE +++
   1.308 +	iThreadContext->iCritSect.Wait();
   1.309 +
   1.310 +	// New DB First read into the iDescReadPtr pointer,
   1.311 +	// then swap,so that write pointer points to correct location, as the ptr pointed to by iDescWritePtr is what is written from in WriteToDrive 
   1.312 +	iThreadContext->iBuffer.iDescReadPtr->iBuf.Set((TUint8*)aDes.Ptr(), aDes.Length(), KMaxBufSize );
   1.313 +	
   1.314 +	iCallback = aFunc;
   1.315 +	iCallbackParameter = aPtr;
   1.316 +
   1.317 +	iWriteCounter++;
   1.318 +	iThreadContext->iBuffer.SwapDesc();
   1.319 +	// Prepare variables for next write
   1.320 +	iThreadContext->iDrive = aDrive;
   1.321 +	iThreadContext->iBuffer.iDescWritePtr->iByteOffset = aOffset;
   1.322 +	// +++ WRITE CS ENDS HERE +++
   1.323 +	__PRINT(_L("Signalling Write CS..."));
   1.324 +	iThreadContext->iCritSect.Signal();
   1.325 +
   1.326 +	iConsumerSem.Signal();
   1.327 +	return KErrNone;
   1.328 +}
   1.329 +
   1.330 +
   1.331 +void CWriteDriveThread::WaitForWriteEmpty()
   1.332 +{
   1.333 +	while(iWriteCounter > 0)
   1.334 +		{
   1.335 +		User::After(100);
   1.336 +		}
   1.337 +}
   1.338 +
   1.339 +// Check if the target address range was recently written to, this is to force a
   1.340 +// cache miss when reading from the same sectors that were just written. 
   1.341 +// Optimisation note: this is only needed if the read precache was started
   1.342 +// before the write was completed.
   1.343 +TBool CWriteDriveThread::IsRecentlyWritten(TInt64 aOffset, TInt aLength)
   1.344 +{
   1.345 +	ASSERT(iWriteCounter == 0);
   1.346 +	if (iIsCommandWrite10) //If the previous command is Write10, then discard pre-read as the same buffers are used and will be over written by Write10 
   1.347 +		return ETrue;
   1.348 +	if(aOffset <= iThreadContext->iBuffer.iDescReadPtr->iByteOffset &&
   1.349 +			aOffset + aLength >= iThreadContext->iBuffer.iDescReadPtr->iByteOffset)
   1.350 +		return ETrue;
   1.351 +	if(aOffset >= iThreadContext->iBuffer.iDescReadPtr->iByteOffset &&
   1.352 +			aOffset <= iThreadContext->iBuffer.iDescReadPtr->iByteOffset + iThreadContext->iBuffer.iDescReadPtr->iLength)
   1.353 +		return ETrue;
   1.354 +	if(aOffset <= iThreadContext->iBuffer.iDescWritePtr->iByteOffset &&
   1.355 +			aOffset + aLength >= iThreadContext->iBuffer.iDescReadPtr->iByteOffset)
   1.356 +		return ETrue;
   1.357 +	if(aOffset >= iThreadContext->iBuffer.iDescWritePtr->iByteOffset &&
   1.358 +			aOffset <= iThreadContext->iBuffer.iDescReadPtr->iByteOffset + iThreadContext->iBuffer.iDescReadPtr->iLength)
   1.359 +		return ETrue;
   1.360 +	return EFalse;
   1.361 +}
   1.362 +
   1.363 +//-----------------------------------------------
   1.364 +
   1.365 +/**
   1.366 +Construct a CReadDriveThread object
   1.367 +*/
   1.368 +CReadDriveThread* CReadDriveThread::NewL()
   1.369 +	{
   1.370 +	__FNLOG("CReadDriveThread::NewL");
   1.371 +	CReadDriveThread* self = new (ELeave) CReadDriveThread();
   1.372 +	CleanupStack::PushL(self);
   1.373 +	self->ConstructL();
   1.374 +	CleanupStack::Pop();
   1.375 +	return self;
   1.376 +	}
   1.377 +
   1.378 +/**
   1.379 +Construct a CReadDriveThread object
   1.380 +
   1.381 +@param aName The name to be assigned to this thread.
   1.382 +@pram aThreadFunction Function to be called when thread is initially scheduled.
   1.383 +*/
   1.384 +void CReadDriveThread::ConstructL()
   1.385 +	{
   1.386 +	__FNLOG("CReadDriveThread::ConstructL");
   1.387 +	TBuf<15> name = _L("MassStorageRead");
   1.388 +	iThreadContext = CThreadContext::NewL(name, ThreadFunction, this);
   1.389 +	}
   1.390 +
   1.391 +/**
   1.392 +Construct a CReadDriveThread object
   1.393 +*/
   1.394 +CReadDriveThread::CReadDriveThread()
   1.395 +	:
   1.396 +	iThreadRunning(EFalse)
   1.397 +	{
   1.398 +	__FNLOG("CReadDriveThread::CReadDriveThread");
   1.399 +	}
   1.400 +
   1.401 +/**
   1.402 +Destructor
   1.403 +*/
   1.404 +CReadDriveThread::~CReadDriveThread()
   1.405 +	{
   1.406 +	__FNLOG("CReadDriveThread::~CReadDriveThread");
   1.407 +	delete iThreadContext;
   1.408 +	}
   1.409 +
   1.410 +/**
   1.411 +This function is called when the thread is initially scheduled.
   1.412 +
   1.413 +@param aSelf Pointer to self to facilitate call to member method.
   1.414 +*/
   1.415 +TInt CReadDriveThread::ThreadFunction(TAny* aSelf)
   1.416 +	{
   1.417 +	__FNLOG("CReadDriveThread::ThreadFunction");
   1.418 +	CReadDriveThread* self = static_cast<CReadDriveThread*>(aSelf);
   1.419 +	return self->ReadFromDrive();
   1.420 +	}
   1.421 +
   1.422 +/**
   1.423 +Reads data from the drive with iOffset and iReadLength into memory pointer iReadBuffer
   1.424 +and suspends the thread.
   1.425 +*/
   1.426 +TInt CReadDriveThread::ReadFromDrive()
   1.427 +	{
   1.428 +	__FNLOG("\tCReadDriveThread::ReadFromDrive");
   1.429 +
   1.430 +	// One-off convenience variable assignment
   1.431 +	TBlockDesc* &desc = iThreadContext->iBuffer.iDescWritePtr;
   1.432 +
   1.433 +	for (;;)
   1.434 +		{
   1.435 +		__PRINT(_L("\tWaiting on Read CS..."));
   1.436 +		iThreadContext->iCritSect.Wait();
   1.437 +		// +++ READ CS STARTS HERE +++
   1.438 +		iThreadRunning = ETrue;
   1.439 +		iCompleted = EFalse;
   1.440 +
   1.441 +		__PRINT1(_L("\tNow using as read buffer: iBuf%d"), iThreadContext->iBuffer.GetBufferNumber(&desc->iBuf));
   1.442 + 
   1.443 +#ifdef MEASURE_AND_DISPLAY_READ_TIME
   1.444 +		RDebug::Print(_L("\tSCSI: reading %d bytes\n"), desc->iBuf.Length());
   1.445 +		TTime t0, t1;
   1.446 +		t0.HomeTime();
   1.447 +#else
   1.448 +		__PRINT1(_L("\tSCSI: reading %d bytes\n"), desc->iBuf.Length());
   1.449 +#endif
   1.450 +		// Fill read buffer from disk
   1.451 +		iThreadContext->iError = iThreadContext->iDrive->Read(desc->iByteOffset,
   1.452 +															  desc->iLength,
   1.453 +															  desc->iBuf,
   1.454 +															  iThreadContext->iDrive->IsWholeMediaAccess());
   1.455 +
   1.456 +#ifdef MEASURE_AND_DISPLAY_READ_TIME
   1.457 +		t1.HomeTime();
   1.458 +		const TTimeIntervalMicroSeconds time = t1.MicroSecondsFrom(t0);
   1.459 +		const TUint time_ms = I64LOW(time.Int64() / 1000);
   1.460 +		RDebug::Print(_L("SCSI: read took %d ms\n"), time_ms);
   1.461 +#endif
   1.462 +
   1.463 +		iCompleted = ETrue;
   1.464 +		iThreadRunning = EFalse;
   1.465 +		__PRINT(_L("\tSignalling Read CS"));
   1.466 +		// +++ READ CS ENDS HERE +++
   1.467 +		iThreadContext->iCritSect.Signal();
   1.468 +		// Suspend self
   1.469 +		__PRINT(_L("\tSuspending Read Thread"));
   1.470 +		RThread().Suspend();
   1.471 +		}
   1.472 +	}
   1.473 +
   1.474 +/**
   1.475 +Client read request of a data block from the specified drive. 
   1.476 +If there is no pre-read data that matches the requested Offset and Length then the drive
   1.477 +is read and the next pre-read is setup. If there is matching pre-read data available then
   1.478 +the next pre-read is setup. Finishes by resuming the thread and the ThreadFunciton runs.
   1.479 +
   1.480 +@param aDrive Drive to read from.
   1.481 +@param aOffset Read offset
   1.482 +@param aLength Length 
   1.483 +*/
   1.484 +TBool CReadDriveThread::ReadDriveData(CMassStorageDrive* aDrive,
   1.485 +									  const TInt64& aOffset,
   1.486 +									  TUint32 aLength,
   1.487 +									  TBool aIgnoreCache)
   1.488 +	{
   1.489 +	__MT_READ_PRINT2(_L("\nRead10: offs %ld len %d"), aOffset, aLength);
   1.490 +
   1.491 +	__PRINT(_L("Waiting on Read CS..."));
   1.492 +	iThreadContext->iCritSect.Wait();
   1.493 +	// +++ READ CS STARTS HERE +++
   1.494 +	__ASSERT_DEBUG(!iThreadRunning, User::Panic(_L("MSDC-THREAD"), 666));
   1.495 +
   1.496 +	TBlockDesc* &desc = iThreadContext->iBuffer.iDescReadPtr;
   1.497 +	TBlockDesc* &bgDesc = iThreadContext->iBuffer.iDescWritePtr;
   1.498 +
   1.499 +	if ((!aIgnoreCache) &&
   1.500 +		(iCompleted) &&
   1.501 +		(iThreadContext->iError == KErrNone) &&
   1.502 +		(iThreadContext->iDrive == aDrive) &&
   1.503 +		(bgDesc->iByteOffset == aOffset) &&
   1.504 +		(bgDesc->iLength == aLength))
   1.505 +		{
   1.506 +		// Good: We pre-read the correct data :-)
   1.507 +		__MT_READ_PRINT(_L("Match: Using pre-read data :-) :-) :-) :-)"));
   1.508 +		}
   1.509 +	else
   1.510 +		{
   1.511 +		__MT_READ_PRINT(_L("Not using pre-read data"));
   1.512 +		if (iThreadContext->iError != KErrNone)
   1.513 +			{
   1.514 +			__MT_READ_PRINT1(_L("Pre-read failed: %d"), iThreadContext->iError);
   1.515 +			}
   1.516 +		if (iThreadContext->iDrive != aDrive)
   1.517 +			{
   1.518 +			__MT_READ_PRINT2(_L("Pre-read drive mismatch: pre 0x%08x / act 0x%08x"),
   1.519 +							 iThreadContext->iDrive, aDrive);
   1.520 +			}
   1.521 +		if (desc->iByteOffset != aOffset)
   1.522 +			{
   1.523 +			__MT_READ_PRINT2(_L("Pre-read offset mismatch: pre %ld / act %ld"),
   1.524 +							 desc->iByteOffset, aOffset);
   1.525 +			}
   1.526 +		if (desc->iLength != aLength)
   1.527 +			{
   1.528 +			__MT_READ_PRINT2(_L("Pre-read length mismatch: pre %d / act %d"),
   1.529 +							 desc->iLength, aLength);
   1.530 +			// Potential optimization: If the pre-read was OK but for more data
   1.531 +			// than the host is now asking for, we could still satisfy that
   1.532 +			// request from the pre-read data by shortening the buffer.
   1.533 +			}
   1.534 +		// No valid pre-read data was available - so we have to read it now
   1.535 +		bgDesc->iByteOffset = aOffset;
   1.536 +		bgDesc->iLength = aLength;
   1.537 +		TInt err = aDrive->Read(aOffset,
   1.538 +								aLength,
   1.539 +								bgDesc->iBuf,
   1.540 +								aDrive->IsWholeMediaAccess());
   1.541 +		if (err != KErrNone)
   1.542 +			{
   1.543 +			__PRINT1(_L("Read failed, err=%d\n"), err);
   1.544 +			// +++ READ CS ENDS HERE +++
   1.545 +			__PRINT(_L("Signalling Read CS..."));
   1.546 +			iThreadContext->iCritSect.Signal();
   1.547 +			return EFalse;
   1.548 +			}
   1.549 +		}
   1.550 +
   1.551 +	// Prepare thread variables for next pre-read attempt by the ReadThread
   1.552 +	const TInt64 offs_new = aOffset + aLength;
   1.553 +	iThreadContext->iDrive = aDrive;	// same drive
   1.554 +	desc->iByteOffset = offs_new;		// next block
   1.555 +	desc->iLength = aLength;			// same length
   1.556 +	iCompleted = EFalse;
   1.557 +	iThreadContext->iBuffer.SwapDesc();
   1.558 +
   1.559 +	// +++ READ CS ENDS HERE +++
   1.560 +	__PRINT(_L("Signalling Read CS..."));
   1.561 +	iThreadContext->iCritSect.Signal();
   1.562 +	// Start background read
   1.563 +	__PRINT(_L("Resuming Read Thread"));
   1.564 +	iThreadContext->Resume();
   1.565 +	return ETrue;
   1.566 +	}
   1.567 +
   1.568 +/**
   1.569 +Discard the read buffer. This is used to force a cache miss when reading from
   1.570 +the same sectors that were just written.
   1.571 +*/
   1.572 +void CReadDriveThread::DiscardRead()
   1.573 +{
   1.574 +	__PRINT(_L("Waiting on Read CS in DiscardRead..."));
   1.575 +	iThreadContext->iCritSect.Wait();
   1.576 +	// +++ READ CS STARTS HERE +++
   1.577 +	__PRINT(_L("Discarding pre-read buffer"));
   1.578 +	iCompleted = EFalse;
   1.579 +	iThreadContext->iBuffer.iDescReadPtr->iLength = 0;
   1.580 +
   1.581 +	// +++ READ CS ENDS HERE +++
   1.582 +	__PRINT(_L("Signalling Read CS in DiscardRead..."));
   1.583 +	iThreadContext->iCritSect.Signal();
   1.584 +}
   1.585 +#endif // MSDC_MULTITHREADED
   1.586 +