sdk-hwV1.3/external/eyesee-mpp/framework/sun8iw21/media/usbcamera/VdecFrameManager.cpp

968 lines
29 KiB
C++
Executable File

/*
********************************************************************************
* eyesee framework module
*
* (c) Copyright 2010-2018, Allwinner Microelectronic Co., Ltd.
* All Rights Reserved
*
* File : VdecFrameManager.cpp
* Version: V1.0
* By :
* Date : 2018-12-13
* Description:
********************************************************************************
*/
#include <plat_log.h>
#include <mpi_uvc.h>
#include <uvcInput.h>
#include <mpi_vdec.h>
#include <mpi_sys.h>
#include <cmath>
#include <cstring>
#include "VdecFrameManager.h"
namespace EyeseeLinux {
VdecFrameManager::DoubleFrame::DoubleFrame()
{
memset(&mOutMainFrame, 0, sizeof(VIDEO_FRAME_INFO_S));
memset(&mOutSubFrame, 0, sizeof(VIDEO_FRAME_INFO_S));
mbMainValid = false;
mbSubValid = false;
mbMainRequestFlag = false;
mbMainReturnFlag = false;
mbSubRequestFlag = false;
mbSubReturnFlag = false;
}
VdecFrameManager::DoubleFrame::DoubleFrame(const DoubleFrame &lhs)
{
mOutMainFrame = lhs.mOutMainFrame;
mOutSubFrame = lhs.mOutSubFrame;
mbMainValid = lhs.mbMainValid;
mbSubValid = lhs.mbSubValid;
mbMainRequestFlag = lhs.mbMainRequestFlag;
mbMainReturnFlag = lhs.mbMainReturnFlag;
mbSubRequestFlag = lhs.mbSubRequestFlag;
mbSubReturnFlag = lhs.mbSubReturnFlag;
}
VdecFrameManager::DoRequestVdecFrameThread::DoRequestVdecFrameThread(VdecFrameManager *p)
:mpVdecFrameManager(p)
{
mThreadState = State::IDLE;
status_t ret = run("VFMReqVDecFrame");
if(ret != NO_ERROR)
{
aloge("fatal error! run thread fail!");
}
}
VdecFrameManager::DoRequestVdecFrameThread::~DoRequestVdecFrameThread()
{
EyeseeMessage msg;
msg.mMsgType = (int)MsgType::Exit;
mMsgQueue.queueMessage(&msg);
join();
mMsgQueue.flushMessage();
}
status_t VdecFrameManager::DoRequestVdecFrameThread::startRequestVdecFrame()
{
AutoMutex lock(mThreadStateLock);
if(State::STARTED == mThreadState)
{
alogd("already in started");
return NO_ERROR;
}
EyeseeMessage msg;
msg.mMsgType = (int)MsgType::SetState;
msg.mPara0 = (int)State::STARTED;
mMsgQueue.queueMessage(&msg);
while(State::STARTED != mThreadState)
{
mThreadCondStateComplete.wait(mThreadStateLock);
}
return NO_ERROR;
}
status_t VdecFrameManager::DoRequestVdecFrameThread::stopRequestVdecFrame()
{
AutoMutex lock(mThreadStateLock);
if(State::IDLE == mThreadState)
{
alogd("already in idle");
return NO_ERROR;
}
EyeseeMessage msg;
msg.mMsgType = (int)MsgType::SetState;
msg.mPara0 = (int)State::IDLE;
mMsgQueue.queueMessage(&msg);
while(State::IDLE != mThreadState)
{
mThreadCondStateComplete.wait(mThreadStateLock);
}
return NO_ERROR;
}
bool VdecFrameManager::DoRequestVdecFrameThread::threadLoop()
{
if(!exitPending())
{
return RequestVdecFrameThread();
}
else
{
return false;
}
}
bool VdecFrameManager::DoRequestVdecFrameThread::RequestVdecFrameThread()
{
bool bRunningFlag = true;
EyeseeMessage msg;
status_t getMsgRet;
ERRORTYPE ret;
while(1)
{
PROCESS_MESSAGE:
getMsgRet = mMsgQueue.dequeueMessage(&msg);
if(getMsgRet == NO_ERROR)
{
if (MsgType::SetState == (MsgType)msg.mMsgType)
{
AutoMutex stateLock(mThreadStateLock);
if (mThreadState == (State)msg.mPara0)
{
alogw("Be careful! state is same[0x%x], check code!", mThreadState);
mThreadCondStateComplete.signal();
}
else
{
switch ((State)msg.mPara0)
{
case State::IDLE:
{
if (State::STARTED == mThreadState || State::PAUSE == mThreadState)
{
mThreadState = State::IDLE;
mThreadCondStateComplete.signal();
}
else
{
aloge("fatal error! wrong current state[0x%x]", mThreadState);
}
break;
}
case State::STARTED:
{
if (State::IDLE == mThreadState || State::PAUSE == mThreadState)
{
mThreadState = State::STARTED;
mThreadCondStateComplete.signal();
}
else
{
aloge("fatal error! wrong current state[0x%x]", mThreadState);
}
break;
}
default:
{
alogw("Be careful! don't support other state[0x%x] transition", msg.mPara0);
break;
}
}
}
}
else if(MsgType::Exit == (MsgType)msg.mMsgType)
{
// Kill thread
bRunningFlag = false;
goto _exit0;
}
goto PROCESS_MESSAGE;
}
if(State::STARTED == mThreadState)
{
VIDEO_FRAME_INFO_S stMainFrame;
VIDEO_FRAME_INFO_S stSubFrame;
ret = AW_MPI_VDEC_GetDoubleImage(mpVdecFrameManager->mVdecChn, &stMainFrame, &stSubFrame, 1000);
if(SUCCESS == ret)
{
AutoMutex lock(mpVdecFrameManager->mListLock);
if(mpVdecFrameManager->mIdleList.empty())
{
alogd("Be careful! idle list is empty? malloc more");
mpVdecFrameManager->mIdleList.emplace_back();
}
auto firstIt = mpVdecFrameManager->mIdleList.begin();
firstIt->mOutMainFrame = stMainFrame;
firstIt->mbMainValid = true;
if(mpVdecFrameManager->mbVdecSubOutputEnable)
{
firstIt->mOutSubFrame = stSubFrame;
firstIt->mbSubValid = true;
}
else
{
firstIt->mbSubValid = false;
}
firstIt->mbMainRequestFlag = false;
firstIt->mbMainReturnFlag = false;
firstIt->mbSubRequestFlag = false;
firstIt->mbSubReturnFlag = false;
mpVdecFrameManager->mReadyList.splice(mpVdecFrameManager->mReadyList.end(), mpVdecFrameManager->mIdleList, firstIt);
if(mpVdecFrameManager->mWaitReadyFrameFlag)
{
mpVdecFrameManager->mWaitReadyFrameFlag = false;
mpVdecFrameManager->mCondReadyFrame.signal();
}
}
else if(ERR_VDEC_NOBUF == ret)
{
alogd("Be careful! get no frame?");
}
else
{
alogw("Be careful! get frame from vdec fail[0x%x]", ret);
}
}
else
{
mMsgQueue.waitMessage();
}
}
_exit0:
return bRunningFlag;
}
VdecFrameManager::VdecFrameManager(VDEC_CHN nVdecChn, bool bVdecSubOutputEnable)
:mVdecChn(nVdecChn)
,mbVdecSubOutputEnable(bVdecSubOutputEnable)
{
mState = State::IDLE;
mbMainEnable = false;
mbSubEnable = false;
mIdleList.resize(mInitBufNums);
mWaitReadyFrameFlag = false;
mpRequestThread = new DoRequestVdecFrameThread(this);
if(nullptr == mpRequestThread)
{
aloge("fatal error! malloc fail!");
}
}
VdecFrameManager::~VdecFrameManager()
{
AutoMutex stateLock(mStateLock);
if(mState != State::IDLE)
{
aloge("fatal error! why state[%d] != IDLE?", mState);
}
if(mpRequestThread != nullptr)
{
delete mpRequestThread;
mpRequestThread = nullptr;
}
AutoMutex listLock(mListLock);
if(mReadyList.size() != 0)
{
aloge("fatal error! why readylist has [%d] elems?", mReadyList.size());
}
}
status_t VdecFrameManager::start()
{
status_t ret = NO_ERROR;
AutoMutex stateLock(mStateLock);
if(State::STARTED == mState)
{
alogd("already started!");
return NO_ERROR;
}
if(State::IDLE == mState)
{
ret = mpRequestThread->startRequestVdecFrame();
if(NO_ERROR == ret)
{
mState = State::STARTED;
}
else
{
aloge("fatal error! why reqeust thread can't start[0x%x]?", ret);
}
}
return ret;
}
status_t VdecFrameManager::stop()
{
AutoMutex stateLock(mStateLock);
return stop_l();
}
status_t VdecFrameManager::stop_l()
{
status_t ret = NO_ERROR;
if(State::IDLE == mState)
{
alogd("already idle!");
return NO_ERROR;
}
if(State::STARTED == mState)
{
{
AutoMutex listLock(mListLock);
if(false != mbMainEnable || false != mbSubEnable)
{
aloge("fatal error! vdec chn is not close, can't stop!");
return PERMISSION_DENIED;
}
}
ret = mpRequestThread->stopRequestVdecFrame();
if(NO_ERROR == ret)
{
status_t relRet = releaseAllFrames_l();
if(relRet != NO_ERROR)
{
aloge("fatal error! check code!");
}
mState = State::IDLE;
}
else
{
aloge("fatal error! why reqeust thread can't stop[0x%x]?", ret);
}
}
return ret;
}
status_t VdecFrameManager::enableUvcChannel(UvcChn chn)
{
status_t ret = NO_ERROR;
AutoMutex listLock(mListLock);
if(chn == UvcChn::VdecMainChn)
{
mbMainEnable = true;
}
else if(chn == UvcChn::VdecSubChn)
{
if(mbVdecSubOutputEnable)
{
mbSubEnable = true;
}
else
{
aloge("fatal error! vdeclib sub output is disable! can't enable uvcChannel[%d]", chn);
mbSubEnable = false;
ret = INVALID_OPERATION;
}
}
else
{
aloge("fatal error! wrong chn[%d] for vdecFrameManager!", chn);
ret = BAD_VALUE;
}
return ret;
}
status_t VdecFrameManager::disableUvcChannel(UvcChn chn)
{
status_t ret = NO_ERROR;
AutoMutex listLock(mListLock);
if(chn == UvcChn::VdecMainChn)
{
mbMainEnable = false;
}
else if(chn == UvcChn::VdecSubChn)
{
mbSubEnable = false;
}
else
{
aloge("fatal error! wrong chn[%d] for vdecFrameManager!", chn);
ret = BAD_VALUE;
}
return ret;
}
/*
* @timeout -1: wait forever, 0:return immediately, >0:unit:ms.
* @return BAD_VALUE, PERMISSION_DENIED, NO_ERROR
*/
status_t VdecFrameManager::getFrame(UvcChn chn, VIDEO_FRAME_INFO_S *pFrameInfo, int timeout) //unit:ms
{
status_t eRet = NO_ERROR;
if(chn != UvcChn::VdecMainChn && chn != UvcChn::VdecSubChn)
{
aloge("fatal error! wrong chn[%d], must vdec chn!", chn);
return BAD_VALUE;
}
if(NULL == pFrameInfo)
{
aloge("fatal error! null pointer!");
return BAD_VALUE;
}
{
AutoMutex stateLock(mStateLock);
if(mState != State::STARTED)
{
alogw("Be careful! vdec frame manager is not in state::started!");
return PERMISSION_DENIED;
}
}
AutoMutex listLock(mListLock);
if(chn == UvcChn::VdecMainChn)
{
if(mbMainEnable != true)
{
aloge("fatal error! uvcChannel[%d] is disable! why request sub frame?", chn);
return BAD_VALUE;
}
}
if(chn == UvcChn::VdecSubChn)
{
if(mbVdecSubOutputEnable != true)
{
aloge("fatal error! vdeclib sub output is disable! why request sub frame?");
return BAD_VALUE;
}
if(mbSubEnable != true)
{
aloge("fatal error! uvcChannel[%d] is disable! why request sub frame?", chn);
return BAD_VALUE;
}
}
TryToGetFrame:
bool bFind = false;
if(!mReadyList.empty())
{
for(DoubleFrame& i : mReadyList)
{
if(chn == UvcChn::VdecMainChn)
{
if(i.mbMainValid)
{
if(true == i.mbMainRequestFlag)
{
continue;
}
else
{
if(i.mbMainReturnFlag != false)
{
aloge("fatal error! mainFrame req/ret flag[%d][%d]", i.mbMainRequestFlag, i.mbMainReturnFlag);
}
//set this node main frame to pFrameInfo
i.mbMainRequestFlag = true;
*pFrameInfo = i.mOutMainFrame;
bFind = true;
break;
}
}
else
{
aloge("fatal error! mainFrame is not valid?");
}
}
else if(chn == UvcChn::VdecSubChn)
{
if(i.mbSubValid)
{
if(true == i.mbSubRequestFlag)
{
continue;
}
else
{
if(i.mbSubReturnFlag != false)
{
aloge("fatal error! subFrame req/ret flag[%d][%d]", i.mbSubRequestFlag, i.mbSubReturnFlag);
}
//set this node sub frame to pFrameInfo
i.mbSubRequestFlag = true;
*pFrameInfo = i.mOutSubFrame;
bFind = true;
break;
}
}
else
{
aloge("fatal error! subFrame is not valid?");
}
}
else
{
aloge("fatal error! check code!");
}
}
}
if(bFind!=true)
{
if(timeout < 0)
{
mWaitReadyFrameFlag = true;
mCondReadyFrame.wait(mListLock);
goto TryToGetFrame;
}
else if(0 == timeout)
{
eRet = NOT_ENOUGH_DATA;
}
else if(timeout > 0)
{
mWaitReadyFrameFlag = true;
int64_t reltime = timeout*1000000;
status_t ret = mCondReadyFrame.waitRelative(mListLock, reltime);
if (TIMED_OUT == ret)
{
alogv("wait ready frame timeout[%d]ms, ret[%d]", timeout, ret);
eRet = NOT_ENOUGH_DATA;
mWaitReadyFrameFlag = false;
}
else if (NO_ERROR == ret)
{
if(mWaitReadyFrameFlag != false)
{
aloge("fatal error! why ready frame flag[%d] != false?", mWaitReadyFrameFlag);
mWaitReadyFrameFlag = false;
}
goto TryToGetFrame;
}
else
{
aloge("fatal error! pthread cond wait timeout ret[%d]", ret);
eRet = NOT_ENOUGH_DATA;
mWaitReadyFrameFlag = false;
}
}
}
else
{
eRet = NO_ERROR;
}
return eRet;
}
status_t VdecFrameManager::releaseFrame(UvcChn chn, VIDEO_FRAME_INFO_S *pFrameInfo)
{
status_t eRet = NO_ERROR;
if(chn != UvcChn::VdecMainChn && chn != UvcChn::VdecSubChn)
{
aloge("fatal error! wrong chn[%d], must vdec chn!", chn);
return BAD_VALUE;
}
if(NULL == pFrameInfo)
{
aloge("fatal error! null pointer!");
return BAD_VALUE;
}
AutoMutex listLock(mListLock);
if(chn == UvcChn::VdecMainChn)
{
if(mbMainEnable != true)
{
aloge("fatal error! uvcChannel[%d] is disable! why request sub frame?", chn);
return BAD_VALUE;
}
}
if(chn == UvcChn::VdecSubChn)
{
if(mbVdecSubOutputEnable != true)
{
aloge("fatal error! vdeclib sub output is disable! why request sub frame?");
return BAD_VALUE;
}
if(mbSubEnable != true)
{
aloge("fatal error! uvcChannel[%d] is disable! why request sub frame?", chn);
return BAD_VALUE;
}
}
if(mReadyList.empty())
{
aloge("fatal error! ready list is empty, why release frame?");
return BAD_VALUE;
}
int nFindNum = 0;
for(std::list<DoubleFrame>::iterator it = mReadyList.begin(); it != mReadyList.end();)
{
bool bItIncrease = false;
if(chn == UvcChn::VdecMainChn)
{
if(it->mbMainValid != true)
{
aloge("fatal error! mainFrame is not valid?");
}
if(pFrameInfo->mId == it->mOutMainFrame.mId)
{
if(0 == nFindNum)
{
if(true == it->mbMainRequestFlag && false == it->mbMainReturnFlag)
{
it->mbMainReturnFlag = true;
//decide if we can release frame.
bool bCanReturn = false;
if(true == it->mbSubValid)
{
if(true == mbSubEnable)
{
if(true==it->mbSubRequestFlag && true==it->mbSubReturnFlag)
{
bCanReturn = true;
}
}
else
{
if((true==it->mbSubRequestFlag && true==it->mbSubReturnFlag)
|| (false==it->mbSubRequestFlag && false==it->mbSubReturnFlag))
{
bCanReturn = true;
}
else
{
aloge("fatal error! sub frame channel is disable, why sub req[%d]/ret[%d] is not match?", it->mbSubRequestFlag, it->mbSubReturnFlag);
}
}
}
else
{
bCanReturn = true;
}
if(bCanReturn)
{
int ret = AW_MPI_VDEC_ReleaseDoubleImage(mVdecChn, &it->mOutMainFrame, &it->mOutSubFrame);
if(SUCCESS == ret)
{
auto tmp = it;
++it;
mIdleList.splice(mIdleList.end(), mReadyList, tmp);
bItIncrease = true;
}
else
{
aloge("fatal error! release frame to vdec fail[0x%x], check code!", ret);
}
}
}
else
{
aloge("fatal error! why value is not respected, frameId[%d], mainReq[%d]/Ret[%d]", it->mbMainRequestFlag, it->mbMainReturnFlag);
}
}
else
{
aloge("fatal error! already exist frameId[%d], num[%d]?", pFrameInfo->mId, nFindNum);
}
nFindNum++;
}
}
else if(chn == UvcChn::VdecSubChn)
{
if(it->mbSubValid != true)
{
aloge("fatal error! subFrame is not valid?");
}
if(pFrameInfo->mId == it->mOutSubFrame.mId)
{
if(0 == nFindNum)
{
if(true == it->mbSubRequestFlag && false == it->mbSubReturnFlag)
{
it->mbSubReturnFlag = true;
//decide if we can release frame.
bool bCanReturn = false;
if(true == it->mbMainValid)
{
if(true == mbMainEnable)
{
if(true==it->mbMainRequestFlag && true==it->mbMainReturnFlag)
{
bCanReturn = true;
}
}
else
{
if((true==it->mbMainRequestFlag && true==it->mbMainReturnFlag)
|| (false==it->mbMainRequestFlag && false==it->mbMainReturnFlag))
{
bCanReturn = true;
}
else
{
aloge("fatal error! main frame channel is disable, why main req[%d]/ret[%d] is not match?", it->mbMainRequestFlag, it->mbMainReturnFlag);
}
}
}
else
{
bCanReturn = true;
}
if(bCanReturn)
{
int ret = AW_MPI_VDEC_ReleaseDoubleImage(mVdecChn, &it->mOutMainFrame, &it->mOutSubFrame);
if(SUCCESS == ret)
{
auto tmp = it;
++it;
mIdleList.splice(mIdleList.end(), mReadyList, tmp);
bItIncrease = true;
}
else
{
aloge("fatal error! release frame to vdec fail[0x%x], check code!", ret);
}
}
}
else
{
aloge("fatal error! why value is not respected, frameId[%d], mainReq[%d]/Ret[%d]", it->mbSubRequestFlag, it->mbSubReturnFlag);
}
}
else
{
aloge("fatal error! already exist frameId[%d], num[%d]?", pFrameInfo->mId, nFindNum);
}
nFindNum++;
}
}
else
{
aloge("fatal error! wrong chn[%d], must vdec chn!", chn);
}
if(!bItIncrease)
{
++it;
}
}
if(0 == nFindNum)
{
aloge("fatal error! uvcChn[%d] not find frameId[%d]", chn, pFrameInfo->mId);
}
return eRet;
}
/**
* when mpi_vdec send callback message of no frame buffer, eyeseeUsbCamera will call this fuction to release frame to vdec
* to let vdec thread decode contiguously.
*/
status_t VdecFrameManager::VdecNotifyNoFrameBuffer()
{
status_t eError = NO_ERROR;
AutoMutex stateLock(mStateLock);
if(mState != State::STARTED)
{
alogd("ignore vdec no frame buffer message.");
return NO_ERROR;
}
AutoMutex listLock(mListLock);
if(mReadyList.empty())
{
alogw("Be careful! ready list is empty, can't release frame!");
return NO_ERROR;
}
bool bRelease = false;
for(std::list<DoubleFrame>::iterator it = mReadyList.begin(); it != mReadyList.end();)
{
if(false == it->mbMainValid && false == it->mbSubValid)
{
aloge("fatal error! check code!");
}
int bMainPermit = false;
int bSubPermit = false;
if(it->mbMainValid)
{
if(true == mbMainEnable)
{
if(true==it->mbMainRequestFlag)
{
if(true==it->mbMainReturnFlag)
{
bMainPermit = true;
}
}
else
{
if(false!=it->mbMainReturnFlag)
{
aloge("fatal error! check code!");
}
bMainPermit = true;
}
}
else
{
if((true==it->mbMainRequestFlag && true==it->mbMainReturnFlag)
|| (false==it->mbMainRequestFlag && false==it->mbMainReturnFlag))
{
bMainPermit = true;
}
else
{
aloge("fatal error! check code![%d][%d]", it->mbMainRequestFlag, it->mbMainReturnFlag);
bMainPermit = true;
}
}
}
else
{
bMainPermit = true;
}
if(it->mbSubValid)
{
if(true == mbSubEnable)
{
if(true==it->mbSubRequestFlag)
{
if(true==it->mbSubReturnFlag)
{
bSubPermit = true;
}
}
else
{
if(false!=it->mbSubReturnFlag)
{
aloge("fatal error! check code!");
}
bSubPermit = true;
}
}
else
{
if((true==it->mbSubRequestFlag && true==it->mbSubReturnFlag)
|| (false==it->mbSubRequestFlag && false==it->mbSubReturnFlag))
{
bSubPermit = true;
}
else
{
aloge("fatal error! check code![%d][%d]", it->mbSubRequestFlag, it->mbSubReturnFlag);
bSubPermit = true;
}
}
}
else
{
bSubPermit = true;
}
if(bMainPermit && bSubPermit)
{
int ret = AW_MPI_VDEC_ReleaseDoubleImage(mVdecChn, &it->mOutMainFrame, &it->mOutSubFrame);
if(SUCCESS == ret)
{
auto tmp = it;
++it;
mIdleList.splice(mIdleList.end(), mReadyList, tmp);
bRelease = true;
break;
}
else
{
aloge("fatal error! release frame to vdec fail[0x%x], check code!", ret);
}
}
++it;
}
if(bRelease)
{
eError = NO_ERROR;
}
else
{
alogd("Be careful! [%d]ready frames can't return.", mReadyList.size());
eError = NOT_ENOUGH_DATA;
}
return eError;
}
status_t VdecFrameManager::releaseAllFrames_l()
{
status_t ret = NO_ERROR;
AutoMutex listLock(mListLock);
for(std::list<DoubleFrame>::iterator it = mReadyList.begin(); it != mReadyList.end();)
{
bool bIncrease = false;
if(false == it->mbMainValid && false == it->mbSubValid)
{
aloge("fatal error! check code!");
}
int bMainPermit = false;
int bSubPermit = false;
if(it->mbMainValid)
{
if((true==it->mbMainRequestFlag && true==it->mbMainReturnFlag)
|| (false==it->mbMainRequestFlag && false==it->mbMainReturnFlag))
{
bMainPermit = true;
}
else
{
aloge("fatal error! check code![%d][%d]", it->mbMainRequestFlag, it->mbMainReturnFlag);
}
}
else
{
bMainPermit = true;
}
if(it->mbSubValid)
{
if((true==it->mbSubRequestFlag && true==it->mbSubReturnFlag)
|| (false==it->mbSubRequestFlag && false==it->mbSubReturnFlag))
{
bSubPermit = true;
}
else
{
aloge("fatal error! check code![%d][%d]", it->mbSubRequestFlag, it->mbSubReturnFlag);
}
}
else
{
bSubPermit = true;
}
if(bMainPermit && bSubPermit)
{
int ret = AW_MPI_VDEC_ReleaseDoubleImage(mVdecChn, &it->mOutMainFrame, &it->mOutSubFrame);
if(SUCCESS == ret)
{
auto tmp = it;
++it;
mIdleList.splice(mIdleList.end(), mReadyList, tmp);
bIncrease = true;
}
else
{
aloge("fatal error! release frame to vdec fail[0x%x], check code!", ret);
}
}
else
{
aloge("fatal error! check code!");
}
if(false == bIncrease)
{
++it;
}
}
if(mReadyList.empty())
{
ret = NO_ERROR;
}
else
{
aloge("fatal error! ready list still has nodes!");
ret = UNKNOWN_ERROR;
}
return ret;
}
}