/* ******************************************************************************** * * (c) Copyright 2010-2013, Allwinner Microelectronic Co., Ltd. * All Rights Reserved * * File : FsCache.c * Version: V1.0 * By : Eric_wang * Date : 2013-8-4 * Description: use noLock machanism for fread and fwrite, to speed up as quickly as possible. So fwrite (nCacheSize-1) at most. ******************************************************************************** */ //#define LOG_NDEBUG 0 #define LOG_TAG "FsCache" #include #include #include #include #include #include #include //#include #define WRITE_BLOCK_SIZE (128*1024) //unit:byte. typedef struct tag_FsCacheThreadContext { // FILE *mpFile; // int mFd; struct cdx_stream_info *mpStream; char *mpCache; size_t mCacheSize; pthread_t mThreadId; volatile int mThreadExitFlag; volatile char *mWritePtr; //fwrite modify it, thread read it. volatile char *mReadPtr; //fwrite read it, thread modify it. cdx_sem_t mWriteStartSem; //fwrite notify thread to start to write data to fs. int mFlushFlag; //fwrite modify it to 1, thread read it and clear it to 0. pthread_mutex_t mFlushLock; //work with mFlushFlag cdx_sem_t mFlushDoneSem; //work with mFlushFlag, thread notify to fwrite flush done! cdx_sem_t mWriteDoneSem; unsigned int mVideoCodecId; }FsCacheThreadContext; static ssize_t FsCacheThreadWrite(FsWriter *thiz, const char *buf, size_t size) { FsCacheThreadContext *pCtx = (FsCacheThreadContext*)thiz->mPriv; char *pRdPtr; char *pWtPtr; size_t nFreeSize; size_t nFreeSizeSection1; size_t nFreeSizeSection2; if(0 == size) { return 0; } while(1) { pRdPtr = (char*)pCtx->mReadPtr; pWtPtr = (char*)pCtx->mWritePtr; if(pWtPtr >= pRdPtr) { nFreeSizeSection1 = pRdPtr - pCtx->mpCache; nFreeSizeSection2 = pCtx->mpCache+pCtx->mCacheSize-pWtPtr; if(nFreeSizeSection1>0) { nFreeSizeSection1-=1; } else if(nFreeSizeSection2>0) { nFreeSizeSection2-=1; } else { aloge("fatal error! at lease left one byte! check code!"); } nFreeSize = nFreeSizeSection1+nFreeSizeSection2; if(nFreeSize>=size) { if(nFreeSizeSection2 >= size) { memcpy(pWtPtr, buf, size); pWtPtr+=size; if(pWtPtr == pCtx->mpCache+pCtx->mCacheSize) { pWtPtr = pCtx->mpCache; } else if(pWtPtr > pCtx->mpCache+pCtx->mCacheSize) { aloge("fatal error! check code!"); } } else { memcpy(pWtPtr, buf, nFreeSizeSection2); memcpy(pCtx->mpCache, buf+nFreeSizeSection2, size-nFreeSizeSection2); pWtPtr = pCtx->mpCache+size-nFreeSizeSection2; } pCtx->mWritePtr = pWtPtr; break; } else { int64_t tm1, tm2; alogd("codecID[%d], nFreeSize[%d]mVideoCodecId, nFreeSize, size); tm1 = CDX_GetSysTimeUsMonotonic(); cdx_sem_up_unique(&pCtx->mWriteStartSem); cdx_sem_wait(&pCtx->mWriteDoneSem); tm2 = CDX_GetSysTimeUsMonotonic(); alogd("codecID[%d], nFreeSize[%d]mVideoCodecId, nFreeSize, size, (tm2-tm1)/1000); } } else //pWtPtr < pRdPtr { nFreeSize = pRdPtr-pWtPtr - 1; if(nFreeSize>=size) { memcpy(pWtPtr, buf, size); pWtPtr+=size; pCtx->mWritePtr = pWtPtr; break; } else { int64_t tm1, tm2; alogd("codecID[%d], nFreeSize[%d]mVideoCodecId, nFreeSize, size); tm1 = CDX_GetSysTimeUsMonotonic(); cdx_sem_up_unique(&pCtx->mWriteStartSem); cdx_sem_wait(&pCtx->mWriteDoneSem); tm2 = CDX_GetSysTimeUsMonotonic(); alogd("codecID[%d], nFreeSize[%d]mVideoCodecId, nFreeSize, size, (tm2-tm1)/1000); } } } if(pCtx->mCacheSize - (nFreeSize+1-size) >= WRITE_BLOCK_SIZE) { cdx_sem_up_unique(&pCtx->mWriteStartSem); // if(0 == cdx_sem_get_val(&pCtx->mWriteStartSem)) // { // cdx_sem_up(&pCtx->mWriteStartSem); // } } return size; } static int FsCacheThreadSeek(FsWriter *thiz, int64_t nOffset, int fromWhere) { FsCacheThreadContext *pCtx = (FsCacheThreadContext*)thiz->mPriv; int ret; thiz->fsFlush(thiz); ret = pCtx->mpStream->seek(pCtx->mpStream, nOffset, fromWhere); return ret; } static int64_t FsCacheThreadTell(FsWriter *thiz) { FsCacheThreadContext *pCtx = (FsCacheThreadContext*)thiz->mPriv; int64_t nFilePos; thiz->fsFlush(thiz); nFilePos = pCtx->mpStream->tell(pCtx->mpStream); return nFilePos; } static int FsCacheThreadTruncate(FsWriter *thiz, int64_t nLength) { int ret; FsCacheThreadContext *pCtx = (FsCacheThreadContext*)thiz->mPriv; thiz->fsFlush(thiz); ret = pCtx->mpStream->truncate(pCtx->mpStream, nLength); return ret; } static int FsCacheThreadFlush(FsWriter *thiz) { FsCacheThreadContext *pCtx = (FsCacheThreadContext*)thiz->mPriv; char *pRdPtr = (char*)pCtx->mReadPtr; char *pWtPtr = (char*)pCtx->mWritePtr; if(pRdPtr == pWtPtr) { return 0; } cdx_mutex_lock(&pCtx->mFlushLock); pCtx->mFlushFlag = 1; cdx_sem_up_unique(&pCtx->mWriteStartSem); cdx_mutex_unlock(&pCtx->mFlushLock); cdx_sem_down(&pCtx->mFlushDoneSem); pRdPtr = (char*)pCtx->mReadPtr; pWtPtr = (char*)pCtx->mWritePtr; if(pRdPtr != pWtPtr) { aloge("fatal error! Flush fail[%p][%p]?", pRdPtr, pWtPtr); return -1; } alogd("mpCache=%p, mReadPtr=%p, mWritePtr=%p, mCacheSize=%d", pCtx->mpCache, pCtx->mReadPtr, pCtx->mWritePtr, pCtx->mCacheSize); return 0; } static void* FsCacheWriteThread(void* pThreadData) { //int ret = 0; FsCacheThreadContext *pCtx = (FsCacheThreadContext *)pThreadData; char *pRdPtr; char *pWtPtr; size_t nValidSize; size_t nValidSizeSection1; size_t nValidSizeSection2; size_t nWriteBlockNum; alogv("FsCacheWriteThread[%d] started", pCtx->mThreadId); while (1) { cdx_sem_down(&pCtx->mWriteStartSem); //(1) detect basic info before start to fwrite(). pRdPtr = (char*)pCtx->mReadPtr; pWtPtr = (char*)pCtx->mWritePtr; if(pWtPtr >= pRdPtr) { nValidSize = pWtPtr - pRdPtr; } else //pWtPtr < pRdPtr { nValidSizeSection1 = pWtPtr - pCtx->mpCache; nValidSizeSection2 = pCtx->mpCache + pCtx->mCacheSize-pRdPtr; nValidSize = nValidSizeSection1 + nValidSizeSection2; } nWriteBlockNum = nValidSize/WRITE_BLOCK_SIZE; if(nWriteBlockNum>0) { if(nWriteBlockNum>1) { alogv("nValidSize[%d]kB, nWriteBlockNum[%d], maybe write slow?", nValidSize/1024, nWriteBlockNum); } } else { alogv("nValidSize[%d]kB is not enough!", nValidSize/1024); } //(2) start to fwrite() while(1) { pRdPtr = (char*)pCtx->mReadPtr; pWtPtr = (char*)pCtx->mWritePtr; if(pWtPtr >= pRdPtr) { nValidSize = pWtPtr - pRdPtr; nWriteBlockNum = nValidSize/WRITE_BLOCK_SIZE; if(nWriteBlockNum>0) { //nWriteBlockNum = 1; fileWriter(pCtx->mpStream, pRdPtr, WRITE_BLOCK_SIZE*nWriteBlockNum); pRdPtr += WRITE_BLOCK_SIZE*nWriteBlockNum; pCtx->mReadPtr = pRdPtr; cdx_sem_signal(&pCtx->mWriteDoneSem); } else { break; } } else //pWtPtr < pRdPtr { nValidSizeSection1 = pWtPtr - pCtx->mpCache; nValidSizeSection2 = pCtx->mpCache + pCtx->mCacheSize-pRdPtr; nValidSize = nValidSizeSection1 + nValidSizeSection2; nWriteBlockNum = nValidSizeSection2/WRITE_BLOCK_SIZE; if(nWriteBlockNum>0) { //nWriteBlockNum = 1; fileWriter(pCtx->mpStream, pRdPtr, WRITE_BLOCK_SIZE*nWriteBlockNum); pRdPtr += WRITE_BLOCK_SIZE*nWriteBlockNum; if(pRdPtr == pCtx->mpCache + pCtx->mCacheSize) { pRdPtr = pCtx->mpCache; } else if(pRdPtr > pCtx->mpCache + pCtx->mCacheSize) { aloge("fatal error! cache overflow![%p][%p][%d]", pRdPtr, pCtx->mpCache, pCtx->mCacheSize); } pCtx->mReadPtr = pRdPtr; } else { aloge("fatal error! Cache status has something wrong, need check[%p][%p][%p][%d][%d]", pRdPtr, pWtPtr, pCtx->mpCache, pCtx->mCacheSize, nValidSizeSection2); fileWriter(pCtx->mpStream, pRdPtr, nValidSizeSection2); pRdPtr = pCtx->mpCache; pCtx->mReadPtr = pRdPtr; } cdx_sem_signal(&pCtx->mWriteDoneSem); } } //(3) process mFlushFlag. cdx_mutex_lock(&pCtx->mFlushLock); if(pCtx->mFlushFlag) { //flush data to fs pRdPtr = (char*)pCtx->mReadPtr; pWtPtr = (char*)pCtx->mWritePtr; if(pWtPtr >= pRdPtr) { nValidSize = pWtPtr - pRdPtr; if(nValidSize>0) { fileWriter(pCtx->mpStream, pRdPtr, nValidSize); pRdPtr += nValidSize; pCtx->mReadPtr = pRdPtr; cdx_sem_signal(&pCtx->mWriteDoneSem); } } else //pWtPtr < pRdPtr { nValidSizeSection1 = pWtPtr - pCtx->mpCache; nValidSizeSection2 = pCtx->mpCache + pCtx->mCacheSize-pRdPtr; nValidSize = nValidSizeSection1 + nValidSizeSection2; if(nValidSizeSection2 > 0) { fileWriter(pCtx->mpStream, pRdPtr, nValidSizeSection2); pRdPtr = pCtx->mpCache; pCtx->mReadPtr = pRdPtr; cdx_sem_signal(&pCtx->mWriteDoneSem); } else { aloge("fatal error! nValidSizeSection2==0!"); } if(nValidSizeSection1 > 0) { fileWriter(pCtx->mpStream, pRdPtr, nValidSizeSection1); pRdPtr += nValidSizeSection1; pCtx->mReadPtr = pRdPtr; cdx_sem_signal(&pCtx->mWriteDoneSem); } } if(pCtx->mReadPtr == pCtx->mWritePtr) { pCtx->mReadPtr = pCtx->mWritePtr = pCtx->mpCache; } else { aloge("fatal error! rdPtr[%p]!=wtPtr[%p]!", pCtx->mReadPtr, pCtx->mWritePtr); } cdx_sem_up(&pCtx->mFlushDoneSem); pCtx->mFlushFlag = 0; } cdx_mutex_unlock(&pCtx->mFlushLock); //(4) process mThreadExitFlag, same as flush. if(pCtx->mThreadExitFlag) { //flush data to fs pRdPtr = (char*)pCtx->mReadPtr; pWtPtr = (char*)pCtx->mWritePtr; if(pWtPtr >= pRdPtr) { nValidSize = pWtPtr - pRdPtr; if(nValidSize>0) { fileWriter(pCtx->mpStream, pRdPtr, nValidSize); pRdPtr += nValidSize; pCtx->mReadPtr = pRdPtr; cdx_sem_signal(&pCtx->mWriteDoneSem); } } else //pWtPtr < pRdPtr { nValidSizeSection1 = pWtPtr - pCtx->mpCache; nValidSizeSection2 = pCtx->mpCache + pCtx->mCacheSize-pRdPtr; nValidSize = nValidSizeSection1 + nValidSizeSection2; if(nValidSizeSection2 > 0) { fileWriter(pCtx->mpStream, pRdPtr, nValidSizeSection2); pRdPtr = pCtx->mpCache; pCtx->mReadPtr = pRdPtr; cdx_sem_signal(&pCtx->mWriteDoneSem); } else { aloge("fatal error! nValidSizeSection2==0!"); } if(nValidSizeSection1 > 0) { fileWriter(pCtx->mpStream, pRdPtr, nValidSizeSection1); pRdPtr += nValidSizeSection1; pCtx->mReadPtr = pRdPtr; cdx_sem_signal(&pCtx->mWriteDoneSem); } } goto EXIT; } //cdx_sem_signal(&pCtx->mWriteDoneSem); //fflush(pCtx->mpFile); } EXIT: alogv("FsCacheWriteThread[%d] will quit now.", pCtx->mThreadId); return NULL; } FsWriter *initFsCacheThreadContext(struct cdx_stream_info *pStream, char *pCache, int nCacheSize, unsigned int vCodec) { int err; FsWriter *pFsWriter = (FsWriter*)malloc(sizeof(FsWriter)); if (NULL == pFsWriter) { aloge("Failed to alloc FsWriter(%s)", strerror(errno)); return NULL; } FsCacheThreadContext *pContext = (FsCacheThreadContext*)malloc(sizeof(FsCacheThreadContext)); if (NULL == pContext) { aloge("Failed to alloc FsCacheThreadContext(%s)", strerror(errno)); goto ERROR0; } pContext->mpStream = pStream; pContext->mCacheSize = nCacheSize; pContext->mpCache = pCache; if(NULL == pContext->mpCache) { alogw("fatal error! malloc [%d]kByte fail.", nCacheSize/1024); goto ERROR1; } pContext->mThreadExitFlag = 0; pContext->mWritePtr = pContext->mReadPtr = pContext->mpCache; pContext->mFlushFlag = 0; pContext->mVideoCodecId = vCodec; err = pthread_mutex_init(&pContext->mFlushLock, NULL); if(err) { aloge("err[%d]", err); goto ERROR2; } err = cdx_sem_init(&pContext->mWriteStartSem,0); if(err) { aloge("err[%d]", err); goto ERROR3; } err = cdx_sem_init(&pContext->mFlushDoneSem,0); if(err) { aloge("err[%d]", err); goto ERROR4; } err = cdx_sem_init(&pContext->mWriteDoneSem, 0); if(err) { aloge("err[%d]", err); goto ERROR5; } // Create writer thread err = pthread_create(&pContext->mThreadId, NULL, FsCacheWriteThread, pContext); if (err || !pContext->mThreadId) { aloge("FsCacheThread create writer thread err"); goto ERROR6; } pFsWriter->fsWrite = FsCacheThreadWrite; pFsWriter->fsSeek = FsCacheThreadSeek; pFsWriter->fsTell = FsCacheThreadTell; pFsWriter->fsTruncate = FsCacheThreadTruncate; pFsWriter->fsFlush = FsCacheThreadFlush; pFsWriter->mMode = FSWRITEMODE_CACHETHREAD; pFsWriter->mPriv = (void*)pContext; return pFsWriter; ERROR6: cdx_sem_deinit(&pContext->mWriteDoneSem); ERROR5: cdx_sem_deinit(&pContext->mFlushDoneSem); ERROR4: cdx_sem_deinit(&pContext->mWriteStartSem); ERROR3: pthread_mutex_destroy(&pContext->mFlushLock); ERROR2: ERROR1: free(pContext); ERROR0: free(pFsWriter); return NULL; } int deinitFsCacheThreadContext(FsWriter *pFsWriter) { int eError = 0; if (NULL == pFsWriter) { aloge("pFsWriter is NULL!!"); return -1; } FsCacheThreadContext *pContext = (FsCacheThreadContext*)pFsWriter->mPriv; if (NULL == pContext) { aloge("pContext is NULL!!"); return -1; } pContext->mThreadExitFlag = 1; cdx_sem_up_unique(&pContext->mWriteStartSem); pthread_join(pContext->mThreadId, (void*) &eError); cdx_sem_deinit(&pContext->mWriteDoneSem); cdx_sem_deinit(&pContext->mFlushDoneSem); cdx_sem_deinit(&pContext->mWriteStartSem); pthread_mutex_destroy(&pContext->mFlushLock); pContext->mpCache = NULL; pContext->mThreadId = 0; free(pContext); free(pFsWriter); return 0; }