|
1 // Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of the License "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include "scsiprot.h" |
|
17 #include "usbmsshared.h" |
|
18 #include "rwdrivethread.h" |
|
19 #include "massstoragedebug.h" |
|
20 |
|
21 // --- |
|
22 |
|
23 #ifdef PRINT_MSDC_MULTITHREADED_READ_INFO |
|
24 #define __MT_READ_PRINT(t) {RDebug::Print(t);} |
|
25 #define __MT_READ_PRINT1(t,a) {RDebug::Print(t,a);} |
|
26 #define __MT_READ_PRINT2(t,a,b) {RDebug::Print(t,a,b);} |
|
27 #else |
|
28 #define __MT_READ_PRINT(t) |
|
29 #define __MT_READ_PRINT1(t,a) |
|
30 #define __MT_READ_PRINT2(t,a,b) |
|
31 #endif // PRINT_MSDC_MULTITHREADED_READ_INFO |
|
32 |
|
33 |
|
34 #ifdef MSDC_MULTITHREADED |
|
35 |
|
36 TBlockDesc::TBlockDesc() |
|
37 :iBuf((TUint8 *)NULL,0,0) |
|
38 { |
|
39 } |
|
40 |
|
41 void TBlockDesc::SetPtr(TPtr8& aDes) |
|
42 { |
|
43 iBuf.Set(aDes); |
|
44 } |
|
45 |
|
46 |
|
47 TBlockDescBuffer::TBlockDescBuffer() |
|
48 { |
|
49 iDescReadPtr = &iDesc1; |
|
50 iDescWritePtr = &iDesc2; |
|
51 } |
|
52 |
|
53 void TBlockDescBuffer::SetUpReadBuf(TPtr8& aDes1, TPtr8& aDes2) |
|
54 { |
|
55 iDesc1.SetPtr(aDes1); |
|
56 iDesc2.SetPtr(aDes2); |
|
57 iDescReadPtr = &iDesc1; |
|
58 iDescWritePtr = &iDesc2; |
|
59 } |
|
60 |
|
61 |
|
62 //----------------------------------------------- |
|
63 |
|
64 /** |
|
65 Construct a CThreadContext object. |
|
66 |
|
67 @param aName The name to be assigned to this thread. |
|
68 @param aThreadFunction Function to be called when thread is initially scheduled. |
|
69 @param aOwner Pointer to the object owning the thread. Used as the parameter to aThreadFunction. |
|
70 */ |
|
71 CThreadContext* CThreadContext::NewL(const TDesC& aName, |
|
72 TThreadFunction aThreadFunction, |
|
73 TAny* aOwner) |
|
74 { |
|
75 __FNLOG("CThreadContext::NewL"); |
|
76 CThreadContext* self = new (ELeave) CThreadContext(); |
|
77 CleanupStack::PushL(self); |
|
78 self->ConstructL(aName, aThreadFunction, aOwner); |
|
79 CleanupStack::Pop(); |
|
80 return self; |
|
81 } |
|
82 |
|
83 /** |
|
84 Construct a CThreadContext object |
|
85 |
|
86 @param aName The name to be assigned to this thread. |
|
87 @param aThreadFunction Function to be called when thread is initially scheduled. |
|
88 @param aOwner Pointer to the object owning the thread. Used as the parameter to aThreadFunction. |
|
89 */ |
|
90 void CThreadContext::ConstructL(const TDesC& aName, |
|
91 TThreadFunction aThreadFunction, |
|
92 TAny* aOwner) |
|
93 { |
|
94 __FNLOG("CThreadContext::ConstructL"); |
|
95 __PRINT(_L("Creating Critical Section")); |
|
96 User::LeaveIfError(iCritSect.CreateLocal()); |
|
97 __PRINT(_L("Creating RThread")); |
|
98 |
|
99 TUint serial(0); // Used to retry creation of a thread in case |
|
100 // one with the same name already exists |
|
101 |
|
102 RBuf threadName; |
|
103 threadName.CreateMaxL(aName.Length() + 8); |
|
104 CleanupClosePushL(threadName); |
|
105 threadName = aName; |
|
106 |
|
107 TInt err; |
|
108 for (;;) |
|
109 { |
|
110 err = iThread.Create(threadName, aThreadFunction, 0x1000, NULL, aOwner); |
|
111 __PRINT2(_L("CThreadContext::ConstructL Created thread %S err=%d"), &threadName, err); |
|
112 |
|
113 // for a restart wait and retry until old thread is gone |
|
114 if (err == KErrAlreadyExists) |
|
115 { |
|
116 User::After(10 * 1000); // 10 mS |
|
117 threadName = aName; |
|
118 threadName.AppendNumFixedWidth(serial, EDecimal, 8); |
|
119 ++serial; |
|
120 } |
|
121 else |
|
122 { |
|
123 break; |
|
124 } |
|
125 } |
|
126 |
|
127 User::LeaveIfError(err); |
|
128 CleanupStack::Pop(); // threadName |
|
129 threadName.Close(); |
|
130 |
|
131 // set priority |
|
132 iThread.SetPriority(EPriorityMore); |
|
133 } |
|
134 |
|
135 |
|
136 /** |
|
137 Construct a CThreadContext object |
|
138 */ |
|
139 CThreadContext::CThreadContext() |
|
140 : |
|
141 iError(KErrNone) |
|
142 { |
|
143 __FNLOG("CThreadContext::CThreadContext"); |
|
144 } |
|
145 |
|
146 /** |
|
147 Destructor |
|
148 */ |
|
149 CThreadContext::~CThreadContext() |
|
150 { |
|
151 __FNLOG("CThreadContext::~CThreadContext"); |
|
152 __PRINT(_L("Closing Critical Section")); |
|
153 iCritSect.Close(); |
|
154 __PRINT(_L("Killing ThreadContext")); |
|
155 iThread.Kill(0); |
|
156 __PRINT(_L("Closing ThreadContext")); |
|
157 iThread.Close(); |
|
158 } |
|
159 |
|
160 //----------------------------------------------- |
|
161 |
|
162 /** |
|
163 Construct a CWriteDriveThread object |
|
164 */ |
|
165 CWriteDriveThread* CWriteDriveThread::NewL() |
|
166 { |
|
167 __FNLOG("CWriteDriveThread::NewL"); |
|
168 CWriteDriveThread* self = new (ELeave) CWriteDriveThread(); |
|
169 CleanupStack::PushL(self); |
|
170 self->ConstructL(); |
|
171 CleanupStack::Pop(); |
|
172 return self; |
|
173 } |
|
174 |
|
175 /** |
|
176 Construct a CWriteDriveThread object |
|
177 */ |
|
178 void CWriteDriveThread::ConstructL() |
|
179 { |
|
180 __FNLOG("CWriteDriveThread::ConstructL"); |
|
181 TBuf<16> name = _L("MassStorageWrite"); |
|
182 iThreadContext = CThreadContext::NewL(name, ThreadFunction, this); |
|
183 // There are two free pointers to start with so initialise the semaphore with 1 |
|
184 User::LeaveIfError(iProducerSem.CreateLocal(1)); |
|
185 User::LeaveIfError(iConsumerSem.CreateLocal(0)); |
|
186 |
|
187 iThreadContext->Resume(); |
|
188 } |
|
189 |
|
190 /** |
|
191 Construct a CWriteDriveThread object |
|
192 */ |
|
193 CWriteDriveThread::CWriteDriveThread() |
|
194 : iIsCommandWrite10(EFalse) |
|
195 { |
|
196 __FNLOG("CWriteDriveThread::CWriteDriveThread"); |
|
197 } |
|
198 |
|
199 /** |
|
200 Destructor |
|
201 */ |
|
202 CWriteDriveThread::~CWriteDriveThread() |
|
203 { |
|
204 __FNLOG("CWriteDriveThread::~CWriteDriveThread"); |
|
205 delete iThreadContext; |
|
206 } |
|
207 |
|
208 /** |
|
209 This function is called when the thread is initially scheduled. |
|
210 |
|
211 @param aSelf Pointer to self to facilitate call to member method. |
|
212 */ |
|
213 TInt CWriteDriveThread::ThreadFunction(TAny* aSelf) |
|
214 { |
|
215 __FNLOG("CWriteDriveThread::ThreadFunction"); |
|
216 CWriteDriveThread* self = static_cast<CWriteDriveThread*>(aSelf); |
|
217 return self->WriteToDrive(); |
|
218 } |
|
219 |
|
220 /** |
|
221 Writes the data pointed to by iDescWritePtr to the drive. |
|
222 */ |
|
223 TInt CWriteDriveThread::WriteToDrive() |
|
224 { |
|
225 __FNLOG("\tCWriteDriveThread::WriteToDrive"); |
|
226 |
|
227 // One-off convenience variable assignment |
|
228 TBlockDesc* &desc = iThreadContext->iBuffer.iDescWritePtr; |
|
229 |
|
230 for(;;) |
|
231 { |
|
232 iConsumerSem.Wait(); |
|
233 __PRINT(_L("\tWaiting on Write CS...")); |
|
234 iThreadContext->iCritSect.Wait(); |
|
235 // +++ WRITE CS STARTS HERE +++ |
|
236 __PRINT1(_L("\tNow using as write buffer: iBuf%d"), iThreadContext->iBuffer.GetBufferNumber(&desc->iBuf)); |
|
237 #ifdef MEASURE_AND_DISPLAY_WRITE_TIME |
|
238 RDebug::Print(_L("\tSCSI: writing %d bytes\n"), desc->iBuf.Length()); |
|
239 TTime t0, t1; |
|
240 t0.HomeTime(); |
|
241 #else |
|
242 __PRINT1(_L("\tSCSI: writing %d bytes\n"), desc->iBuf.Length()); |
|
243 #endif |
|
244 // Write buffer to disk |
|
245 |
|
246 #ifdef INJECT_ERROR |
|
247 if (desc->iBuf[0] == '2') |
|
248 { |
|
249 desc->iBuf[0] = 'x'; |
|
250 RDebug::Printf("Injecting error"); |
|
251 } |
|
252 |
|
253 |
|
254 RDebug::Printf("%08lx %x [%x] [%x]", desc->iByteOffset, desc->iBuf.Length(), |
|
255 desc->iBuf[0], |
|
256 desc->iBuf[desc->iBuf.Length()-1]); |
|
257 #endif |
|
258 |
|
259 iThreadContext->iError = iThreadContext->iDrive->Write(desc->iByteOffset, desc->iBuf,iThreadContext->iDrive->IsWholeMediaAccess()); |
|
260 #ifdef INJECT_ERROR |
|
261 if (desc->iBuf[0] == 'x') |
|
262 { |
|
263 iThreadContext->iError = KErrUnknown; |
|
264 } |
|
265 #endif |
|
266 |
|
267 #ifdef MEASURE_AND_DISPLAY_WRITE_TIME |
|
268 t1.HomeTime(); |
|
269 const TTimeIntervalMicroSeconds time = t1.MicroSecondsFrom(t0); |
|
270 const TUint time_ms = I64LOW(time.Int64() / 1000); |
|
271 RDebug::Print(_L("SCSI: write took %d ms\n"), time_ms); |
|
272 #endif |
|
273 iCallback((TUint8*) (desc->iBuf.Ptr()), iCallbackParameter); |
|
274 iWriteCounter--; |
|
275 ASSERT(iWriteCounter >= 0); |
|
276 |
|
277 __PRINT(_L("\tSignalling Write CS")); |
|
278 iThreadContext->iCritSect.Signal(); |
|
279 // +++ WRITE CS ENDS HERE +++ |
|
280 iProducerSem.Signal(); |
|
281 } |
|
282 } |
|
283 |
|
284 /** |
|
285 Initiates writing data pointed to by iReadBuf to the drive and resumes the thread. Writing |
|
286 is completed by the ThreadFunction when the thread is resumed. |
|
287 |
|
288 @param aDrive Drive to write to. |
|
289 @param aOffset Write offset. |
|
290 */ |
|
291 TInt CWriteDriveThread::WriteDriveData(CMassStorageDrive* aDrive, const TInt64& aOffset, TPtrC8& aDes, ProcessWriteCompleteFunc aFunc, TAny* aPtr) |
|
292 { |
|
293 // Check error code from previous write |
|
294 const TInt r = iThreadContext->iError; |
|
295 if (r != KErrNone) |
|
296 { |
|
297 __PRINT1(_L("Error after previous write = 0x%x \n"), r); |
|
298 return KErrAbort; |
|
299 } |
|
300 |
|
301 // Swap the two buffer pointers |
|
302 iProducerSem.Wait(); |
|
303 __PRINT(_L("Waiting on Write CS...")); |
|
304 // +++ WRITE CS STARTS HERE +++ |
|
305 iThreadContext->iCritSect.Wait(); |
|
306 |
|
307 // New DB First read into the iDescReadPtr pointer, |
|
308 // then swap,so that write pointer points to correct location, as the ptr pointed to by iDescWritePtr is what is written from in WriteToDrive |
|
309 iThreadContext->iBuffer.iDescReadPtr->iBuf.Set((TUint8*)aDes.Ptr(), aDes.Length(), KMaxBufSize ); |
|
310 |
|
311 iCallback = aFunc; |
|
312 iCallbackParameter = aPtr; |
|
313 |
|
314 iWriteCounter++; |
|
315 iThreadContext->iBuffer.SwapDesc(); |
|
316 // Prepare variables for next write |
|
317 iThreadContext->iDrive = aDrive; |
|
318 iThreadContext->iBuffer.iDescWritePtr->iByteOffset = aOffset; |
|
319 // +++ WRITE CS ENDS HERE +++ |
|
320 __PRINT(_L("Signalling Write CS...")); |
|
321 iThreadContext->iCritSect.Signal(); |
|
322 |
|
323 iConsumerSem.Signal(); |
|
324 return KErrNone; |
|
325 } |
|
326 |
|
327 |
|
328 void CWriteDriveThread::WaitForWriteEmpty() |
|
329 { |
|
330 while(iWriteCounter > 0) |
|
331 { |
|
332 User::After(100); |
|
333 } |
|
334 } |
|
335 |
|
336 // Check if the target address range was recently written to, this is to force a |
|
337 // cache miss when reading from the same sectors that were just written. |
|
338 // Optimisation note: this is only needed if the read precache was started |
|
339 // before the write was completed. |
|
340 TBool CWriteDriveThread::IsRecentlyWritten(TInt64 aOffset, TInt aLength) |
|
341 { |
|
342 ASSERT(iWriteCounter == 0); |
|
343 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 |
|
344 return ETrue; |
|
345 if(aOffset <= iThreadContext->iBuffer.iDescReadPtr->iByteOffset && |
|
346 aOffset + aLength >= iThreadContext->iBuffer.iDescReadPtr->iByteOffset) |
|
347 return ETrue; |
|
348 if(aOffset >= iThreadContext->iBuffer.iDescReadPtr->iByteOffset && |
|
349 aOffset <= iThreadContext->iBuffer.iDescReadPtr->iByteOffset + iThreadContext->iBuffer.iDescReadPtr->iLength) |
|
350 return ETrue; |
|
351 if(aOffset <= iThreadContext->iBuffer.iDescWritePtr->iByteOffset && |
|
352 aOffset + aLength >= iThreadContext->iBuffer.iDescReadPtr->iByteOffset) |
|
353 return ETrue; |
|
354 if(aOffset >= iThreadContext->iBuffer.iDescWritePtr->iByteOffset && |
|
355 aOffset <= iThreadContext->iBuffer.iDescReadPtr->iByteOffset + iThreadContext->iBuffer.iDescReadPtr->iLength) |
|
356 return ETrue; |
|
357 return EFalse; |
|
358 } |
|
359 |
|
360 //----------------------------------------------- |
|
361 |
|
362 /** |
|
363 Construct a CReadDriveThread object |
|
364 */ |
|
365 CReadDriveThread* CReadDriveThread::NewL() |
|
366 { |
|
367 __FNLOG("CReadDriveThread::NewL"); |
|
368 CReadDriveThread* self = new (ELeave) CReadDriveThread(); |
|
369 CleanupStack::PushL(self); |
|
370 self->ConstructL(); |
|
371 CleanupStack::Pop(); |
|
372 return self; |
|
373 } |
|
374 |
|
375 /** |
|
376 Construct a CReadDriveThread object |
|
377 |
|
378 @param aName The name to be assigned to this thread. |
|
379 @pram aThreadFunction Function to be called when thread is initially scheduled. |
|
380 */ |
|
381 void CReadDriveThread::ConstructL() |
|
382 { |
|
383 __FNLOG("CReadDriveThread::ConstructL"); |
|
384 TBuf<15> name = _L("MassStorageRead"); |
|
385 iThreadContext = CThreadContext::NewL(name, ThreadFunction, this); |
|
386 } |
|
387 |
|
388 /** |
|
389 Construct a CReadDriveThread object |
|
390 */ |
|
391 CReadDriveThread::CReadDriveThread() |
|
392 : |
|
393 iThreadRunning(EFalse) |
|
394 { |
|
395 __FNLOG("CReadDriveThread::CReadDriveThread"); |
|
396 } |
|
397 |
|
398 /** |
|
399 Destructor |
|
400 */ |
|
401 CReadDriveThread::~CReadDriveThread() |
|
402 { |
|
403 __FNLOG("CReadDriveThread::~CReadDriveThread"); |
|
404 delete iThreadContext; |
|
405 } |
|
406 |
|
407 /** |
|
408 This function is called when the thread is initially scheduled. |
|
409 |
|
410 @param aSelf Pointer to self to facilitate call to member method. |
|
411 */ |
|
412 TInt CReadDriveThread::ThreadFunction(TAny* aSelf) |
|
413 { |
|
414 __FNLOG("CReadDriveThread::ThreadFunction"); |
|
415 CReadDriveThread* self = static_cast<CReadDriveThread*>(aSelf); |
|
416 return self->ReadFromDrive(); |
|
417 } |
|
418 |
|
419 /** |
|
420 Reads data from the drive with iOffset and iReadLength into memory pointer iReadBuffer |
|
421 and suspends the thread. |
|
422 */ |
|
423 TInt CReadDriveThread::ReadFromDrive() |
|
424 { |
|
425 __FNLOG("\tCReadDriveThread::ReadFromDrive"); |
|
426 |
|
427 // One-off convenience variable assignment |
|
428 TBlockDesc* &desc = iThreadContext->iBuffer.iDescWritePtr; |
|
429 |
|
430 for (;;) |
|
431 { |
|
432 __PRINT(_L("\tWaiting on Read CS...")); |
|
433 iThreadContext->iCritSect.Wait(); |
|
434 // +++ READ CS STARTS HERE +++ |
|
435 iThreadRunning = ETrue; |
|
436 iCompleted = EFalse; |
|
437 |
|
438 __PRINT1(_L("\tNow using as read buffer: iBuf%d"), iThreadContext->iBuffer.GetBufferNumber(&desc->iBuf)); |
|
439 |
|
440 #ifdef MEASURE_AND_DISPLAY_READ_TIME |
|
441 RDebug::Print(_L("\tSCSI: reading %d bytes\n"), desc->iBuf.Length()); |
|
442 TTime t0, t1; |
|
443 t0.HomeTime(); |
|
444 #else |
|
445 __PRINT1(_L("\tSCSI: reading %d bytes\n"), desc->iBuf.Length()); |
|
446 #endif |
|
447 // Fill read buffer from disk |
|
448 iThreadContext->iError = iThreadContext->iDrive->Read(desc->iByteOffset, |
|
449 desc->iLength, |
|
450 desc->iBuf, |
|
451 iThreadContext->iDrive->IsWholeMediaAccess()); |
|
452 |
|
453 #ifdef MEASURE_AND_DISPLAY_READ_TIME |
|
454 t1.HomeTime(); |
|
455 const TTimeIntervalMicroSeconds time = t1.MicroSecondsFrom(t0); |
|
456 const TUint time_ms = I64LOW(time.Int64() / 1000); |
|
457 RDebug::Print(_L("SCSI: read took %d ms\n"), time_ms); |
|
458 #endif |
|
459 |
|
460 iCompleted = ETrue; |
|
461 iThreadRunning = EFalse; |
|
462 __PRINT(_L("\tSignalling Read CS")); |
|
463 // +++ READ CS ENDS HERE +++ |
|
464 iThreadContext->iCritSect.Signal(); |
|
465 // Suspend self |
|
466 __PRINT(_L("\tSuspending Read Thread")); |
|
467 RThread().Suspend(); |
|
468 } |
|
469 } |
|
470 |
|
471 /** |
|
472 Client read request of a data block from the specified drive. |
|
473 If there is no pre-read data that matches the requested Offset and Length then the drive |
|
474 is read and the next pre-read is setup. If there is matching pre-read data available then |
|
475 the next pre-read is setup. Finishes by resuming the thread and the ThreadFunciton runs. |
|
476 |
|
477 @param aDrive Drive to read from. |
|
478 @param aOffset Read offset |
|
479 @param aLength Length |
|
480 */ |
|
481 TBool CReadDriveThread::ReadDriveData(CMassStorageDrive* aDrive, |
|
482 const TInt64& aOffset, |
|
483 TUint32 aLength, |
|
484 TBool aIgnoreCache) |
|
485 { |
|
486 __MT_READ_PRINT2(_L("\nRead10: offs %ld len %d"), aOffset, aLength); |
|
487 |
|
488 __PRINT(_L("Waiting on Read CS...")); |
|
489 iThreadContext->iCritSect.Wait(); |
|
490 // +++ READ CS STARTS HERE +++ |
|
491 __ASSERT_DEBUG(!iThreadRunning, User::Panic(_L("MSDC-THREAD"), 666)); |
|
492 |
|
493 TBlockDesc* &desc = iThreadContext->iBuffer.iDescReadPtr; |
|
494 TBlockDesc* &bgDesc = iThreadContext->iBuffer.iDescWritePtr; |
|
495 |
|
496 if ((!aIgnoreCache) && |
|
497 (iCompleted) && |
|
498 (iThreadContext->iError == KErrNone) && |
|
499 (iThreadContext->iDrive == aDrive) && |
|
500 (bgDesc->iByteOffset == aOffset) && |
|
501 (bgDesc->iLength == aLength)) |
|
502 { |
|
503 // Good: We pre-read the correct data :-) |
|
504 __MT_READ_PRINT(_L("Match: Using pre-read data :-) :-) :-) :-)")); |
|
505 } |
|
506 else |
|
507 { |
|
508 __MT_READ_PRINT(_L("Not using pre-read data")); |
|
509 if (iThreadContext->iError != KErrNone) |
|
510 { |
|
511 __MT_READ_PRINT1(_L("Pre-read failed: %d"), iThreadContext->iError); |
|
512 } |
|
513 if (iThreadContext->iDrive != aDrive) |
|
514 { |
|
515 __MT_READ_PRINT2(_L("Pre-read drive mismatch: pre 0x%08x / act 0x%08x"), |
|
516 iThreadContext->iDrive, aDrive); |
|
517 } |
|
518 if (desc->iByteOffset != aOffset) |
|
519 { |
|
520 __MT_READ_PRINT2(_L("Pre-read offset mismatch: pre %ld / act %ld"), |
|
521 desc->iByteOffset, aOffset); |
|
522 } |
|
523 if (desc->iLength != aLength) |
|
524 { |
|
525 __MT_READ_PRINT2(_L("Pre-read length mismatch: pre %d / act %d"), |
|
526 desc->iLength, aLength); |
|
527 // Potential optimization: If the pre-read was OK but for more data |
|
528 // than the host is now asking for, we could still satisfy that |
|
529 // request from the pre-read data by shortening the buffer. |
|
530 } |
|
531 // No valid pre-read data was available - so we have to read it now |
|
532 bgDesc->iByteOffset = aOffset; |
|
533 bgDesc->iLength = aLength; |
|
534 TInt err = aDrive->Read(aOffset, |
|
535 aLength, |
|
536 bgDesc->iBuf, |
|
537 aDrive->IsWholeMediaAccess()); |
|
538 if (err != KErrNone) |
|
539 { |
|
540 __PRINT1(_L("Read failed, err=%d\n"), err); |
|
541 // +++ READ CS ENDS HERE +++ |
|
542 __PRINT(_L("Signalling Read CS...")); |
|
543 iThreadContext->iCritSect.Signal(); |
|
544 return EFalse; |
|
545 } |
|
546 } |
|
547 |
|
548 // Prepare thread variables for next pre-read attempt by the ReadThread |
|
549 const TInt64 offs_new = aOffset + aLength; |
|
550 iThreadContext->iDrive = aDrive; // same drive |
|
551 desc->iByteOffset = offs_new; // next block |
|
552 desc->iLength = aLength; // same length |
|
553 iCompleted = EFalse; |
|
554 iThreadContext->iBuffer.SwapDesc(); |
|
555 |
|
556 // +++ READ CS ENDS HERE +++ |
|
557 __PRINT(_L("Signalling Read CS...")); |
|
558 iThreadContext->iCritSect.Signal(); |
|
559 // Start background read |
|
560 __PRINT(_L("Resuming Read Thread")); |
|
561 iThreadContext->Resume(); |
|
562 return ETrue; |
|
563 } |
|
564 |
|
565 /** |
|
566 Discard the read buffer. This is used to force a cache miss when reading from |
|
567 the same sectors that were just written. |
|
568 */ |
|
569 void CReadDriveThread::DiscardRead() |
|
570 { |
|
571 __PRINT(_L("Waiting on Read CS in DiscardRead...")); |
|
572 iThreadContext->iCritSect.Wait(); |
|
573 // +++ READ CS STARTS HERE +++ |
|
574 __PRINT(_L("Discarding pre-read buffer")); |
|
575 iCompleted = EFalse; |
|
576 iThreadContext->iBuffer.iDescReadPtr->iLength = 0; |
|
577 |
|
578 // +++ READ CS ENDS HERE +++ |
|
579 __PRINT(_L("Signalling Read CS in DiscardRead...")); |
|
580 iThreadContext->iCritSect.Signal(); |
|
581 } |
|
582 #endif // MSDC_MULTITHREADED |
|
583 |