1 /* ------------------------------------------------------------------
2 * Copyright (C) 1998-2009 PacketVideo
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
13 * express or implied.
14 * See the License for the specific language governing permissions
15 * and limitations under the License.
16 * -------------------------------------------------------------------
17 */
18 /*
19 * =============================================================================
20 * Name : oscl_file_async_read.cpp
21 * Part of :
22 * Interface :
23 * Description :
24 * Version :
25 * =============================================================================
26 */
27
28
29
30 #include "oscl_file_async_read.h"
31 #include "oscl_file_handle.h"
32 #include "pvlogger.h"
33 #include "oscl_file_native.h"
34
35 //When set, this code will create a duplicate file handle and
36 //verify every single API call against results from that handle.
37 //Great for debugging.
38 //Make sure this is 0 in checked-in code!
39 #define VERIFY_THIS 0
40
41 // ================== Class OsclAsyncFileBuffer ===================================== //
42
NewL(int32 aBufferSize,int32 aId)43 OsclAsyncFileBuffer* OsclAsyncFileBuffer::NewL(int32 aBufferSize, int32 aId)
44 {
45 OsclAsyncFileBuffer* self = OSCL_NEW(OsclAsyncFileBuffer, (aBufferSize, aId));
46 OsclError::PushL(self);
47 self->ConstructL();
48 OsclError::Pop();
49 return self;
50 }
51
OsclAsyncFileBuffer(int32 aBufferSize,int32 aId)52 OsclAsyncFileBuffer::OsclAsyncFileBuffer(int32 aBufferSize, int32 aId) :
53 iBufferSize(aBufferSize), iId(aId)
54 {
55 iBuffer = NULL;
56 iOffset = 0;
57 iInUse = false;
58 iLength = 0;
59 iValid = false;
60 }
61
~OsclAsyncFileBuffer()62 OsclAsyncFileBuffer::~OsclAsyncFileBuffer()
63 {
64 if (iBuffer)
65 OsclBuf::Delete(iBuffer);
66 }
67
ConstructL()68 void OsclAsyncFileBuffer::ConstructL()
69 {
70 iBuffer = OsclBuf::NewL(iBufferSize);
71 }
72
UpdateData()73 void OsclAsyncFileBuffer::UpdateData()
74 {
75 iValid = true;
76 iLength = iBuffer->Length();
77 }
78
Buffer()79 OsclBuf* OsclAsyncFileBuffer::Buffer()
80 {
81 return iBuffer;
82 }
83
HasThisOffset(TOsclFileOffset aOffset)84 bool OsclAsyncFileBuffer::HasThisOffset(TOsclFileOffset aOffset)
85 {
86 if (!iValid)
87 return false;
88 if ((aOffset >= iOffset) && (aOffset <= iOffset + iLength - 1))
89 return true;
90 return false;
91 }
92
93
94 // ================== Class OsclAsyncFile ===================================== //
95
NewL(OsclNativeFile & aFile,int32 aCacheSize,PVLogger * aLogger)96 OsclAsyncFile* OsclAsyncFile::NewL(OsclNativeFile& aFile, int32 aCacheSize, PVLogger* aLogger)
97 {
98 OsclAsyncFile* self = OSCL_NEW(OsclAsyncFile, (aFile, aCacheSize, aLogger));
99 OsclError::PushL(self);
100 self->ConstructL();
101 OsclError::Pop();
102 return self;
103 }
104
Delete(OsclAsyncFile * a)105 void OsclAsyncFile::Delete(OsclAsyncFile*a)
106 {
107 if (a)
108 OSCL_DELETE(a);
109 }
110
~OsclAsyncFile()111 OsclAsyncFile::~OsclAsyncFile()
112 {
113 //Stop the tread, cancel any requests for Run()
114 //and remove AO from the scheduler
115
116 StopAsyncReadThread();
117
118 Cancel();
119 RemoveFromScheduler();
120
121 if (iNativeFileDuplicate)
122 {
123 OSCL_DELETE(iNativeFileDuplicate);
124 }
125
126 for (uint32 i = 0; i < iDataBufferArray.size(); i++)
127 OSCL_DELETE(iDataBufferArray[i]);
128
129 iDataBufferArray.clear();
130 iSortedDataBufferArray.clear();
131 iLinkedDataBufferArray.clear();
132 OSCL_DELETE(iDataBuffer);
133
134 }
135
136
OsclAsyncFile(OsclNativeFile & aFile,int32 aCacheSize,PVLogger * aLogger)137 OsclAsyncFile::OsclAsyncFile(OsclNativeFile& aFile, int32 aCacheSize, PVLogger* aLogger)
138 : OsclActiveObject(OsclActiveObject::EPriorityHighest, "OsclAsyncFile"),
139 iNativeFile(aFile),
140 iNativeFileDuplicate(NULL),
141 iTotalCacheSize(aCacheSize),
142 iStartAsyncRead(false),
143 iReadPtrDummyLen(0),
144 iReadPtr(0, iReadPtrDummyLen, 0)
145 {
146 iLogger = aLogger;
147
148 iNumOfRun = 0;
149 iNumOfRunErr = 0;
150 iHasNativeAsyncRead = iNativeFile.HasAsyncRead();
151
152 // Init thread state tracking variable(s)
153 if (iHasNativeAsyncRead)
154 {
155 /* For native async read set the thread state to active
156 * since this is logically equivalent to the read thread
157 * already running for the non-native implementation. Some
158 * of the shared logic in this class uses this state variable
159 * so it needs to be set properly for the native async case also.
160 */
161 iAsyncReadThreadState = EAsyncReadActive;
162 }
163 else
164 {
165 /* otherwise there is not native async read support so this
166 * class will need to launch a reader thread. Initialize the
167 * state to EAsyncReadNotActive to record the fact that the thread
168 * has not been launched yet.
169 */
170 iAsyncReadThreadState = EAsyncReadNotActive;
171 }
172 iAsyncReadThreadExitFlag = false;
173
174
175
176 //this is the number of buffers we allocate internally for the linked buffer
177 //list. There is also one extra buffer of this size-- the 'iDataBuffer'.
178 iKCacheBufferCount = 4;
179
180 //'min bytes read ahead' is how much data we will try to keep available
181 //from the async reads. It must be <= iKCacheBufferCount * iTotalCacheSize;
182 iKMinBytesReadAhead = (3 * iTotalCacheSize);
183 OSCL_ASSERT(iKMinBytesReadAhead <= (int32)(iKCacheBufferCount*iTotalCacheSize));
184
185 //this is the size of each read request to the native file system.
186 //it must be <= the individual buffer size, so we use 8k as the default,
187 //but then limit it to the input buffer size.
188 iKAsyncReadBufferSize = 8 * 1024 ;
189 if ((uint32)iKAsyncReadBufferSize > iTotalCacheSize)
190 iKAsyncReadBufferSize = iTotalCacheSize;
191
192 }
193
ConstructL()194 void OsclAsyncFile::ConstructL()
195 {
196 //Create a duplicate file handle
197 //@todo: on symbian we need to make sure this is a duplicate of iNativeFile.
198 //on non-symbian it doesn't matter-- you can just create a new instance.
199 iNativeFileDuplicate = OSCL_NEW(OsclNativeFile, ());
200
201 #if(VERIFY_THIS)
202 iNativeFileVerify = OSCL_NEW(OsclNativeFile, ());
203 iVerifyCount = 0;
204 #endif
205
206 iFileSize = iNativeFile.Size();
207
208 iDataBufferArray.reserve(iKCacheBufferCount);
209
210 // Create data buffers
211
212 OsclAsyncFileBuffer* tmpBuffer;
213 for (int32 i = 0; i < iKCacheBufferCount; i++)
214 {
215 tmpBuffer = OsclAsyncFileBuffer::NewL(iTotalCacheSize, i);
216 iDataBufferArray.push_back(tmpBuffer);
217 }
218
219 // create a local instance of OsclAsyncFileBuffer
220 iDataBuffer = OsclAsyncFileBuffer::NewL(iTotalCacheSize, -1);
221
222 AddToScheduler();
223
224 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
225 (0, "OsclAsyncFile(0x%x)::ConstructL bufferSize %d numBuffers %d readSize %d readAhead %d"
226 , this, iTotalCacheSize, iKCacheBufferCount, iKAsyncReadBufferSize, iKMinBytesReadAhead));
227 }
228
Open(const oscl_wchar * filename,uint32 mode,const OsclNativeFileParams & params,Oscl_FileServer & fileserv)229 int32 OsclAsyncFile::Open(const oscl_wchar *filename, uint32 mode
230 , const OsclNativeFileParams& params
231 , Oscl_FileServer& fileserv)
232 {
233 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
234 (0, "OsclAsyncFile(0x%x)::Open mode %d ", this, mode));
235
236 //open the duplicate file handle
237 if (!iNativeFileDuplicate)
238 return -1;
239 int32 result = iNativeFileDuplicate->Open(filename, mode, params, fileserv);
240
241 //launch the thread
242 if (!iHasNativeAsyncRead)
243 {
244 LaunchAsyncReadThread();
245 }
246
247 iFilePosition = 0;
248 iSyncFilePosition = 0;
249 iAsyncFilePosition = 0;
250 iLastUserFileRead = 0;
251
252 //start the async read if file is opened and thread is running
253 if ((result == 0) && (iAsyncReadThreadState == EAsyncReadActive))
254 {
255 StartAsyncRead(true);
256 }
257 else
258 {
259 result = -1;
260 }
261
262 #if(VERIFY_THIS)
263 if (iNativeFileVerify->Open(filename, mode, params, fileserv) != result)
264 OSCL_ASSERT(0);
265 #endif
266 return result;
267 }
268
Open(const char * filename,uint32 mode,const OsclNativeFileParams & params,Oscl_FileServer & fileserv)269 int32 OsclAsyncFile::Open(const char *filename, uint32 mode
270 , const OsclNativeFileParams& params
271 , Oscl_FileServer& fileserv)
272 {
273 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
274 (0, "OsclAsyncFile(0x%x)::Open filename '%s' mode %d ", this, filename, mode));
275
276 //open the duplicate file handle
277 if (!iNativeFileDuplicate)
278 return -1;
279 int32 result = iNativeFileDuplicate->Open(filename, mode, params, fileserv);
280
281 //launch the thread
282 if (!iHasNativeAsyncRead)
283 {
284 LaunchAsyncReadThread();
285 }
286
287 iFilePosition = 0;
288 iSyncFilePosition = 0;
289 iAsyncFilePosition = 0;
290 iLastUserFileRead = 0;
291
292 //start the async read if file is opened and thread is running
293 if ((result == 0) && (iAsyncReadThreadState == EAsyncReadActive))
294 {
295 StartAsyncRead(true);
296 }
297 else
298 {
299 result = -1;
300 }
301
302 #if(VERIFY_THIS)
303 if (iNativeFileVerify->Open(filename, mode, params, fileserv) != result)
304 OSCL_ASSERT(0);
305 #endif
306 return result;
307 }
308
Close()309 int32 OsclAsyncFile::Close()
310 {
311 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
312 (0, "OsclAsyncFile(0x%x)::Close", this));
313
314 //stop the async read
315 StartAsyncRead(false);
316
317 //stop the thread
318 if (!iHasNativeAsyncRead)
319 {
320 StopAsyncReadThread();
321 }
322
323 //close the duplicate file handle
324 if (!iNativeFileDuplicate)
325 return -1;
326 int32 result = iNativeFileDuplicate->Close();
327
328 #if(VERIFY_THIS)
329 if (iNativeFileVerify->Close() != result)
330 OSCL_ASSERT(0);
331 #endif
332 return result;
333 }
334
StartAsyncRead(bool aStartAsyncRead)335 void OsclAsyncFile::StartAsyncRead(bool aStartAsyncRead)
336 {
337 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
338 (0, "OsclAsyncFile(0x%x)::StartAsyncRead %d", this, aStartAsyncRead));
339
340 iStartAsyncRead = aStartAsyncRead;
341 // Do the asynchronous read
342 }
343
Read(OsclAny * aBuffer,uint32 aDataSize,uint32 aNumElements)344 uint32 OsclAsyncFile::Read(OsclAny* aBuffer, uint32 aDataSize, uint32 aNumElements)
345 {
346 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
347 (0, "OsclAsyncFile(0x%x)::Read size %d numelements %d", this, aDataSize, aNumElements));
348
349 #if(VERIFY_THIS)
350 if (iVerifyCount == 1315)
351 fprintf(stderr, "");
352 //verify file pos before the read
353 if (iNativeFileVerify->Tell() != iFilePosition)
354 OSCL_ASSERT(0);
355 #endif
356
357 //Pull data from the internal buffers until we get the requested amount.
358
359 //note: there's no logic to prevent reading partial elements here.
360
361 uint32 bytesToRead = aDataSize * aNumElements;
362
363 uint32 bytesRead = 0;
364
365 uint8* databuf = NULL;
366
367 uint8* dest = (uint8*)aBuffer;
368
369 uint32 thisread;
370
371 uint32 nbytes;
372
373 while (bytesToRead > 0)
374 {
375 //note: the "doRead" routine only handles reads of <= iTotalCacheSize,
376 //so limit the read size here.
377 thisread = (bytesToRead > iTotalCacheSize) ? iTotalCacheSize : bytesToRead;
378
379 nbytes = doRead(databuf, aDataSize, thisread / aDataSize, iFilePosition);
380 if (nbytes)
381 {
382 oscl_memcpy(dest, databuf, nbytes);
383 dest += nbytes;
384 bytesRead += nbytes;
385 bytesToRead -= nbytes;
386 }
387 else
388 break;//no more data or an error occurred.
389 }
390
391 #if(VERIFY_THIS)
392 {
393 uint8* buf = (uint8*)OSCL_MALLOC(aDataSize * aNumElements);
394 int32 result2 = iNativeFileVerify->Read(buf, aDataSize, aNumElements);
395 if (result2 != bytesRead / aDataSize)
396 OSCL_ASSERT(0);
397 //verify file pos after the read
398 if (iNativeFileVerify->Tell() != iFilePosition)
399 OSCL_ASSERT(0);
400 //verify the data
401 if (oscl_memcmp(buf, aBuffer, bytesRead) != NULL)
402 OSCL_ASSERT(0);
403 OSCL_FREE(buf);
404 iVerifyCount++;
405 }
406 #endif
407
408 //return number of whole elements read.
409 return bytesRead / aDataSize;
410 }
411
412 //This routine locates the data in one of the internal buffers and returns a pointer.
413 //If data is not available, it will do a blocking read.
414 //The amount of data requested cannot exceed the internal buffer size.
415 //Note this returns bytes read, not number of elements read.
doRead(uint8 * & aBuffer1,uint32 aDataSize,uint32 aNumElements,TOsclFileOffset aOffset)416 uint32 OsclAsyncFile::doRead(uint8 *& aBuffer1, uint32 aDataSize, uint32 aNumElements, TOsclFileOffset aOffset)
417 {
418 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
419 (0, "OsclAsyncFile(0x%x)::doRead size %d numelements %d offset %d", this, aDataSize, aNumElements, aOffset));
420
421 //this routine only handles requests <= the individual buffer size-- it is
422 //the caller's responsibility to check.
423 OSCL_ASSERT(aNumElements*aDataSize <= iTotalCacheSize);
424
425 // Clear buffers in use
426 for (int32 i = 0; i < iKCacheBufferCount; i++)
427 {
428 iDataBufferArray[i]->CleanInUse();
429 }
430
431 // Check if we have data already in the buffer
432 OsclAsyncFileBuffer* dataBuffer = NULL;
433 int32 bufferFoundId;
434 uint32 bytesRead = 0;
435
436 // Check if we have data available
437 if (FindDataBuffer(dataBuffer, bufferFoundId, aOffset, aNumElements*aDataSize))
438 {
439 // Mark buffer in use
440 dataBuffer->SetInUse();
441 // update user pointer
442 OsclBuf* buff = dataBuffer->Buffer();
443 OsclPtr ptrRead = buff->Des();
444 aBuffer1 = const_cast<uint8*>(ptrRead.Ptr());
445 // offset pointer to correct location
446 aBuffer1 += (aOffset - dataBuffer->Offset());
447 bytesRead = dataBuffer->Length() - (uint32)(aOffset - dataBuffer->Offset());
448 // Redo queue of linked buffers
449 ReOrderBuffersQueue(bufferFoundId);
450 }
451 else
452 {
453 //Data is not available-- must do a synchronous read.
454
455 int32 ret = iNativeFileDuplicate->Seek(aOffset, Oscl_File::SEEKSET);
456 if (ret != 0)
457 {
458 // This is not good
459 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
460 (0, "OsclAsyncFile(0x%x)::doRead Seek Failed returned %d offset %d", this, ret, aOffset));
461 return 0;
462 }
463
464 // reset linked data bufferarray
465 iLinkedDataBufferArray.clear();
466
467 // Get buffer to read
468 OsclAsyncFileBuffer* dataBuffer;
469 bool bufferAvailable = GetNextDataBuffer(dataBuffer, aOffset);
470 if (!bufferAvailable)
471 {
472 // This case should never happen since iLinkedDataBufferArray was just closed
473 OSCL_ASSERT(0);
474 return 0;
475 }
476
477 dataBuffer->SetOffset(aOffset);
478
479 // Buffer is in use
480 dataBuffer->SetInUse();
481 OsclBuf* readBuffer = dataBuffer->Buffer();
482 OsclPtr ptrCurrentBuffer = readBuffer->Des();
483 ptrCurrentBuffer.SetLength(0);
484
485 // Do read. Might as well fill up the entire buffer while we're at it, so
486 // go ahead and read 'iTotalCacheSize'. The caller has already verified that
487 // the number of bytes requested in this read is <= 'iTotalCacheSize'.
488 bytesRead = iNativeFileDuplicate->Read(ptrCurrentBuffer.Ptr(), 1, iTotalCacheSize);
489 ptrCurrentBuffer.SetLength(bytesRead);
490
491 // update data
492 dataBuffer->UpdateData();
493
494 // return value
495 aBuffer1 = (uint8*)ptrCurrentBuffer.Ptr();
496
497 iSyncFilePosition = aOffset + bytesRead;
498
499 iLinkedDataBufferArray.push_back(dataBuffer);
500 }
501
502 // set the returned byte count to the actual bytes available from this call
503 // or the amount requested, whichever is less.
504 if (bytesRead > aDataSize*aNumElements)
505 bytesRead = aDataSize * aNumElements;
506
507 // Update virtual file pointer position
508 iFilePosition = aOffset + bytesRead;
509
510 // This is the position from where we read
511 iLastUserFileRead = iFilePosition;
512
513 // Continue reading
514 if (iStartAsyncRead)
515 {
516 UpdateReading();
517 }
518
519 return bytesRead;
520 }
521
FindDataBuffer(OsclAsyncFileBuffer * & aDataBuffer,int32 & aBufferId,TOsclFileOffset aOffset,int32 aSize)522 bool OsclAsyncFile::FindDataBuffer(OsclAsyncFileBuffer*& aDataBuffer, int32& aBufferId, TOsclFileOffset aOffset, int32 aSize)
523 {
524 // Look for the requested value on the queue
525 OsclAsyncFileBuffer* tmpDataBuffer;
526 int32 i;
527 bool found = false;
528 for (i = 0; i < iKCacheBufferCount; i++)
529 {
530 tmpDataBuffer = iDataBufferArray[i];
531 if (tmpDataBuffer->HasThisOffset(aOffset))
532 {
533 // we want a linked buffer
534 if (IsLinkedDataBuffer(tmpDataBuffer))
535 {
536 found = true;
537 break;
538 }
539 else
540 {
541 // check if it can be linked
542 if (CanBeLinked(tmpDataBuffer))
543 {
544 found = true;
545 break;
546 }
547 }
548 }
549 }
550
551 if (!found)
552 return false;
553
554 aDataBuffer = tmpDataBuffer;
555
556 // return id of the buffer (needed because down below we might return a different buffer than tmpDataBuffer)
557 // but we still need the id
558 aBufferId = tmpDataBuffer->Id();
559
560 // Check if we have enough data to return in a single buffer
561 TOsclFileOffset maxOffset = tmpDataBuffer->Offset() + tmpDataBuffer->Length();
562
563 // the easy case
564 if (aOffset + aSize <= maxOffset)
565 return true;
566
567 // Last option, check if we can concatenate required data from two buffers
568 int32 availableData = (int32)(maxOffset - aOffset);
569
570 // check again in the buffers for the remaining part of the data
571 OsclAsyncFileBuffer* tmpDataBuffer2 = iDataBufferArray[0]; //Init pointer to clean-up compiler warning=MG
572 for (i = 0; i < iKCacheBufferCount; i++)
573 {
574 tmpDataBuffer2 = iDataBufferArray[i];
575 if (tmpDataBuffer2->HasThisOffset(maxOffset))
576 {
577 break;
578 }
579 }
580
581 // extra data not found
582 if (i == iKCacheBufferCount)
583 return false;
584
585 // make sure data is valid
586 if (tmpDataBuffer2->Offset() != maxOffset)
587 return false;
588
589 // Set the buffer
590 aDataBuffer = iDataBuffer;
591
592 // length to copy from second buffer
593 int32 lengthToCopy;
594 if (tmpDataBuffer2->Length() + availableData >= aSize)
595 lengthToCopy = aSize - availableData;
596 else
597 return false; //shouldn't happen
598
599 //note: could copy directly into target buffer here instead of into iDataBuffer,
600 //but would take some code re-structuring.
601
602 // extra data found, proceed to concatenate with memcpy
603 OsclBuf* buffer = iDataBuffer->Buffer();
604 OsclPtr ptrDes = buffer->Des();
605 ptrDes.Zero();
606 OsclPtrC tmpDataPtr = tmpDataBuffer->Buffer()->DesC();
607 OsclPtrC temp_tmpDataPtr = tmpDataPtr.Right(availableData);
608 tmpDataPtr.Set(&temp_tmpDataPtr);
609 ptrDes.Append(tmpDataPtr);
610 // find out the length to copy
611 OsclBuf* buffer2 = tmpDataBuffer2->Buffer();
612 OsclPtrC ptrDes2 = buffer2->DesC();
613 OsclPtrC temp_ptrDes2 = ptrDes2.Left(lengthToCopy);
614 ptrDes2.Set(&temp_ptrDes2);
615 ptrDes.Append(ptrDes2);
616 iDataBuffer->UpdateData();
617 iDataBuffer->SetOffset(aOffset);
618
619 return true;
620 }
621
622
UpdateReading()623 void OsclAsyncFile::UpdateReading()
624 {
625 // Wait for Run() to process data
626 if (IsBusy())
627 return;
628
629 int32 bytesReadAhead = BytesReadAhead();
630
631 // don't need to read more
632 if (bytesReadAhead >= iKMinBytesReadAhead)
633 {
634 return;
635 }
636
637 TOsclFileOffset posToReadFrom = iFilePosition + bytesReadAhead;
638
639 // check for eof. We stop reading
640 if (posToReadFrom == iFileSize)
641 {
642 return;
643 }
644
645 // Start next read
646
647 StartNextRead(posToReadFrom);
648 }
649
BytesReadAhead()650 int32 OsclAsyncFile::BytesReadAhead()
651 {
652 // Get the maximum offset of the last element in the linked buffer array
653 int32 index = iLinkedDataBufferArray.size() - 1;
654 if (index == -1)
655 {
656 return 0;
657 }
658
659 OsclAsyncFileBuffer* tmpDataBuffer = iLinkedDataBufferArray[index];
660 TOsclFileOffset maxOffset = tmpDataBuffer->Offset() + tmpDataBuffer->Length();
661 int32 bytesReadAhead = (int32)(maxOffset - iLastUserFileRead);
662
663 return bytesReadAhead;
664 }
665
SortDataBuffers()666 int32 OsclAsyncFile::SortDataBuffers()
667 {
668 Oscl_Vector<OsclAsyncFileBuffer*, OsclMemAllocator> tmpArray;
669 // Append valid and not in use elements only
670 OsclAsyncFileBuffer* tmpDataBuffer;
671 uint32 i;
672 for (i = 0; i < (uint32)iKCacheBufferCount; i++)
673 {
674 tmpDataBuffer = iDataBufferArray[i];
675 if (tmpDataBuffer->IsValid())
676 {
677 tmpArray.push_back(tmpDataBuffer);
678 }
679 }
680
681 iSortedDataBufferArray.clear();
682
683 // sort them out
684 for (i = 0; i < tmpArray.size(); i++)
685 {
686 if (i == 0)
687 iSortedDataBufferArray.push_back(tmpArray[i]);
688 else
689 {
690 OsclAsyncFileBuffer* tmpDataBuffer = tmpArray[i];
691 bool insertedData = false;
692 // check where to insert new element
693 for (uint32 j = 0; j < iSortedDataBufferArray.size(); j++)
694 {
695 OsclAsyncFileBuffer* tmpDataBuffer2 = iSortedDataBufferArray[j];
696 if (tmpDataBuffer->Offset() < tmpDataBuffer2->Offset())
697 {
698 // Insert element
699 //iSortedDataBufferArray.Insert(tmpDataBuffer, j);
700 {
701 //note: there's no insert in oscl vector-- push to end then
702 //bubble down to desired spot.
703 iSortedDataBufferArray.push_back(tmpDataBuffer);
704 for (uint32 k = (uint32)iSortedDataBufferArray.size() - 1; k > j; k--)
705 {
706 OsclAsyncFileBuffer* temp = iSortedDataBufferArray[k-1];
707 iSortedDataBufferArray[k-1] = iSortedDataBufferArray[k];
708 iSortedDataBufferArray[k] = temp;
709 }
710 }
711 insertedData = true;
712 break;
713 }
714 }
715
716 if (!insertedData)
717 {
718 iSortedDataBufferArray.push_back(tmpDataBuffer);
719 }
720 }
721
722 }
723 tmpArray.clear();
724
725 return 0;
726 }
727
GetNextDataBuffer(OsclAsyncFileBuffer * & aDataBuffer,TOsclFileOffset aFilePointerToReadFrom)728 bool OsclAsyncFile::GetNextDataBuffer(OsclAsyncFileBuffer*& aDataBuffer, TOsclFileOffset aFilePointerToReadFrom)
729 {
730 uint32 i;
731
732 OSCL_UNUSED_ARG(aFilePointerToReadFrom);
733
734 // Loop through the buffer list and find out the first buffer available for use
735 OsclAsyncFileBuffer* tmpDataBuffer;
736 for (i = 0; i < iDataBufferArray.size(); i++)
737 {
738 tmpDataBuffer = iDataBufferArray[i];
739 if (!tmpDataBuffer->IsValid())
740 {
741 aDataBuffer = tmpDataBuffer;
742 return true;
743 }
744 }
745
746 // if all buffers are valid, return the next one with oldest data
747 TOsclFileOffset smallerOffset = 0;
748 OsclAsyncFileBuffer* dataBufferToUse = NULL;
749 OsclAsyncFileBuffer* lastOptionBufferToUse = NULL;
750 for (i = 0; i < iDataBufferArray.size(); i++)
751 {
752 tmpDataBuffer = iDataBufferArray[i];
753 // Only return valid buffers
754 if (tmpDataBuffer->IsInUse() || IsLinkedDataBuffer(tmpDataBuffer) || (tmpDataBuffer == iDataBufferInUse))
755 continue;
756
757 // We check if the buffer can be linked
758 // if so, we rather take another one that is not of any use.
759 if (CanBeLinked(tmpDataBuffer))
760 {
761 // we remember it just in case there's no other buffer to use
762 lastOptionBufferToUse = dataBufferToUse;
763 continue;
764 }
765
766 if (dataBufferToUse == NULL)
767 {
768 smallerOffset = tmpDataBuffer->Offset();
769 dataBufferToUse = tmpDataBuffer;
770 }
771 else
772 {
773 if (tmpDataBuffer->Offset() < smallerOffset)
774 {
775 smallerOffset = tmpDataBuffer->Offset();
776 dataBufferToUse = tmpDataBuffer;
777 }
778 }
779
780 }
781
782 // check what we have
783 if (dataBufferToUse == NULL)
784 dataBufferToUse = lastOptionBufferToUse;
785
786 // If we didn't find any buffer, return false
787 if (dataBufferToUse == NULL)
788 return false;
789
790 // Initialize buffer to use
791 aDataBuffer = dataBufferToUse;
792
793 return true;
794 }
795
ReOrderBuffersQueue(int32 aFirstBufferId)796 void OsclAsyncFile::ReOrderBuffersQueue(int32 aFirstBufferId)
797 {
798 // reset array
799 iLinkedDataBufferArray.clear();
800
801 // Sort data buffers first
802 SortDataBuffers();
803
804 // aFirstBufferId points to the index of the buffer in iDataBufferArray
805 OsclAsyncFileBuffer* tmpDataBuffer = iDataBufferArray[aFirstBufferId];
806
807 // Add the head element
808 iLinkedDataBufferArray.push_back(tmpDataBuffer);
809
810 // Look for the linked elements
811 TOsclFileOffset offset = tmpDataBuffer->Offset() + tmpDataBuffer->Length();
812 for (uint32 i = 0; i < iSortedDataBufferArray.size(); i++)
813 {
814 tmpDataBuffer = iSortedDataBufferArray[i];
815 if (offset == tmpDataBuffer->Offset())
816 {
817 offset = tmpDataBuffer->Offset() + tmpDataBuffer->Length();
818 iLinkedDataBufferArray.push_back(tmpDataBuffer);
819 }
820 }
821 }
822
IsLinkedDataBuffer(OsclAsyncFileBuffer * aDataBuffer)823 bool OsclAsyncFile::IsLinkedDataBuffer(OsclAsyncFileBuffer* aDataBuffer)
824 {
825 for (uint32 i = 0; i < iLinkedDataBufferArray.size(); i++)
826 {
827 OsclAsyncFileBuffer* tmpDataBuffer = iLinkedDataBufferArray[i];
828 if (tmpDataBuffer == aDataBuffer)
829 {
830 return true;
831 }
832 }
833 return false;
834 }
835
CanBeLinked(OsclAsyncFileBuffer * aDataBuffer)836 bool OsclAsyncFile::CanBeLinked(OsclAsyncFileBuffer* aDataBuffer)
837 {
838 if (iLinkedDataBufferArray.size() == 0)
839 {
840 return false;
841 }
842
843 if (aDataBuffer->Offset() + aDataBuffer->Length() == iLinkedDataBufferArray[0]->Offset())
844 {
845 return true;
846 }
847
848 return false;
849 }
850
851 //The AO is invoked when the asynchronous Read request is complete.
Run()852 void OsclAsyncFile::Run()
853 {
854 iNumOfRun++;
855 if (iStatus != OSCL_REQUEST_ERR_NONE)
856 {
857 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
858 (0, "OsclAsyncFile(0x%x)::Run Error!!!", this));
859 iNumOfRunErr++;
860 return;
861 }
862
863 if (iAsyncReadThreadState == EAsyncReadNotActive)
864 {
865 //This is a safeguard against the situation when the thread is already stopped,
866 //but the last completed request triggers the Run() execution
867 return;
868 }
869
870 //update the iReadPtr descriptor length with the result of the async read.
871 //Note all reads are of size 1, so we can use "num elements" interchangeably with
872 //bytes here.
873 if (iHasNativeAsyncRead)
874 iReadPtr.SetLength(iNativeFile.GetReadAsyncNumElements());
875 else
876 iReadPtr.SetLength(iAsyncReadNumBytes);
877
878 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
879 (0, "OsclAsyncFile(0x%x)::Run numBytes %d ", this, iReadPtr.Length()));
880
881 // First of all, update the internal file pointer
882 // and the current buffer
883 OsclBuf* readBuffer = iDataBufferInUse->Buffer();
884 OsclPtr ptrCurrentBuffer = readBuffer->Des();
885 int32 bytesRead = iReadPtr.Length();
886 ptrCurrentBuffer.SetLength(ptrCurrentBuffer.Length() + bytesRead);
887 iAsyncFilePosition += bytesRead;
888 iDataBufferInUse->UpdateData();
889
890 // check how many bytes ahead of the user file position we have
891 // bool seekDone = false;
892 int32 bytesReadAhead = BytesReadAhead();
893
894 // next position to read from
895 TOsclFileOffset posToReadFrom = iLastUserFileRead + bytesReadAhead;
896
897 // check for eof. We stop reading
898 if (posToReadFrom == iFileSize)
899 {
900 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
901 (0, "OsclAsyncFile(0x%x)::Run Reached end of file ", this));
902 return;
903 }
904
905 // don't need to read more for now
906 if (bytesReadAhead >= iKMinBytesReadAhead)
907 {
908 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
909 (0, "OsclAsyncFile(0x%x)::Run Reached the read-ahead count", this));
910 return;
911 }
912
913 // Start next read
914 StartNextRead(posToReadFrom);
915 }
916
DoCancel()917 void OsclAsyncFile::DoCancel()
918 {
919 if (!iHasNativeAsyncRead)
920 {
921 if (iAsyncReadThreadState == EAsyncReadNotActive)
922 {
923 //in case thread exited with some request active, this will
924 //complete it
925 OsclActiveObject::DoCancel();
926 }
927 else
928 {
929 //in this case, thread is active. since there's no way to
930 //interrupt the thread's blocking read call, just do nothing
931 //here, then scheduler will wait on request completion.
932 }
933 }
934 }
935
StartNextRead(TOsclFileOffset aPosToReadFrom)936 void OsclAsyncFile::StartNextRead(TOsclFileOffset aPosToReadFrom)
937 {
938 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
939 (0, "OsclAsyncFile(0x%x)::StartNextRead pos %d ", this, aPosToReadFrom));
940
941 // Determine from which file pointer we're going to read
942 if (aPosToReadFrom == iAsyncFilePosition)
943 {
944 // just continue reading
945 OsclBuf* readBuffer = iDataBufferInUse->Buffer();
946 OsclPtr ptrCurrentBuffer = readBuffer->Des();
947 bool validBuffer = true;
948 // Extra validation. This should never be false, but let's check it
949 if (iDataBufferInUse->Offset() + iDataBufferInUse->Length() != aPosToReadFrom)
950 {
951 validBuffer = false;
952 }
953
954 // Check if we need a new buffer
955 if (validBuffer && (iTotalCacheSize - ptrCurrentBuffer.Length() < (uint32)iKAsyncReadBufferSize))
956 {
957 // This is the case where we could get NULL if
958 // we're trying to read ahead more data than what the
959 // buffers can hold. We just add some protection in case
960 // someone makes a wrong configuration
961 bool availableBuffer = GetNextDataBuffer(iDataBufferInUse, aPosToReadFrom);
962 if (!availableBuffer)
963 {
964 return;
965 }
966 // we link the new buffer as soon as we get it
967 iLinkedDataBufferArray.push_back(iDataBufferInUse);
968 iDataBufferInUse->SetOffset(aPosToReadFrom);
969 OsclBuf* newReadBuffer = iDataBufferInUse->Buffer();
970 OsclPtr ptrNewReadBuffer = newReadBuffer->Des();
971 ptrNewReadBuffer.SetLength(0);
972 iDataBufferInUse->UpdateData();
973 iReadPtr.Set((uint8*)ptrNewReadBuffer.Ptr(), 0, iKAsyncReadBufferSize);
974 }
975 else
976 {
977 iReadPtr.Set((uint8*)(ptrCurrentBuffer.Ptr() + ptrCurrentBuffer.Length()), 0, iKAsyncReadBufferSize);
978 }
979 }
980 else if (aPosToReadFrom == iSyncFilePosition)
981 {
982 // update the async file pointer to the desired location
983 int32 result = iNativeFile.Seek(aPosToReadFrom, Oscl_File::SEEKSET);
984 if (result != 0)
985 {
986 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
987 (0, "OsclAsyncFile(0x%x)::StartNextRead Seek failed, return %d, offset %d ", this, result, aPosToReadFrom));
988 return;
989 }
990
991 // we need to swap the file pointers
992 TOsclFileOffset tmpPosition = iSyncFilePosition;
993 iSyncFilePosition = iAsyncFilePosition;
994 iAsyncFilePosition = tmpPosition;
995
996 // We need a new buffer
997 bool availableBuffer = GetNextDataBuffer(iDataBufferInUse, aPosToReadFrom);
998 if (!availableBuffer)
999 {
1000 // This is an error condition. There should always be a buffer available
1001 OSCL_ASSERT(0);
1002 return;
1003 }
1004 // we link the new buffer as soon as we get it
1005 iLinkedDataBufferArray.push_back(iDataBufferInUse);
1006 iDataBufferInUse->SetOffset(aPosToReadFrom);
1007 // Initialize the read pointer (iReadPtr)
1008 OsclBuf* readBuffer = iDataBufferInUse->Buffer();
1009 OsclPtr ptrCurrentBuffer = readBuffer->Des();
1010 // reset length
1011 ptrCurrentBuffer.SetLength(0);
1012 iDataBufferInUse->UpdateData();
1013 iReadPtr.Set((uint8*)ptrCurrentBuffer.Ptr(), 0, iKAsyncReadBufferSize);
1014 }
1015 else
1016 {
1017 // This case should never happen.. check it out
1018 OSCL_ASSERT(0);
1019 return;
1020 }
1021
1022 // Issue the asynchronous read request.
1023
1024 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
1025 (0, "OsclAsyncFile(0x%x)::StartNextRead Issuing async read, size %d ", this, iKAsyncReadBufferSize));
1026
1027 if (iHasNativeAsyncRead)
1028 {
1029 //Activate the AO that will handle read completion.
1030 PendForExec();
1031
1032 //Start the native async read operation
1033 int32 result = iNativeFile.ReadAsync(iReadPtr.Ptr(), 1, iKAsyncReadBufferSize, StatusRef());
1034
1035 //if it failed to start, then cancel the request with an error.
1036 if (result != 0)
1037 PendComplete(OSCL_REQUEST_ERR_GENERAL);
1038 }
1039 else
1040 {
1041 //Start the non-native async read operation.
1042
1043 StartNonNativeAsyncRead();
1044 //the AO will run when read is complete.
1045 }
1046 }
1047
Seek(TOsclFileOffset offset,Oscl_File::seek_type origin)1048 int32 OsclAsyncFile::Seek(TOsclFileOffset offset, Oscl_File::seek_type origin)
1049 {
1050 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
1051 (0, "OsclAsyncFile(0x%x)::Seek offset %d origin %d", this, offset, origin));
1052
1053 if (origin == Oscl_File::SEEKCUR)
1054 {
1055 iFilePosition += offset;
1056 }
1057 else if (origin == Oscl_File::SEEKSET)
1058 {
1059 iFilePosition = offset;
1060 }
1061 else if (origin == Oscl_File::SEEKEND)
1062 {
1063 iFilePosition = iFileSize + offset;
1064 }
1065
1066 //some sanity checks.
1067 OSCL_ASSERT(iFilePosition >= 0);
1068 OSCL_ASSERT(iFilePosition <= iFileSize);
1069
1070 #if(VERIFY_THIS)
1071 if (iNativeFileVerify->Seek(offset, origin) != 0)
1072 OSCL_ASSERT(0);
1073 //also verify current position
1074 if (iNativeFileVerify->Tell() != iFilePosition)
1075 OSCL_ASSERT(0);
1076 #endif
1077 return 0;
1078 }
1079
1080
Tell()1081 TOsclFileOffset OsclAsyncFile::Tell()
1082 {
1083 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
1084 (0, "OsclAsyncFile(0x%x)::Tell pos %d ", this, iFilePosition));
1085
1086 #if(VERIFY_THIS)
1087 if (iNativeFileVerify->Tell() != iFilePosition)
1088 OSCL_ASSERT(0);
1089 #endif
1090 return iFilePosition;
1091 }
1092
EndOfFile()1093 int32 OsclAsyncFile::EndOfFile()
1094 {
1095 int32 result = (iFilePosition >= iFileSize) ? 1 : 0;
1096
1097 #if(VERIFY_THIS)
1098 if (iNativeFileVerify->EndOfFile() != result)
1099 OSCL_ASSERT(0);
1100 #endif
1101
1102 return result;
1103 }
1104
Size()1105 TOsclFileOffset OsclAsyncFile::Size()
1106 {
1107 #if(VERIFY_THIS)
1108 if (iNativeFileVerify->Size() != iFileSize)
1109 OSCL_ASSERT(0);
1110 #endif
1111
1112 return iFileSize;
1113 }
1114
1115 // ================== Non-Native async read implementation ========================== //
1116
LaunchAsyncReadThread()1117 void OsclAsyncFile::LaunchAsyncReadThread()
1118 {
1119 if (iAsyncReadThreadState != EAsyncReadActive)
1120 {
1121 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
1122 (0, "OsclAsyncFile(0x%x)::LaunchAsyncReadThread starting thread ", this));
1123
1124 iAsyncReadSem.Create(0);
1125 iAsyncReadExitSem.Create(0);
1126 OsclThread thread;
1127 OsclProcStatus::eOsclProcError status = thread.Create(iAsyncReadThreadFunc, 4096, (TOsclThreadFuncArg)this);
1128 if (status == OsclProcStatus::SUCCESS_ERROR)
1129 {
1130 //Thread was successfuly started, update thread's state
1131 iAsyncReadThreadState = EAsyncReadActive;
1132 }
1133 }
1134 }
1135
StopAsyncReadThread()1136 void OsclAsyncFile::StopAsyncReadThread()
1137 {
1138 if (iAsyncReadThreadState == EAsyncReadActive)
1139 {
1140 PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
1141 (0, "OsclAsyncFile(0x%x)::StopAsyncReadThread stopping thread ", this));
1142
1143 //signal the thread to exit & wake it up.
1144 iAsyncReadThreadExitFlag = true;
1145 iAsyncReadSem.Signal();
1146
1147 //wait on thread to exit so we can reset the sems safely
1148 iAsyncReadExitSem.Wait();
1149 //Thread is terminated, update thread's state
1150 iAsyncReadThreadState = EAsyncReadNotActive;
1151
1152 iAsyncReadSem.Close();
1153 iAsyncReadExitSem.Close();
1154 }
1155 }
1156
StartNonNativeAsyncRead()1157 void OsclAsyncFile::StartNonNativeAsyncRead()
1158 {
1159 //note: we assume the read requests are nicely serialized here,
1160 //so there's no handling for overlapping requests.
1161
1162 //Activate the AO that will wait on read completion.
1163 PendForExec();
1164
1165 //wake up the thread
1166 iAsyncReadSem.Signal();
1167 }
1168
InThread()1169 void OsclAsyncFile::InThread()
1170 {
1171 while (iAsyncReadThreadExitFlag != true)
1172 {
1173 //wait for the signal
1174 iAsyncReadSem.Wait();
1175
1176 //see if it's a close request
1177 if (iAsyncReadThreadExitFlag == true)
1178 {
1179 break;
1180 }
1181
1182 //issue the read.
1183 iAsyncReadNumBytes = iNativeFile.Read(iReadPtr.Ptr(), 1, iKAsyncReadBufferSize);
1184
1185 //complete the request.
1186 if (IsAdded() && iStatus == OSCL_REQUEST_PENDING)
1187 PendComplete(OSCL_REQUEST_ERR_NONE);
1188 }
1189
1190 // reset the thread exit flag
1191 iAsyncReadThreadExitFlag = false;
1192 //signal that thread is exiting.
1193 iAsyncReadExitSem.Signal();
1194 }
1195
1196 //static thread routine.
iAsyncReadThreadFunc(TOsclThreadFuncArg aArg)1197 TOsclThreadFuncRet OSCL_THREAD_DECL OsclAsyncFile::iAsyncReadThreadFunc(TOsclThreadFuncArg aArg)
1198 {
1199 OsclAsyncFile* This = (OsclAsyncFile*)aArg;
1200
1201 This->InThread();
1202
1203 return 0;
1204 }
1205