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 +