blob: 361e597cc3e8898f645afd1e0b1b0a93a5cfe812 [file] [log] [blame]
/* ------------------------------------------------------------------
* Copyright (C) 1998-2009 PacketVideo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied.
* See the License for the specific language governing permissions
* and limitations under the License.
* -------------------------------------------------------------------
*/
/*
* =============================================================================
* Name : oscl_file_async_read.cpp
* Part of :
* Interface :
* Description :
* Version :
* =============================================================================
*/
#include "oscl_file_async_read.h"
#include "oscl_file_handle.h"
#include "pvlogger.h"
#include "oscl_file_native.h"
//When set, this code will create a duplicate file handle and
//verify every single API call against results from that handle.
//Great for debugging.
//Make sure this is 0 in checked-in code!
#define VERIFY_THIS 0
// ================== Class OsclAsyncFileBuffer ===================================== //
OsclAsyncFileBuffer* OsclAsyncFileBuffer::NewL(int32 aBufferSize, int32 aId)
{
OsclAsyncFileBuffer* self = OSCL_NEW(OsclAsyncFileBuffer, (aBufferSize, aId));
OsclError::PushL(self);
self->ConstructL();
OsclError::Pop();
return self;
}
OsclAsyncFileBuffer::OsclAsyncFileBuffer(int32 aBufferSize, int32 aId) :
iBufferSize(aBufferSize), iId(aId)
{
iBuffer = NULL;
iOffset = 0;
iInUse = false;
iLength = 0;
iValid = false;
}
OsclAsyncFileBuffer::~OsclAsyncFileBuffer()
{
if (iBuffer)
OsclBuf::Delete(iBuffer);
}
void OsclAsyncFileBuffer::ConstructL()
{
iBuffer = OsclBuf::NewL(iBufferSize);
}
void OsclAsyncFileBuffer::UpdateData()
{
iValid = true;
iLength = iBuffer->Length();
}
OsclBuf* OsclAsyncFileBuffer::Buffer()
{
return iBuffer;
}
bool OsclAsyncFileBuffer::HasThisOffset(int32 aOffset)
{
if (!iValid)
return false;
if ((aOffset >= iOffset) && (aOffset <= iOffset + iLength - 1))
return true;
return false;
}
// ================== Class OsclAsyncFile ===================================== //
OsclAsyncFile* OsclAsyncFile::NewL(OsclNativeFile& aFile, int32 aCacheSize, PVLogger* aLogger)
{
OsclAsyncFile* self = OSCL_NEW(OsclAsyncFile, (aFile, aCacheSize, aLogger));
OsclError::PushL(self);
self->ConstructL();
OsclError::Pop();
return self;
}
void OsclAsyncFile::Delete(OsclAsyncFile*a)
{
if (a)
OSCL_DELETE(a);
}
OsclAsyncFile::~OsclAsyncFile()
{
//Stop the tread, cancel any requests for Run()
//and remove AO from the scheduler
StopAsyncReadThread();
Cancel();
RemoveFromScheduler();
if (iNativeFileDuplicate)
{
OSCL_DELETE(iNativeFileDuplicate);
}
for (uint32 i = 0; i < iDataBufferArray.size(); i++)
OSCL_DELETE(iDataBufferArray[i]);
iDataBufferArray.clear();
iSortedDataBufferArray.clear();
iLinkedDataBufferArray.clear();
OSCL_DELETE(iDataBuffer);
}
OsclAsyncFile::OsclAsyncFile(OsclNativeFile& aFile, int32 aCacheSize, PVLogger* aLogger)
: OsclActiveObject(OsclActiveObject::EPriorityHighest, "OsclAsyncFile"),
iNativeFile(aFile),
iNativeFileDuplicate(NULL),
iTotalCacheSize(aCacheSize),
iStartAsyncRead(false),
iReadPtrDummyLen(0),
iReadPtr(0, iReadPtrDummyLen, 0)
{
iLogger = aLogger;
iNumOfRun = 0;
iNumOfRunErr = 0;
iHasNativeAsyncRead = iNativeFile.HasAsyncRead();
// Init thread state tracking variable(s)
if (iHasNativeAsyncRead)
{
/* For native async read set the thread state to active
* since this is logically equivalent to the read thread
* already running for the non-native implementation. Some
* of the shared logic in this class uses this state variable
* so it needs to be set properly for the native async case also.
*/
iAsyncReadThreadState = EAsyncReadActive;
}
else
{
/* otherwise there is not native async read support so this
* class will need to launch a reader thread. Initialize the
* state to EAsyncReadNotActive to record the fact that the thread
* has not been launched yet.
*/
iAsyncReadThreadState = EAsyncReadNotActive;
}
iAsyncReadThreadExitFlag = false;
//this is the number of buffers we allocate internally for the linked buffer
//list. There is also one extra buffer of this size-- the 'iDataBuffer'.
iKCacheBufferCount = 4;
//'min bytes read ahead' is how much data we will try to keep available
//from the async reads. It must be <= iKCacheBufferCount * iTotalCacheSize;
iKMinBytesReadAhead = (3 * iTotalCacheSize);
OSCL_ASSERT(iKMinBytesReadAhead <= (int32)(iKCacheBufferCount*iTotalCacheSize));
//this is the size of each read request to the native file system.
//it must be <= the individual buffer size, so we use 8k as the default,
//but then limit it to the input buffer size.
iKAsyncReadBufferSize = 8 * 1024 ;
if ((uint32)iKAsyncReadBufferSize > iTotalCacheSize)
iKAsyncReadBufferSize = iTotalCacheSize;
}
void OsclAsyncFile::ConstructL()
{
//Create a duplicate file handle
//@todo: on symbian we need to make sure this is a duplicate of iNativeFile.
//on non-symbian it doesn't matter-- you can just create a new instance.
iNativeFileDuplicate = OSCL_NEW(OsclNativeFile, ());
#if(VERIFY_THIS)
iNativeFileVerify = OSCL_NEW(OsclNativeFile, ());
iVerifyCount = 0;
#endif
iFileSize = iNativeFile.Size();
iDataBufferArray.reserve(iKCacheBufferCount);
// Create data buffers
OsclAsyncFileBuffer* tmpBuffer;
for (int32 i = 0; i < iKCacheBufferCount; i++)
{
tmpBuffer = OsclAsyncFileBuffer::NewL(iTotalCacheSize, i);
iDataBufferArray.push_back(tmpBuffer);
}
// create a local instance of OsclAsyncFileBuffer
iDataBuffer = OsclAsyncFileBuffer::NewL(iTotalCacheSize, -1);
AddToScheduler();
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::ConstructL bufferSize %d numBuffers %d readSize %d readAhead %d"
, this, iTotalCacheSize, iKCacheBufferCount, iKAsyncReadBufferSize, iKMinBytesReadAhead));
}
int32 OsclAsyncFile::Open(const oscl_wchar *filename, uint32 mode
, const OsclNativeFileParams& params
, Oscl_FileServer& fileserv)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::Open mode %d ", this, mode));
//open the duplicate file handle
if (!iNativeFileDuplicate)
return -1;
int32 result = iNativeFileDuplicate->Open(filename, mode, params, fileserv);
//launch the thread
if (!iHasNativeAsyncRead)
{
LaunchAsyncReadThread();
}
iFilePosition = 0;
iSyncFilePosition = 0;
iAsyncFilePosition = 0;
iLastUserFileRead = 0;
//start the async read if file is opened and thread is running
if ((result == 0) && (iAsyncReadThreadState == EAsyncReadActive))
{
StartAsyncRead(true);
}
else
{
result = -1;
}
#if(VERIFY_THIS)
if (iNativeFileVerify->Open(filename, mode, params, fileserv) != result)
OSCL_ASSERT(0);
#endif
return result;
}
int32 OsclAsyncFile::Open(const char *filename, uint32 mode
, const OsclNativeFileParams& params
, Oscl_FileServer& fileserv)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::Open filename '%s' mode %d ", this, filename, mode));
//open the duplicate file handle
if (!iNativeFileDuplicate)
return -1;
int32 result = iNativeFileDuplicate->Open(filename, mode, params, fileserv);
//launch the thread
if (!iHasNativeAsyncRead)
{
LaunchAsyncReadThread();
}
iFilePosition = 0;
iSyncFilePosition = 0;
iAsyncFilePosition = 0;
iLastUserFileRead = 0;
//start the async read if file is opened and thread is running
if ((result == 0) && (iAsyncReadThreadState == EAsyncReadActive))
{
StartAsyncRead(true);
}
else
{
result = -1;
}
#if(VERIFY_THIS)
if (iNativeFileVerify->Open(filename, mode, params, fileserv) != result)
OSCL_ASSERT(0);
#endif
return result;
}
int32 OsclAsyncFile::Close()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::Close", this));
//stop the async read
StartAsyncRead(false);
//stop the thread
if (!iHasNativeAsyncRead)
{
StopAsyncReadThread();
}
//close the duplicate file handle
if (!iNativeFileDuplicate)
return -1;
int32 result = iNativeFileDuplicate->Close();
#if(VERIFY_THIS)
if (iNativeFileVerify->Close() != result)
OSCL_ASSERT(0);
#endif
return result;
}
void OsclAsyncFile::StartAsyncRead(bool aStartAsyncRead)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::StartAsyncRead %d", this, aStartAsyncRead));
iStartAsyncRead = aStartAsyncRead;
// Do the asynchronous read
}
uint32 OsclAsyncFile::Read(OsclAny* aBuffer, uint32 aDataSize, uint32 aNumElements)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::Read size %d numelements %d", this, aDataSize, aNumElements));
#if(VERIFY_THIS)
if (iVerifyCount == 1315)
fprintf(stderr, "");
//verify file pos before the read
if (iNativeFileVerify->Tell() != iFilePosition)
OSCL_ASSERT(0);
#endif
//Pull data from the internal buffers until we get the requested amount.
//note: there's no logic to prevent reading partial elements here.
uint32 bytesToRead = aDataSize * aNumElements;
uint32 bytesRead = 0;
uint8* databuf = NULL;
uint8* dest = (uint8*)aBuffer;
uint32 thisread;
uint32 nbytes;
while (bytesToRead > 0)
{
//note: the "doRead" routine only handles reads of <= iTotalCacheSize,
//so limit the read size here.
thisread = (bytesToRead > iTotalCacheSize) ? iTotalCacheSize : bytesToRead;
nbytes = doRead(databuf, aDataSize, thisread / aDataSize, iFilePosition);
if (nbytes)
{
oscl_memcpy(dest, databuf, nbytes);
dest += nbytes;
bytesRead += nbytes;
bytesToRead -= nbytes;
}
else
break;//no more data or an error occurred.
}
#if(VERIFY_THIS)
{
uint8* buf = (uint8*)OSCL_MALLOC(aDataSize * aNumElements);
int32 result2 = iNativeFileVerify->Read(buf, aDataSize, aNumElements);
if (result2 != bytesRead / aDataSize)
OSCL_ASSERT(0);
//verify file pos after the read
if (iNativeFileVerify->Tell() != iFilePosition)
OSCL_ASSERT(0);
//verify the data
if (oscl_memcmp(buf, aBuffer, bytesRead) != NULL)
OSCL_ASSERT(0);
OSCL_FREE(buf);
iVerifyCount++;
}
#endif
//return number of whole elements read.
return bytesRead / aDataSize;
}
//This routine locates the data in one of the internal buffers and returns a pointer.
//If data is not available, it will do a blocking read.
//The amount of data requested cannot exceed the internal buffer size.
//Note this returns bytes read, not number of elements read.
uint32 OsclAsyncFile::doRead(uint8 *& aBuffer1, uint32 aDataSize, uint32 aNumElements, int32 aOffset)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::doRead size %d numelements %d offset %d", this, aDataSize, aNumElements, aOffset));
//this routine only handles requests <= the individual buffer size-- it is
//the caller's responsibility to check.
OSCL_ASSERT(aNumElements*aDataSize <= iTotalCacheSize);
// Clear buffers in use
for (int32 i = 0; i < iKCacheBufferCount; i++)
{
iDataBufferArray[i]->CleanInUse();
}
// Check if we have data already in the buffer
OsclAsyncFileBuffer* dataBuffer = NULL;
int32 bufferFoundId;
uint32 bytesRead = 0;
// Check if we have data available
if (FindDataBuffer(dataBuffer, bufferFoundId, aOffset, aNumElements*aDataSize))
{
// Mark buffer in use
dataBuffer->SetInUse();
// update user pointer
OsclBuf* buff = dataBuffer->Buffer();
OsclPtr ptrRead = buff->Des();
aBuffer1 = const_cast<uint8*>(ptrRead.Ptr());
// offset pointer to correct location
aBuffer1 += (aOffset - dataBuffer->Offset());
bytesRead = dataBuffer->Length() - (aOffset - dataBuffer->Offset());
// Redo queue of linked buffers
ReOrderBuffersQueue(aOffset, bufferFoundId);
}
else
{
//Data is not available-- must do a synchronous read.
int32 ret = iNativeFileDuplicate->Seek(aOffset, Oscl_File::SEEKSET);
if (ret != 0)
{
// This is not good
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::doRead Seek Failed returned %d offset %d", this, ret, aOffset));
return 0;
}
// reset linked data bufferarray
iLinkedDataBufferArray.clear();
// Get buffer to read
OsclAsyncFileBuffer* dataBuffer;
bool bufferAvailable = GetNextDataBuffer(dataBuffer, aOffset);
if (!bufferAvailable)
{
// This case should never happen since iLinkedDataBufferArray was just closed
OSCL_ASSERT(0);
return 0;
}
dataBuffer->SetOffset(aOffset);
// Buffer is in use
dataBuffer->SetInUse();
OsclBuf* readBuffer = dataBuffer->Buffer();
OsclPtr ptrCurrentBuffer = readBuffer->Des();
ptrCurrentBuffer.SetLength(0);
// Do read. Might as well fill up the entire buffer while we're at it, so
// go ahead and read 'iTotalCacheSize'. The caller has already verified that
// the number of bytes requested in this read is <= 'iTotalCacheSize'.
bytesRead = iNativeFileDuplicate->Read(ptrCurrentBuffer.Ptr(), 1, iTotalCacheSize);
ptrCurrentBuffer.SetLength(bytesRead);
// update data
dataBuffer->UpdateData();
// return value
aBuffer1 = (uint8*)ptrCurrentBuffer.Ptr();
iSyncFilePosition = aOffset + bytesRead;
iLinkedDataBufferArray.push_back(dataBuffer);
}
// set the returned byte count to the actual bytes available from this call
// or the amount requested, whichever is less.
if (bytesRead > aDataSize*aNumElements)
bytesRead = aDataSize * aNumElements;
// Update virtual file pointer position
iFilePosition = aOffset + bytesRead;
// This is the position from where we read
iLastUserFileRead = iFilePosition;
// Continue reading
if (iStartAsyncRead)
{
UpdateReading();
}
return bytesRead;
}
bool OsclAsyncFile::FindDataBuffer(OsclAsyncFileBuffer*& aDataBuffer, int32& aBufferId, int32 aOffset, int32 aSize)
{
// Look for the requested value on the queue
OsclAsyncFileBuffer* tmpDataBuffer;
int32 i;
bool found = false;
for (i = 0; i < iKCacheBufferCount; i++)
{
tmpDataBuffer = iDataBufferArray[i];
if (tmpDataBuffer->HasThisOffset(aOffset))
{
// we want a linked buffer
if (IsLinkedDataBuffer(tmpDataBuffer))
{
found = true;
break;
}
else
{
// check if it can be linked
if (CanBeLinked(tmpDataBuffer))
{
found = true;
break;
}
}
}
}
if (!found)
return false;
aDataBuffer = tmpDataBuffer;
// return id of the buffer (needed because down below we might return a different buffer than tmpDataBuffer)
// but we still need the id
aBufferId = tmpDataBuffer->Id();
// Check if we have enough data to return in a single buffer
int32 maxOffset = tmpDataBuffer->Offset() + tmpDataBuffer->Length();
// the easy case
if (aOffset + aSize <= maxOffset)
return true;
// Last option, check if we can concatenate required data from two buffers
int32 availableData = maxOffset - aOffset;
// check again in the buffers for the remaining part of the data
OsclAsyncFileBuffer* tmpDataBuffer2 = iDataBufferArray[0]; //Init pointer to clean-up compiler warning=MG
for (i = 0; i < iKCacheBufferCount; i++)
{
tmpDataBuffer2 = iDataBufferArray[i];
if (tmpDataBuffer2->HasThisOffset(maxOffset))
{
break;
}
}
// extra data not found
if (i == iKCacheBufferCount)
return false;
// make sure data is valid
if (tmpDataBuffer2->Offset() != maxOffset)
return false;
// Set the buffer
aDataBuffer = iDataBuffer;
// length to copy from second buffer
int32 lengthToCopy;
if (tmpDataBuffer2->Length() + availableData >= aSize)
lengthToCopy = aSize - availableData;
else
return false; //shouldn't happen
//note: could copy directly into target buffer here instead of into iDataBuffer,
//but would take some code re-structuring.
// extra data found, proceed to concatenate with memcpy
OsclBuf* buffer = iDataBuffer->Buffer();
OsclPtr ptrDes = buffer->Des();
ptrDes.Zero();
OsclPtrC tmpDataPtr = tmpDataBuffer->Buffer()->DesC();
OsclPtrC temp_tmpDataPtr = tmpDataPtr.Right(availableData);
tmpDataPtr.Set(&temp_tmpDataPtr);
ptrDes.Append(tmpDataPtr);
// find out the length to copy
OsclBuf* buffer2 = tmpDataBuffer2->Buffer();
OsclPtrC ptrDes2 = buffer2->DesC();
OsclPtrC temp_ptrDes2 = ptrDes2.Left(lengthToCopy);
ptrDes2.Set(&temp_ptrDes2);
ptrDes.Append(ptrDes2);
iDataBuffer->UpdateData();
iDataBuffer->SetOffset(aOffset);
return true;
}
void OsclAsyncFile::UpdateReading()
{
// Wait for Run() to process data
if (IsBusy())
return;
int32 bytesReadAhead = BytesReadAhead(iFilePosition);
// don't need to read more
if (bytesReadAhead >= iKMinBytesReadAhead)
{
return;
}
int32 posToReadFrom = iFilePosition + bytesReadAhead;
// check for eof. We stop reading
if (posToReadFrom == iFileSize)
{
return;
}
// Start next read
StartNextRead(posToReadFrom);
}
int32 OsclAsyncFile::BytesReadAhead(int32 aOffset)
{
OSCL_UNUSED_ARG(aOffset);
// Get the maximum offset of the last element in the linked buffer array
int32 index = iLinkedDataBufferArray.size() - 1;
if (index == -1)
{
return 0;
}
OsclAsyncFileBuffer* tmpDataBuffer = iLinkedDataBufferArray[index];
int32 maxOffset = tmpDataBuffer->Offset() + tmpDataBuffer->Length();
int32 bytesReadAhead = maxOffset - iLastUserFileRead;
return bytesReadAhead;
}
int32 OsclAsyncFile::SortDataBuffers()
{
Oscl_Vector<OsclAsyncFileBuffer*, OsclMemAllocator> tmpArray;
// Append valid and not in use elements only
OsclAsyncFileBuffer* tmpDataBuffer;
uint32 i;
for (i = 0; i < (uint32)iKCacheBufferCount; i++)
{
tmpDataBuffer = iDataBufferArray[i];
if (tmpDataBuffer->IsValid())
{
tmpArray.push_back(tmpDataBuffer);
}
}
iSortedDataBufferArray.clear();
// sort them out
for (i = 0; i < tmpArray.size(); i++)
{
if (i == 0)
iSortedDataBufferArray.push_back(tmpArray[i]);
else
{
OsclAsyncFileBuffer* tmpDataBuffer = tmpArray[i];
bool insertedData = false;
// check where to insert new element
for (uint32 j = 0; j < iSortedDataBufferArray.size(); j++)
{
OsclAsyncFileBuffer* tmpDataBuffer2 = iSortedDataBufferArray[j];
if (tmpDataBuffer->Offset() < tmpDataBuffer2->Offset())
{
// Insert element
//iSortedDataBufferArray.Insert(tmpDataBuffer, j);
{
//note: there's no insert in oscl vector-- push to end then
//bubble down to desired spot.
iSortedDataBufferArray.push_back(tmpDataBuffer);
for (uint32 k = (uint32)iSortedDataBufferArray.size() - 1;k > j;k--)
{
OsclAsyncFileBuffer* temp = iSortedDataBufferArray[k-1];
iSortedDataBufferArray[k-1] = iSortedDataBufferArray[k];
iSortedDataBufferArray[k] = temp;
}
}
insertedData = true;
break;
}
}
if (!insertedData)
{
iSortedDataBufferArray.push_back(tmpDataBuffer);
}
}
}
tmpArray.clear();
return 0;
}
bool OsclAsyncFile::GetNextDataBuffer(OsclAsyncFileBuffer*& aDataBuffer, int32 aFilePointerToReadFrom)
{
uint32 i;
OSCL_UNUSED_ARG(aFilePointerToReadFrom);
// Loop through the buffer list and find out the first buffer available for use
OsclAsyncFileBuffer* tmpDataBuffer;
for (i = 0; i < iDataBufferArray.size(); i++)
{
tmpDataBuffer = iDataBufferArray[i];
if (!tmpDataBuffer->IsValid())
{
aDataBuffer = tmpDataBuffer;
return true;
}
}
// if all buffers are valid, return the next one with oldest data
uint32 smallerOffset = 0;
OsclAsyncFileBuffer* dataBufferToUse = NULL;
OsclAsyncFileBuffer* lastOptionBufferToUse = NULL;
for (i = 0; i < iDataBufferArray.size(); i++)
{
tmpDataBuffer = iDataBufferArray[i];
// Only return valid buffers
if (tmpDataBuffer->IsInUse() || IsLinkedDataBuffer(tmpDataBuffer) || (tmpDataBuffer == iDataBufferInUse))
continue;
// We check if the buffer can be linked
// if so, we rather take another one that is not of any use.
if (CanBeLinked(tmpDataBuffer))
{
// we remember it just in case there's no other buffer to use
lastOptionBufferToUse = dataBufferToUse;
continue;
}
if (dataBufferToUse == NULL)
{
smallerOffset = tmpDataBuffer->Offset();
dataBufferToUse = tmpDataBuffer;
}
else
{
if ((uint32)(tmpDataBuffer->Offset()) < smallerOffset)
{
smallerOffset = tmpDataBuffer->Offset();
dataBufferToUse = tmpDataBuffer;
}
}
}
// check what we have
if (dataBufferToUse == NULL)
dataBufferToUse = lastOptionBufferToUse;
// If we didn't find any buffer, return false
if (dataBufferToUse == NULL)
return false;
// Initialize buffer to use
aDataBuffer = dataBufferToUse;
return true;
}
void OsclAsyncFile::ReOrderBuffersQueue(int32 aOffset, int32 aFirstBufferId)
{
OSCL_UNUSED_ARG(aOffset);
// reset array
iLinkedDataBufferArray.clear();
// Sort data buffers first
SortDataBuffers();
// aFirstBufferId points to the index of the buffer in iDataBufferArray
OsclAsyncFileBuffer* tmpDataBuffer = iDataBufferArray[aFirstBufferId];
// Add the head element
iLinkedDataBufferArray.push_back(tmpDataBuffer);
// Look for the linked elements
int32 offset = tmpDataBuffer->Offset() + tmpDataBuffer->Length();
for (uint32 i = 0; i < iSortedDataBufferArray.size(); i++)
{
tmpDataBuffer = iSortedDataBufferArray[i];
if (offset == tmpDataBuffer->Offset())
{
offset = tmpDataBuffer->Offset() + tmpDataBuffer->Length();
iLinkedDataBufferArray.push_back(tmpDataBuffer);
}
}
}
bool OsclAsyncFile::IsLinkedDataBuffer(OsclAsyncFileBuffer* aDataBuffer)
{
for (uint32 i = 0; i < iLinkedDataBufferArray.size(); i++)
{
OsclAsyncFileBuffer* tmpDataBuffer = iLinkedDataBufferArray[i];
if (tmpDataBuffer == aDataBuffer)
{
return true;
}
}
return false;
}
bool OsclAsyncFile::CanBeLinked(OsclAsyncFileBuffer* aDataBuffer)
{
if (iLinkedDataBufferArray.size() == 0)
{
return false;
}
if (aDataBuffer->Offset() + aDataBuffer->Length() == iLinkedDataBufferArray[0]->Offset())
{
return true;
}
return false;
}
//The AO is invoked when the asynchronous Read request is complete.
void OsclAsyncFile::Run()
{
iNumOfRun++;
if (iStatus != OSCL_REQUEST_ERR_NONE)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::Run Error!!!", this));
iNumOfRunErr++;
return;
}
if (iAsyncReadThreadState == EAsyncReadNotActive)
{
//This is a safeguard against the situation when the thread is already stopped,
//but the last completed request triggers the Run() execution
return;
}
//update the iReadPtr descriptor length with the result of the async read.
//Note all reads are of size 1, so we can use "num elements" interchangeably with
//bytes here.
if (iHasNativeAsyncRead)
iReadPtr.SetLength(iNativeFile.GetReadAsyncNumElements());
else
iReadPtr.SetLength(iAsyncReadNumBytes);
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::Run numBytes %d ", this, iReadPtr.Length()));
// First of all, update the internal file pointer
// and the current buffer
OsclBuf* readBuffer = iDataBufferInUse->Buffer();
OsclPtr ptrCurrentBuffer = readBuffer->Des();
int32 bytesRead = iReadPtr.Length();
ptrCurrentBuffer.SetLength(ptrCurrentBuffer.Length() + bytesRead);
iAsyncFilePosition += bytesRead;
iDataBufferInUse->UpdateData();
// check how many bytes ahead of the user file position we have
// bool seekDone = false;
int32 bytesReadAhead = BytesReadAhead(iLastUserFileRead);
// next position to read from
int32 posToReadFrom = iLastUserFileRead + bytesReadAhead;
// check for eof. We stop reading
if (posToReadFrom == iFileSize)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::Run Reached end of file ", this));
return;
}
// don't need to read more for now
if (bytesReadAhead >= iKMinBytesReadAhead)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::Run Reached the read-ahead count", this));
return;
}
// Start next read
StartNextRead(posToReadFrom);
}
void OsclAsyncFile::DoCancel()
{
if (!iHasNativeAsyncRead)
{
if (iAsyncReadThreadState == EAsyncReadNotActive)
{
//in case thread exited with some request active, this will
//complete it
OsclActiveObject::DoCancel();
}
else
{
//in this case, thread is active. since there's no way to
//interrupt the thread's blocking read call, just do nothing
//here, then scheduler will wait on request completion.
}
}
}
void OsclAsyncFile::StartNextRead(int32 aPosToReadFrom)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::StartNextRead pos %d ", this, aPosToReadFrom));
// Determine from which file pointer we're going to read
if (aPosToReadFrom == iAsyncFilePosition)
{
// just continue reading
OsclBuf* readBuffer = iDataBufferInUse->Buffer();
OsclPtr ptrCurrentBuffer = readBuffer->Des();
bool validBuffer = true;
// Extra validation. This should never be false, but let's check it
if (iDataBufferInUse->Offset() + iDataBufferInUse->Length() != aPosToReadFrom)
{
validBuffer = false;
}
// Check if we need a new buffer
if (validBuffer && (iTotalCacheSize - ptrCurrentBuffer.Length() < (uint32)iKAsyncReadBufferSize))
{
// This is the case where we could get NULL if
// we're trying to read ahead more data than what the
// buffers can hold. We just add some protection in case
// someone makes a wrong configuration
bool availableBuffer = GetNextDataBuffer(iDataBufferInUse, aPosToReadFrom);
if (!availableBuffer)
{
return;
}
// we link the new buffer as soon as we get it
iLinkedDataBufferArray.push_back(iDataBufferInUse);
iDataBufferInUse->SetOffset(aPosToReadFrom);
OsclBuf* newReadBuffer = iDataBufferInUse->Buffer();
OsclPtr ptrNewReadBuffer = newReadBuffer->Des();
ptrNewReadBuffer.SetLength(0);
iDataBufferInUse->UpdateData();
iReadPtr.Set((uint8*)ptrNewReadBuffer.Ptr(), 0, iKAsyncReadBufferSize);
}
else
{
iReadPtr.Set((uint8*)(ptrCurrentBuffer.Ptr() + ptrCurrentBuffer.Length()), 0, iKAsyncReadBufferSize);
}
}
else
if (aPosToReadFrom == iSyncFilePosition)
{
// update the async file pointer to the desired location
int32 result = iNativeFile.Seek(aPosToReadFrom, Oscl_File::SEEKSET);
if (result != 0)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::StartNextRead Seek failed, return %d, offset %d ", this, result, aPosToReadFrom));
return;
}
// we need to swap the file pointers
int32 tmpPosition = iSyncFilePosition;
iSyncFilePosition = iAsyncFilePosition;
iAsyncFilePosition = tmpPosition;
// We need a new buffer
bool availableBuffer = GetNextDataBuffer(iDataBufferInUse, aPosToReadFrom);
if (!availableBuffer)
{
// This is an error condition. There should always be a buffer available
OSCL_ASSERT(0);
return;
}
// we link the new buffer as soon as we get it
iLinkedDataBufferArray.push_back(iDataBufferInUse);
iDataBufferInUse->SetOffset(aPosToReadFrom);
// Initialize the read pointer (iReadPtr)
OsclBuf* readBuffer = iDataBufferInUse->Buffer();
OsclPtr ptrCurrentBuffer = readBuffer->Des();
// reset length
ptrCurrentBuffer.SetLength(0);
iDataBufferInUse->UpdateData();
iReadPtr.Set((uint8*)ptrCurrentBuffer.Ptr(), 0, iKAsyncReadBufferSize);
}
else
{
// This case should never happen.. check it out
OSCL_ASSERT(0);
return;
}
// Issue the asynchronous read request.
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::StartNextRead Issuing async read, size %d ", this, iKAsyncReadBufferSize));
if (iHasNativeAsyncRead)
{
//Activate the AO that will handle read completion.
PendForExec();
//Start the native async read operation
int32 result = iNativeFile.ReadAsync(iReadPtr.Ptr(), 1, iKAsyncReadBufferSize, StatusRef());
//if it failed to start, then cancel the request with an error.
if (result != 0)
PendComplete(OSCL_REQUEST_ERR_GENERAL);
}
else
{
//Start the non-native async read operation.
StartNonNativeAsyncRead();
//the AO will run when read is complete.
}
}
int32 OsclAsyncFile::Seek(int32 offset, Oscl_File::seek_type origin)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::Seek offset %d origin %d", this, offset, origin));
if (origin == Oscl_File::SEEKCUR)
{
iFilePosition += offset;
}
else
if (origin == Oscl_File::SEEKSET)
{
iFilePosition = offset;
}
else
if (origin == Oscl_File::SEEKEND)
{
iFilePosition = iFileSize + offset;
}
//some sanity checks.
OSCL_ASSERT(iFilePosition >= 0);
OSCL_ASSERT(iFilePosition <= iFileSize);
#if(VERIFY_THIS)
if (iNativeFileVerify->Seek(offset, origin) != 0)
OSCL_ASSERT(0);
//also verify current position
if (iNativeFileVerify->Tell() != iFilePosition)
OSCL_ASSERT(0);
#endif
return 0;
}
int32 OsclAsyncFile::Tell()
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::Tell pos %d ", this, iFilePosition));
#if(VERIFY_THIS)
if (iNativeFileVerify->Tell() != iFilePosition)
OSCL_ASSERT(0);
#endif
return iFilePosition;
}
int32 OsclAsyncFile::EndOfFile()
{
int32 result = (iFilePosition >= iFileSize) ? 1 : 0;
#if(VERIFY_THIS)
if (iNativeFileVerify->EndOfFile() != result)
OSCL_ASSERT(0);
#endif
return result;
}
int32 OsclAsyncFile::Size()
{
#if(VERIFY_THIS)
if (iNativeFileVerify->Size() != iFileSize)
OSCL_ASSERT(0);
#endif
return iFileSize;
}
// ================== Non-Native async read implementation ========================== //
void OsclAsyncFile::LaunchAsyncReadThread()
{
if (iAsyncReadThreadState != EAsyncReadActive)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::LaunchAsyncReadThread starting thread ", this));
iAsyncReadSem.Create(0);
iAsyncReadExitSem.Create(0);
OsclThread thread;
OsclProcStatus::eOsclProcError status = thread.Create(iAsyncReadThreadFunc, 4096, (TOsclThreadFuncArg)this);
if (status == OsclProcStatus::SUCCESS_ERROR)
{
//Thread was successfuly started, update thread's state
iAsyncReadThreadState = EAsyncReadActive;
}
}
}
void OsclAsyncFile::StopAsyncReadThread()
{
if (iAsyncReadThreadState == EAsyncReadActive)
{
PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_DEBUG,
(0, "OsclAsyncFile(0x%x)::StopAsyncReadThread stopping thread ", this));
//signal the thread to exit & wake it up.
iAsyncReadThreadExitFlag = true;
iAsyncReadSem.Signal();
//wait on thread to exit so we can reset the sems safely
iAsyncReadExitSem.Wait();
//Thread is terminated, update thread's state
iAsyncReadThreadState = EAsyncReadNotActive;
iAsyncReadSem.Close();
iAsyncReadExitSem.Close();
}
}
void OsclAsyncFile::StartNonNativeAsyncRead()
{
//note: we assume the read requests are nicely serialized here,
//so there's no handling for overlapping requests.
//Activate the AO that will wait on read completion.
PendForExec();
//wake up the thread
iAsyncReadSem.Signal();
}
void OsclAsyncFile::InThread()
{
while (iAsyncReadThreadExitFlag != true)
{
//wait for the signal
iAsyncReadSem.Wait();
//see if it's a close request
if (iAsyncReadThreadExitFlag == true)
{
break;
}
//issue the read.
iAsyncReadNumBytes = iNativeFile.Read(iReadPtr.Ptr(), 1, iKAsyncReadBufferSize);
//complete the request.
if (IsAdded() && iStatus == OSCL_REQUEST_PENDING)
PendComplete(OSCL_REQUEST_ERR_NONE);
}
// reset the thread exit flag
iAsyncReadThreadExitFlag = false;
//signal that thread is exiting.
iAsyncReadExitSem.Signal();
}
//static thread routine.
TOsclThreadFuncRet OSCL_THREAD_DECL OsclAsyncFile::iAsyncReadThreadFunc(TOsclThreadFuncArg aArg)
{
OsclAsyncFile* This = (OsclAsyncFile*)aArg;
This->InThread();
return 0;
}