• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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