/* ------------------------------------------------------------------
 * Copyright (C) 1998-2010 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.
 * -------------------------------------------------------------------
 */


#include "oscl_scheduler.h"


#include "oscl_error.h"
#include "oscl_tickcount.h"
#include "pvlogger.h"
#include "oscl_error_trapcleanup.h"
#include "pvlogger.h"
#include "oscl_tls.h"
#include "oscl_int64_utils.h"

#define OSCL_DISABLE_WARNING_CONDITIONAL_IS_CONSTANT
#include "osclconfig_compiler_warnings.h"


#include "oscl_scheduler_tuneables.h"


/////////////////////////////////////
// Logger Macros
/////////////////////////////////////

//LOGERROR is for scheduler errors.
//This logging also goes to stderr on platforms with ANSI stdio.
#define LOGERROR(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_REL,iLogger,PVLOGMSG_ERR,m);

//LOGNOTICE is for scheduler start/stop, install/uninstall notices.
#define LOGNOTICE(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG,iLogger,PVLOGMSG_NOTICE,m);

#if(PV_SCHED_ENABLE_PERF_LOGGING)
//LOGSTATS is for logging the AO summary statistics.  These are loggged either
//when the AO is deleted, or when the scheduling ends.
#define LOGSTATS(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF,iLogger,PVLOGMSG_INFO,m);
//LOGPERF is for detailed performance logging.
#define LOGPERF_LEVEL PVLOGMSG_INFO
#define LOGPERF(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF,iLogger,LOGPERF_LEVEL,m);
//LOGPERF2 is for highest detail on scheduler activity.
#define LOGPERF2_LEVEL PVLOGMSG_INFO+1
#define LOGPERF2(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF,iLogger,LOGPERF2_LEVEL,m);
//max perf log string length
#define LOGPERFMAXSTR 64
#define RESET_LOG_PERF(m)\
    ResetLogPerf();\
    LOGPERF(m);
#else
#define LOGSTATS(m)
#define LOGPERF(m)
#define LOGPERF2(m)
#define LOGPERFMAXSTR 64
#define RESET_LOG_PERF(m)
#endif//(PV_SCHED_ENABLE_PERF_LOGGING)

/////////////////////////////////////
// DLL Entry
/////////////////////////////////////
#ifndef OSCL_COMBINED_DLL
#include "oscl_dll.h"
OSCL_DLL_ENTRY_POINT_DEFAULT()
#endif

/////////////////////////////////////
// OsclScheduler
/////////////////////////////////////

OSCL_EXPORT_REF void OsclScheduler::Init(const char *name, Oscl_DefAlloc *alloc, int nreserve)
//Init the scheduler for this thread.
{
    int32 err;
    OSCL_TRY(err,
             OsclExecScheduler *sched = OsclExecScheduler::NewL(name, alloc, nreserve);
             sched->InstallScheduler(););
    if (err != OsclErrNone)
        OsclError::Leave(OsclErrNotInstalled);
}



OSCL_EXPORT_REF void OsclScheduler::Cleanup()
//Cleanup the scheduler for this thread.
{
    OsclExecSchedulerCommonBase *sched = OsclExecSchedulerCommonBase::GetScheduler();
    if (!sched)
        OsclError::Leave(OsclErrNotInstalled);
    sched->UninstallScheduler();
    Oscl_DefAlloc *alloc = sched->iAlloc;
    bool default_alloc = (sched->iAlloc == &sched->iDefAlloc);
    sched->~OsclExecSchedulerCommonBase();
    if (default_alloc)
    {
        OsclMemAllocator defalloc;
        defalloc.deallocate(sched);
    }
    else
    {
        alloc->deallocate(sched);
    }
}

/////////////////////////////////////
// OsclExecSchedulerCommonBase
/////////////////////////////////////

/////////////////////////////////////
// OsclExecScheduler
/////////////////////////////////////

//use the TLS registry, or the singleton registry if no TLS.
//Note: singleton registry only works for single-threaded
//scenarios, since this implementation assumes a per-thread registry.
#include "oscl_error.h"
#define PVSCHEDULER_REGISTRY OsclTLSRegistryEx
#define PVSCHEDULER_REGISTRY_ID OSCL_TLS_ID_PVSCHEDULER


/////////////////////////////////////
//For AO statistics.
/////////////////////////////////////

#if !(PV_SCHED_ENABLE_AO_STATS)
//no stats
#define PVTICK uint32
#define PVTICK_INT uint32
#define INIT_TICK(tick)
#define SET_TICK(tick)
#define GET_TICKFREQ(tick)
#define TICK_INT(tick1)
#define TICKSTR ""
#define TICK_INT_STR ""
#define TICK_INT_EXPR(x)
#else
//else use the oscl timer.
#define PVTICK uint32
#define PVTICK_INT uint32
#define INIT_TICK(tick) tick=0
#define SET_TICK(tick) tick=OsclTickCount::TickCount()
#define GET_TICKFREQ(tick) tick=OsclTickCount::TickCountFrequency()
#define TICK_INT(tick1) tick1
#define TICKSTR "Ticks"
#define TICK_INT_STR "%u"
#define TICK_INT_EXPR(x) x
#endif

#if !(PV_SCHED_ENABLE_AO_STATS)
#define DIFF_TICK(tick1,diff)
#define UPDATE_RUNERROR_TIME(x,y)
#define UPDATE_RUNL_TIME(x,y)
#define UPDATE_LEAVE_CODE(x,err)
#else
#define DIFF_TICK(tick1,diff) PVTICK _now;SET_TICK(_now);diff=TICK_INT(_now)-TICK_INT(tick1)
#define UPDATE_RUNERROR_TIME(stats,delta)\
        if(stats->i64Valid) stats->i64TotalTicksInRun+=delta;\
        else stats->iTotalTicksInRun+=delta;\
        if(delta>stats->iMaxTicksInRun) stats->iMaxTicksInRun=delta;\
        stats->iNumRunError++
#define UPDATE_RUNL_TIME(stats,delta)\
        if(stats->i64Valid) stats->i64TotalTicksInRun+=delta;\
        else stats->iTotalTicksInRun+=delta;\
        if(delta>stats->iMaxTicksInRun) stats->iMaxTicksInRun=delta;\
        stats->iNumRun++;
#define UPDATE_LEAVE_CODE(stats,err)if (err!=OsclErrNone)stats->iLeave=err
#endif

#if (PV_SCHED_ENABLE_LOOP_STATS)
#define DECLARE_LOOP_STATS int64 loopdelta = 0; PVTICK looptime;

#define START_LOOP_STATS(stats)SET_TICK(looptime);

#define START_WAIT_LOOP_STATS(rc,stats)\
        if (rc<1)\
            SET_TICK(looptime);

#define END_LOOP_STATS(stats)\
        {\
            DIFF_TICK(looptime,loopdelta);\
            if(stats->i64Valid) stats->i64TotalTicksInRun+=loopdelta;\
            else stats->iTotalTicksInRun+=Oscl_Int64_Utils::get_int64_lower32(loopdelta);\
            stats->iNumRun++;\
            LOGPERF((0,"PVSCHED: Run %d %s AO %s",(int32)loopdelta,TICKSTR,stats->iAOName.get_cstr()));\
        }

#define END_WAIT_LOOP_STATS(rc,stats)\
        if (rc<1)\
        {\
            DIFF_TICK(looptime,loopdelta);\
            if(stats->i64Valid) stats->i64TotalTicksInRun+=loopdelta;\
            else stats->iTotalTicksInRun+=Oscl_Int64_Utils::get_int64_lower32(loopdelta);\
            stats->iNumRun++;\
            LOGPERF((0,"PVSCHED: Run %d %s AO %s",(int32)loopdelta,TICKSTR,stats->iAOName.get_cstr()));\
        }

#else
#define DECLARE_LOOP_STATS
#define START_LOOP_STATS(stats)
#define START_WAIT_LOOP_STATS(rc,stats)
#define END_LOOP_STATS(stats)
#define END_WAIT_LOOP_STATS(rc,stats)

#endif

#if(PV_SCHED_ENABLE_PERF_LOGGING)
void OsclExecSchedulerCommonBase::ResetLogPerf()
{
    //print total time spend in prior interval of continuous Run calls.
    if (iLogPerfTotal > 0)
    {
        LOGPERF((0, "PVSCHED: Prior Interval %d %s", iLogPerfTotal, TICKSTR));
    }
    //reset interval.
    iLogPerfTotal = 0;
    //reset indentation to zero.
    iLogPerfIndentStrLen = 0;
    if (iLogPerfIndentStr)
        iLogPerfIndentStr[iLogPerfIndentStrLen] = '\0';
}

void OsclExecSchedulerCommonBase::IncLogPerf(uint32 delta)
{
    //add to total interval time.
    iLogPerfTotal += delta;
    //add a space to the indent string up to the max.
    if (iLogPerfIndentStr
            && iLogPerfIndentStrLen < LOGPERFMAXSTR)
    {
        iLogPerfIndentStr[iLogPerfIndentStrLen++] = ' ';
        iLogPerfIndentStr[iLogPerfIndentStrLen] = '\0';
    }
}
#endif//(PV_SCHED_ENABLE_PERF_LOGGING)

OsclExecSchedulerCommonBase* OsclExecSchedulerCommonBase::GetScheduler()
//static function to get currently installed scheduler
//for this thread.
{
    OsclExecSchedulerCommonBase *current = (OsclExecSchedulerCommonBase*)PVSCHEDULER_REGISTRY::getInstance(PVSCHEDULER_REGISTRY_ID);
    return current;
}

OsclExecSchedulerCommonBase* OsclExecSchedulerCommonBase::SetScheduler(OsclExecSchedulerCommonBase *a)
//static function to set currently installed scheduler
//for this thread. return previous scheduler, if any.
{
    OsclExecSchedulerCommonBase* temp = GetScheduler();
    PVSCHEDULER_REGISTRY::registerInstance(a, PVSCHEDULER_REGISTRY_ID);
    return temp;
}

OSCL_EXPORT_REF OsclNameString<PVSCHEDNAMELEN> *OsclExecSchedulerCommonBase::GetName()
//static function to get scheduler name for this thread.
{
    OsclExecSchedulerCommonBase *sched = GetScheduler();
    if (sched)
        return &sched->iName;
    else
        return NULL;
}

OSCL_EXPORT_REF uint32 OsclExecSchedulerCommonBase::GetId()
{
    return PVThreadContext::Id();
}

OsclExecScheduler * OsclExecScheduler::NewL(const char *name, Oscl_DefAlloc *alloc, int nreserve)
{
    OsclExecScheduler *self;
    OsclMemAllocator defalloc;
    OsclAny* ptr = (alloc) ? alloc->ALLOCATE(sizeof(OsclExecScheduler))
                   : defalloc.ALLOCATE(sizeof(OsclExecScheduler));
    OsclError::LeaveIfNull(ptr);
    self = OSCL_PLACEMENT_NEW(ptr, OsclExecScheduler(alloc));
    OsclError::PushL(self);
    self->ConstructL(name, nreserve);
    OsclError::Pop();
    return self;
}

OsclExecSchedulerCommonBase::~OsclExecSchedulerCommonBase()
{
    //make sure scheduler is not currently installed in
    //any thread.
    if (IsInstalled())
        OsclError::Leave(OsclErrInvalidState);//scheduler not stopped

    if (iStopper)
    {
        iStopper->~PVSchedulerStopper();
        iAlloc->deallocate(iStopper);
    }
#if(PV_SCHED_ENABLE_PERF_LOGGING)
    if (iLogPerfIndentStr)
        _oscl_free(iLogPerfIndentStr);
#endif
}

OsclExecScheduler::~OsclExecScheduler()
{
}

OsclExecSchedulerCommonBase::OsclExecSchedulerCommonBase(Oscl_DefAlloc *alloc)
{
    iAlloc = (alloc) ? alloc : &iDefAlloc;
#if(PV_SCHED_ENABLE_PERF_LOGGING)
    iLogPerfIndentStr = NULL;
    iLogPerfTotal = 0;
#endif
}

OsclExecScheduler::OsclExecScheduler(Oscl_DefAlloc *alloc)
        : OsclExecSchedulerCommonBase(alloc)
{
}

void OsclExecSchedulerCommonBase::ConstructL(const char *name, int nreserve)
{
    iNumAOAdded = 1;

    OsclAny* ptr = iAlloc->ALLOCATE(sizeof(PVSchedulerStopper));
    OsclError::LeaveIfNull(ptr);
    iStopper = new(ptr) PVSchedulerStopper;

#if(PV_SCHED_ENABLE_AO_STATS)
    ConstructStatQ();
#endif

    InitExecQ(nreserve);

    iBlockingMode = false;
    iNativeMode = false;
    iName.Set(name);
    iLogger = PVLogger::GetLoggerObject("pvscheduler");

#if (PV_SCHED_ENABLE_PERF_LOGGING)
    iLogPerfIndentStr = (char*)_oscl_malloc(LOGPERFMAXSTR + 1);
    OsclError::LeaveIfNull(iLogPerfIndentStr);
    ResetLogPerf();
#endif
}

void OsclExecScheduler::ConstructL(const char *name, int nreserve)
{
    OsclExecSchedulerCommonBase::ConstructL(name, nreserve);
}

void OsclExecSchedulerCommonBase::InstallScheduler()
{
    //make sure this scheduler is not installed in
    //any thread.
    if (IsInstalled())
        OsclError::Leave(OsclErrAlreadyInstalled);
    //make sure no scheduler is installed in this thread.
    if (GetScheduler())
        OsclError::Leave(OsclErrAlreadyInstalled);

    SetScheduler(this);

    iThreadContext.EnterThreadContext();

    iErrorTrapImp = OsclErrorTrap::GetErrorTrapImp();
    if (!iErrorTrapImp)
        OsclError::Leave(OsclErrNotInstalled);//error trap not installed.

    if (iStopperCrit.Create() != OsclProcStatus::SUCCESS_ERROR)
        OsclError::Leave(OsclErrSystemCallFailed);//mutex error

    iResumeSem.Create();
    iDoStop = iDoSuspend = iSuspended = false;

    iReadyQ.ThreadLogon();

    LOGNOTICE((0, "PVSCHED:Scheduler '%s', Thread 0x%x: Installed", iName.Str(), PVThreadContext::Id()));

#if (PV_SCHED_ENABLE_PERF_LOGGING)
    //print tick frequencies that will show up in the perf log.
    PVTICK f;
    GET_TICKFREQ(f);
    PVTICK_INT tickint = TICK_INT(f);
    LOGPERF((0, "PVSCHED: %s frequency %s", TICKSTR, TICK_INT_STR, TICK_INT_EXPR(tickint)));
    OSCL_UNUSED_ARG(tickint);
#endif
}

void OsclExecSchedulerCommonBase::UninstallScheduler()
{
    //make sure this scheduler is currently installed in
    //this thread.
    if (!IsInstalled() || GetScheduler() != this)
        OsclError::Leave(OsclErrNotInstalled);


    if (iBlockingMode)
    {
        //in case a thread error happened, go ahead and end
        //scheduling.
        OsclErrorTrapImp *trap = OsclErrorTrapImp::GetErrorTrap();
        if (trap
                && trap->iLeave != OsclErrNone)
            EndScheduling();
        //make sure scheduler is stopped.  If not, leave instead.
        if (IsStarted())
            OsclError::Leave(OsclErrInvalidState);//scheduler not stopped
    }
    else if (IsStarted())
    {
        //end non-blocking scheduling
        EndScheduling();
    }

    SetScheduler(NULL);

    iThreadContext.ExitThreadContext();

    CleanupExecQ();

    //Cleanup the stat queue.
#if(PV_SCHED_ENABLE_AO_STATS)
    CleanupStatQ();
#endif

    if (iStopperCrit.Close() != OsclProcStatus::SUCCESS_ERROR)
        OsclError::Leave(OsclErrSystemCallFailed);//mutex error

    iReadyQ.ThreadLogoff();
    iResumeSem.Close();

    LOGNOTICE((0, "PVSCHED:Scheduler '%s', Thread 0x%x: Uninstalled", iName.Str(), PVThreadContext::Id()));
}

OSCL_EXPORT_REF OsclExecScheduler* OsclExecScheduler::Current()
//static routine to get current scheduler.
{
    return (OsclExecScheduler*)GetScheduler();
}

bool OsclExecSchedulerCommonBase::IsStarted()
{
    iStopperCrit.Lock();
    bool val = (iStopper->IsAdded()) ? true : false;
    iStopperCrit.Unlock();
    return val;
}

inline bool OsclExecSchedulerCommonBase::IsInstalled()
{
    return iThreadContext.iOpen;
}

void OsclExecSchedulerCommonBase::BeginScheduling(bool blocking, bool native)
//called before entering scheduling loop.
{
    //make sure scheduler is installed...
    if (!IsInstalled() || GetScheduler() != this)
        OsclError::Leave(OsclErrNotInstalled);

    //make sure scheduler is idle...
    if (IsStarted())
        OsclError::Leave(OsclErrInvalidState);

    iBlockingMode = blocking;
    iNativeMode = native;

    //Add stopper AO to scheduler.
    iStopperCrit.Lock();
    {
        iStopper->AddToScheduler();
        iStopper->PendForExec();
    }
    iStopperCrit.Unlock();

#if(PV_SCHED_ENABLE_PERF_LOGGING)
    ResetLogPerf();
#endif
#if(PV_SCHED_ENABLE_AO_STATS)
    BeginStats();
#endif
}


void OsclExecSchedulerCommonBase::EndScheduling()
//called after exiting scheduling loop.
{
    //see if it's already stopped..
    if (!IsStarted())
        return;

    //remove stopper AO.
    iStopperCrit.Lock();
    iStopper->RemoveFromScheduler();
    iStopperCrit.Unlock();

#if(PV_SCHED_ENABLE_AO_STATS)
    EndStats();
#endif
}

#if(PV_SCHED_ENABLE_AO_STATS)
void OsclExecSchedulerCommonBase::ConstructStatQ()
{
    //create a placeholder for summary stats for
    //all AOs that are not PVActiveBase.
    for (uint32 i = 0; i < EOtherExecStats_Last; i++)
        iOtherExecStats[i] = NULL;

    OsclAny* ptr = iAlloc->ALLOCATE(sizeof(PVActiveStats));
    OsclError::LeaveIfNull(ptr);
    iOtherExecStats[EOtherExecStats_NativeOS] = OSCL_PLACEMENT_NEW(ptr, PVActiveStats(this, "Sched_TotalNativeOS", NULL));
    //init the stat queue offset.
    {
        int offset = (int) & (iOtherExecStats[EOtherExecStats_NativeOS])->iPVStatQLink - (int)(iOtherExecStats[EOtherExecStats_NativeOS]);
        iPVStatQ.SetOffset(offset);
    }

#if(PV_SCHED_ENABLE_LOOP_STATS)
    //create nodes for summary stats for scheduler loop time

    ptr = iAlloc->ALLOCATE(sizeof(PVActiveStats));
    OsclError::LeaveIfNull(ptr);
    iOtherExecStats[EOtherExecStats_QueueTime] = OSCL_PLACEMENT_NEW(ptr, PVActiveStats(this, "Sched_QueueTime", NULL));

    ptr = iAlloc->ALLOCATE(sizeof(PVActiveStats));
    OsclError::LeaveIfNull(ptr);
    iOtherExecStats[EOtherExecStats_WaitTime] = OSCL_PLACEMENT_NEW(ptr, PVActiveStats(this, "Sched_WaitTime", NULL));

#endif
    //add the non-AO stats nodes to the stat Q.
    {
        for (uint32 i = 0; i < EOtherExecStats_Last; i++)
        {
            if (iOtherExecStats[i])
                iPVStatQ.InsertTail(*iOtherExecStats[i]);
        }
    }
}

void OsclExecSchedulerCommonBase::CleanupStatQ()
{
    while (!iPVStatQ.IsEmpty())
    {
        PVActiveStats* first = iPVStatQ.Head();
        first->iPVStatQLink.Remove();
        first->~PVActiveStats();
        first->iScheduler->iAlloc->deallocate(first);
    }
}

void OsclExecSchedulerCommonBase::ShowStats(PVActiveStats *active)
//static routine to print stats for a PV AO.
{
    if (!active)
        return;

    //don't print any AOs that never ran.
    if ((active->iNumRun + active->iNumCancel) == 0)
        return;

    PVLogger* iLogger = PVLogger::GetLoggerObject("pvscheduler");
    if (active->i64Valid)
    {
        int64 avgTicksPerRun = (active->iNumRun == 0) ? 0 : active->i64TotalTicksInRun / (int64)active->iNumRun;

        LOGSTATS((0, "PVSCHED:Scheduler '%s', AO '%s': Pri %d NumRun %d, MaxTicksInRun 0x%x, AvgTicksPerRun (hi,lo) (0x%x,0x%08x) Units %s, NumCancel %d, NumError %d, LeaveCode %d"
                  , active->iScheduler->iName.Str()
                  , active->iAOName.get_cstr()
                  , active->iPriority
                  , active->iNumRun
                  , active->iMaxTicksInRun
                  , Oscl_Int64_Utils::get_int64_upper32(avgTicksPerRun)
                  , Oscl_Int64_Utils::get_int64_lower32(avgTicksPerRun)
                  , TICKSTR
                  , active->iNumCancel
                  , active->iNumRunError
                  , active->iLeave
                 ));
    }
    else
    {
        uint32 avgTicksPerRun = (active->iNumRun == 0) ? 0 : active->iTotalTicksInRun / active->iNumRun;
        LOGSTATS((0, "PVSCHED:Scheduler '%s', AO '%s': Pri %d NumRun %d, MaxTicksInRun %d, AvgTicksPerRun %d Units %s, NumCancel %d, NumError %d, LeaveCode %d"
                  , active->iScheduler->iName.Str()
                  , active->iAOName.get_cstr()
                  , active->iPriority
                  , active->iNumRun
                  , active->iMaxTicksInRun
                  , avgTicksPerRun
                  , TICKSTR
                  , active->iNumCancel
                  , active->iNumRunError
                  , active->iLeave
                 ));
    }
}

void OsclExecSchedulerCommonBase::ShowSummaryStats(PVActiveStats *active, PVLogger*logger, int64 total, int64& aGrandTotal, float& aTotalPercent)
//static routine to print stats for a PV AO.
{
    if (total == (int64)0)
        return;//to avoid divide by zero

    if (!active)
        return;

    //don't print any AO's that never ran.
    if ((active->iNumRun + active->iNumCancel) == 0)
        return;

    //calculate percent of the total time that was spent in this AO.
    if (active->i64Valid)
    {
        active->iPercent = 100.0 * active->i64TotalTicksInRun / total;
        aGrandTotal += active->i64TotalTicksInRun;
    }
    else
    {
        active->iPercent = 100.0 * active->iTotalTicksInRun / total;
        aGrandTotal += active->iTotalTicksInRun;
    }
    aTotalPercent += active->iPercent;

    int32 fraction = (int32)active->iPercent;
    float decimal = active->iPercent - fraction;
    decimal *= 100.0;

    //print results
    if (active->i64Valid)
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO
                        , (0, "  TIME PERCENT %d.%02d, AO '%s', Pri %d NumRun %d, MaxTicksInRun 0x%x, TotalTicksInRun Hi,Lo (0x%x,0x%08x), NumCancel %d, NumError %d, LeaveCode %d, NumInstance %d"
                           , (int32)active->iPercent, (int32)decimal
                           , active->iAOName.get_cstr()
                           , active->iPriority
                           , active->iNumRun
                           , active->iMaxTicksInRun
                           , Oscl_Int64_Utils::get_int64_upper32(active->i64TotalTicksInRun)
                           , Oscl_Int64_Utils::get_int64_lower32(active->i64TotalTicksInRun)
                           , active->iNumCancel
                           , active->iNumRunError
                           , active->iLeave
                           , active->iNumInstances
                          ));
    }
    else
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO
                        , (0, "  TIME PERCENT %d.%02d, AO '%s', Pri %d NumRun %d, MaxTicksInRun 0x%x, TotalTicksInRun 0x%x, NumCancel %d, NumError %d, LeaveCode %d, NumInstance %d"
                           , (int32)active->iPercent, (int32)decimal
                           , active->iAOName.get_cstr()
                           , active->iPriority
                           , active->iNumRun
                           , active->iMaxTicksInRun
                           , (int32)active->iTotalTicksInRun
                           , active->iNumCancel
                           , active->iNumRunError
                           , active->iLeave
                           , active->iNumInstances
                          ));
    }
}

void OsclExecSchedulerCommonBase::BeginStats()
//Begin stats for all AOs.
{
    iTotalTicksTemp = (uint8*)OSCL_MALLOC(sizeof(PVTICK));
    SET_TICK(*((PVTICK*)iTotalTicksTemp));
}

void OsclExecSchedulerCommonBase::EndStats()
//End stats for all AOs.
{
    //get the end time for the scheduler run.
    int64 total;
    DIFF_TICK((*((PVTICK*)iTotalTicksTemp)), total);
    OSCL_FREE(iTotalTicksTemp);

    //there may be multiple entries per AO in the stats table, so combine them now.
    if (!iPVStatQ.IsEmpty())
    {
        OsclDoubleRunner<PVActiveStats> iter(iPVStatQ);
        PVActiveStats *item;
        for (iter.SetToHead(); ; iter++)
        {
            item = iter;
            //find all subsequent entries in the list that have
            //the same AO name as this entry.
            if (item->iNumInstances > 0
                    && !iPVStatQ.IsTail(item))
            {
                OsclDoubleRunner<PVActiveStats> iter2(iPVStatQ);
                PVActiveStats* item2;
                for (iter2 = iter, iter2++; ; iter2++)
                {
                    item2 = iter2;
                    if (item2->iAOName == item->iAOName)
                    {
                        item->Combine(*item2);
                        //mark this entry to ignore in further processing.
                        item2->iNumInstances = 0;
                    }
                    if (iPVStatQ.IsTail(item2))
                        break;
                }
            }
            if (iPVStatQ.IsTail(item))
                break;
        }
    }
    //end of multiple-instance combine.

    QUE_ITER_BEGIN(PVActiveStats, iPVStatQ)
    {
        if (item
                && item->iNumInstances > 0)
            OsclExecScheduler::ShowStats(item);
    }
    QUE_ITER_END(iPVStatQ)

    //Show summary stats

    PVLogger* logger = PVLogger::GetLoggerObject("OsclSchedulerPerfStats");

    PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO,
                    (0, "OSCL SCHEDULER SUMMARY STATISTICS FOR SCHEDULER '%s'", iName.Str())
                   )

    iGrandTotalTicks = 0;
    iTotalPercent = 0.0;
    QUE_ITER_BEGIN(PVActiveStats, iPVStatQ)
    {
        if (item
                && item->iNumInstances > 0)
            OsclExecScheduler::ShowSummaryStats(item, logger, total, iGrandTotalTicks, iTotalPercent);
    }
    QUE_ITER_END(iPVStatQ)

    //split total percent into whole & decimal parts.
    int32 fraction = (int32)iTotalPercent;
    float decimal = iTotalPercent - fraction;
    decimal *= 100.0;

    PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO,
                    (0, "  Total Time (hi,lo): (0x%x,0x%08x) Units: %s", Oscl_Int64_Utils::get_int64_upper32(total), Oscl_Int64_Utils::get_int64_lower32(total), TICKSTR)
                   )
    PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO,
                    (0, "  Total Time Accounted (hi,lo): (0x%x,0x%08x) Units: %s", Oscl_Int64_Utils::get_int64_upper32(iGrandTotalTicks), Oscl_Int64_Utils::get_int64_lower32(iGrandTotalTicks), TICKSTR)
                   )
    PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO,
                    (0, "  Total Percent Accounted: %d.%02d", (int32)iTotalPercent, (int32)decimal)
                   )

    PVTICK f;
    GET_TICKFREQ(f);
    PVTICK_INT tickint = TICK_INT(f);
    PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO,
                    (0, "  Tick Units: %s Frequency: %s", TICKSTR, TICK_INT_STR, TICK_INT_EXPR(tickint))
                   )

    PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO,
                    (0, "END OSCL SCHEDULER SUMMARY STATISTICS FOR SCHEDULER '%s'", iName.Str())
                   )

}
#endif //PV_SCHED_ENABLE_AO_STATS


void OsclExecSchedulerCommonBase::Error(int32 anError) const
//call this when any AO leaves and its error handler does not handle the error.
{
    LOGERROR((0, "PVSCHED:Scheduler '%s', Thread 0x%x: Error! Reason %d", iName.Str(), PVThreadContext::Id(), anError));
    fprintf(stderr, "PVSCHED:Scheduler '%s', Thread 0x%x: Error! Reason %d\n", iName.Str(), PVThreadContext::Id(), anError);

    //propagate the leave
    OsclError::Leave(anError);
}

OSCL_EXPORT_REF void OsclExecSchedulerCommonBase::StartScheduler(OsclSemaphore *aSignal)
//blocking call to start PV scheduler.
//Will leave if any AO leaves.
{

    BeginScheduling(true, false);//blocking, non-native
    if (aSignal)
        aSignal->Signal();


    LOGNOTICE((0, "PVSCHED:Scheduler '%s', Thread 0x%x: Starting PV Scheduling Loop", iName.Str(), PVThreadContext::Id()));

    int32 err;
    OSCL_TRY(err, BlockingLoopL(););

    LOGNOTICE((0, "PVSCHED:Scheduler '%s', Thread 0x%x: Exited PV Scheduling Loop", iName.Str(), PVThreadContext::Id()));

    EndScheduling();

    if (err)
        OsclError::Leave(err);

}

OSCL_EXPORT_REF void OsclExecSchedulerCommonBase::StartNativeScheduler()
//blocking call to start native scheduler.
//Will leave if any AO leaves.
{
    OsclError::Leave(OsclErrNotSupported);//native scheduler not supported.
}

//scheduler stopper request status codes.
#define STOPPER_REQUEST_STOP_NATIVE 0
#define STOPPER_REQUEST_STOP_PV 1
#define STOPPER_REQUEST_SUSPEND 2

OSCL_EXPORT_REF void OsclExecSchedulerCommonBase::StopScheduler()
//any thread can use this to stop the blocking scheduler.
{
    if (!IsInstalled())
        OsclError::Leave(OsclErrNotInstalled);

    if (!iBlockingMode)
        OsclError::Leave(OsclErrNotReady);
    if (!IsStarted())
        return ;

    if (iStopper->iStatus != OSCL_REQUEST_PENDING)
        OsclError::Leave(OsclErrNotReady);

    //in case scheduler is in the suspend loop...
    if (iDoSuspend || iSuspended)
        iResumeSem.Signal();

    if (iNativeMode)
        iStopper->PendComplete(STOPPER_REQUEST_STOP_NATIVE);
    else
        iStopper->PendComplete(STOPPER_REQUEST_STOP_PV);

}

OSCL_EXPORT_REF void OsclExecSchedulerCommonBase::SuspendScheduler()
//any thread can use this to suspend the blocking scheduler.
{
    if (!IsInstalled())
        OsclError::Leave(OsclErrNotInstalled);

    if (iNativeMode)
        OsclError::Leave(OsclErrNotSupported);

    if (!iBlockingMode)
        OsclError::Leave(OsclErrNotSupported);

    if (!IsStarted())
        return;

    if (iStopper->iStatus != OSCL_REQUEST_PENDING)
        OsclError::Leave(OsclErrNotReady);

    iStopper->PendComplete(STOPPER_REQUEST_SUSPEND);

}

OSCL_EXPORT_REF void OsclExecSchedulerCommonBase::ResumeScheduler()
//any thread can use this to resume the blocking scheduler.
{
    if (!IsInstalled())
        OsclError::Leave(OsclErrNotInstalled);

    if (iDoSuspend || iSuspended)
    {
        iResumeSem.Signal();
        return ;
    }
    else
        OsclError::Leave(OsclErrNotReady); //not suspended.
}

OSCL_EXPORT_REF void OsclExecScheduler::RunSchedulerNonBlocking(int32 aCount, int32 &aReady, uint32 &aShortestDelay)
//run scheduler in non-blocking mode.
//Will leave if any AO leaves.
{


    aReady = 0;
    aShortestDelay = 0;

    //make sure this scheduler is installed.
    if (!IsInstalled())
        OsclError::Leave(OsclErrNotInstalled);

#if !(OSCL_RELEASE_BUILD)
    //make sure this scheduler is really installed in this
    //thread.
    if (GetScheduler() != this)
        OsclError::Leave(OsclErrNotInstalled);
#endif

    //start scheduling if needed.
    if (!IsStarted())
    {
        BeginScheduling(false, false);//nonblocking, non-native
    }
    else if (iBlockingMode || iNativeMode)
        OsclError::Leave(OsclErrInvalidState);//not stopped

    //Process timers.  All ready timers will get
    //moved to the ready queue.
    UpdateTimersMsec(aShortestDelay);

    //Run until the requested count is reached, or there
    //aren't any AOs ready.
    for (int32 count = 0; count < aCount;)
    {
        //find highest pri ready AO.
        PVActiveBase* pvactive = iReadyQ.PopTop();
        if (pvactive)
        {
            //run it
            count++;
            CallRunExec(pvactive);

            //re-evaluate timers
            UpdateTimersMsec(aShortestDelay);
        }
        else
            break;//nothing ready
    }

    //at this point, either nothing else is ready or the target count was reached.

    aReady = iReadyQ.Depth();

}

OSCL_EXPORT_REF void OsclExecScheduler::RegisterForCallback(OsclSchedulerObserver* aCallback, OsclAny* aCallbackContext)
{
    //Update the callback pointers.
    iReadyQ.RegisterForCallback(aCallback, aCallbackContext);
}

////////////////////////////////////////
// Queue Management
////////////////////////////////////////


PVActiveBase * OsclExecSchedulerCommonBase::FindPVBase(PVActiveBase *active, OsclDoubleList<PVActiveBase> &q)
//Search a PVActiveBase queue, given a PVActiveBase ptr.
{
    QUE_ITER_BEGIN(PVActiveBase, q)
    {
        if (item == active)
            return item;
    }
    QUE_ITER_END(q)
    return NULL;
}

void OsclExecSchedulerCommonBase::CleanupExecQ()
{
    //Cleanup timers.
    {
        PVActiveBase *top;
        while ((top = iExecTimerQ.PopTop()))
            top->RemoveFromScheduler();
    }
    //Cleanup ready AOs.
    PVActiveBase* top = iReadyQ.Top();
    while (top)
    {
        top->RemoveFromScheduler();
        top = iReadyQ.Top();
    }
}

void OsclExecSchedulerCommonBase::InitExecQ(int nreserve)
//init the pvactive queues.
{
    iExecTimerQ.Construct(nreserve);
    iReadyQ.Construct(nreserve);
}

////////////////////////////////////////
// Non-Symbian queue management
////////////////////////////////////////

void OsclExecSchedulerCommonBase::AddToExecTimerQ(PVActiveBase* anActive, uint32 aDelayMicrosec)
//timer implementation.
//Add an AO to the pending timer queue.
{
    OSCL_ASSERT(anActive);//EExecNull

    //make sure this AO is not already added.
    if (anActive->IsInAnyQ())
        OsclError::Leave(OsclErrInvalidState);//EExecAlreadyAdded

    //Set time in ticks when AO should run.

    uint32 tickperiod = OsclTickCount::TickCountPeriod();
    OSCL_ASSERT(tickperiod != 0);

    //Round to the nearest integer with the computation:
    //floor((2*Interval_usec/ticks_per_usec +  1)/2)
    //
    //The computed time may rollover the 32-bit value-- that's OK, because
    //the tick count will also rollover.
    uint32 timenow = OsclTickCount::TickCount();
    anActive->iPVReadyQLink.iTimeToRunTicks = timenow + (aDelayMicrosec * 2 / tickperiod + 1) / 2;

    if (aDelayMicrosec > 0)
    {
        LOGPERF2((0, "PVSCHED:%s AO %s Timer delay %d TimeToRunTicks %d Timenow %d"
                  , iLogPerfIndentStr, anActive->iName.Str()
                  , aDelayMicrosec, anActive->iPVReadyQLink.iTimeToRunTicks
                  , timenow));
    }

    //queue it
    iExecTimerQ.Add(anActive);

    //if this AO is in the front of the queue now, we need to do a
    //callback, because the shortest delay interval has changed.
    if (iReadyQ.Callback()
            && anActive == iExecTimerQ.Top())
    {
        iReadyQ.TimerCallback(aDelayMicrosec);
    }
}

//For 32-bit time comparisons with rollover handling.
//This value is (2^31)-1
const uint32 OsclExecSchedulerCommonBase::iTimeCompareThreshold = 0x7fffffff;

PVActiveBase* OsclExecSchedulerCommonBase::UpdateTimers(uint32 &aShortestDelay)
//timer processing.
//Complete requests for all timers that are ready now,
//then return the pending timer with the shortest delay if any.
//If any pending timer is returned it's the top of the queue so
//it can be discarded later with Pop.
{
    aShortestDelay = 0;

    PVActiveBase *top = iExecTimerQ.Top();

    if (!top)
        return NULL;

    uint32 timenow = OsclTickCount::TickCount();

    //Find all timers that are ready, and the first
    //timer that isn't.  The list is sorted by
    //time then priority.
    for (; top; top = iExecTimerQ.Top())
    {
        //calculate time to run <= timenow, taking possible rollover into account
        uint32 deltaTicks = timenow - top->iPVReadyQLink.iTimeToRunTicks;
        if (deltaTicks <= iTimeCompareThreshold)
        {
            //this timer is ready
            iExecTimerQ.Pop(top);

            PendComplete(top, OSCL_REQUEST_ERR_NONE, EPVThreadContext_InThread);
        }
        else
        {
            //we found the pending timer with the shortest delay.
            //get the delay value
            int32 delayTicks = deltaTicks;
            if (delayTicks < 0)
                delayTicks = (-delayTicks);
            aShortestDelay = delayTicks;
            return top;
        }
    }

    return NULL;//no pending timers.
}

PVActiveBase* OsclExecSchedulerCommonBase::UpdateTimersMsec(uint32 &aShortestDelay)
//Identical to UpdateTimers except the delay returned is milliseconds instead
//of ticks.
{
    aShortestDelay = 0;

    PVActiveBase *top = iExecTimerQ.Top();

    if (!top)
        return NULL;

    uint32 timenow = OsclTickCount::TickCount();

    //Find all timers that are ready, and the first
    //timer that isn't.  The list is sorted by
    //time then priority.
    for (; top; top = iExecTimerQ.Top())
    {
        //calculate time to run <= timenow, taking possible rollover into account
        uint32 deltaTicks = timenow - top->iPVReadyQLink.iTimeToRunTicks;
        if (deltaTicks <= iTimeCompareThreshold)
        {
            //this timer is ready
            iExecTimerQ.Pop(top);

            PendComplete(top, OSCL_REQUEST_ERR_NONE, EPVThreadContext_InThread);
        }
        else
        {
            //we found the pending timer with the shortest delay.
            //get the delay value
            int32 delayTicks = deltaTicks;
            if (delayTicks < 0)
                delayTicks = (-delayTicks);
            aShortestDelay = OsclTickCount::TicksToMsec(delayTicks);

            //if delay became zero after the conversion from ticks to msec,
            //then just consider this timer to be ready now.
            if (aShortestDelay == 0)
            {
                //this timer is ready
                iExecTimerQ.Pop(top);

                PendComplete(top, OSCL_REQUEST_ERR_NONE, EPVThreadContext_InThread);
            }
            else
            {
                return top;
            }
        }
    }

    return NULL;//no pending timers.
}

void OsclExecSchedulerCommonBase::PendComplete(PVActiveBase *pvbase, int32 aReason, TPVThreadContext aThreadContext)
//complete a request for this scheduler.
//Calling context can be any thread.
{
    //During timer cancellation, the AO may still be in the ExecTimerQ.
    //Remove it now, since it won't get removed by the scheduler loop.
    //Check thread context first, to accessing timer queue from out-of-thread.
    if (aThreadContext == EPVThreadContext_InThread)
    {
        LOGPERF2((0, "PVSCHED: %s AO %s Request complete", iLogPerfIndentStr, pvbase->iName.Str()));

        if (iExecTimerQ.IsIn(pvbase))
            iExecTimerQ.Remove(pvbase);
    }

    //Pass this to the ReadyQ so it can do appropriate queue locks
    int32 err = iReadyQ.PendComplete(pvbase, aReason);

    OsclError::LeaveIfError(err);
}

void OsclExecSchedulerCommonBase::RequestCanceled(PVActiveBase* pvbase)
{
    LOGPERF2((0, "PVSCHED: %s AO %s Request canceled", iLogPerfIndentStr, pvbase->iName.Str()));

    //This gets called right after the AO's DoCancel was
    //called.  It must wait on the request to cancel, and remove it
    //from the ready Q.

    //Calling context is always in-thread, since Cancel context is always
    //in-thread.

    //See if the request was completed by the DoCancel.
    bool complete = iReadyQ.IsIn(pvbase);

    DECLARE_LOOP_STATS;

    if (!complete)
    {
        //If request is still pending after DoCancel is called, it
        //means some other thread will complete the request cancellation.
        //If the AO does not have a proper DoCancel, this wait will hang up.

        //reset the perf indent when scheduler gives up CPU...
        RESET_LOG_PERF((0, "PVSCHED: Waiting on cancel... AO '%s'", pvbase->iName.Str()));

#if (PV_SCHED_ENABLE_PERF_LOGGING)
        uint32 ticks = OsclTickCount::TickCount();
#endif
        START_LOOP_STATS(iOtherExecStats[EOtherExecStats_WaitTime]);
        int32 err = iReadyQ.WaitForRequestComplete(pvbase);
        END_LOOP_STATS(iOtherExecStats[EOtherExecStats_WaitTime]);

#if (PV_SCHED_ENABLE_PERF_LOGGING)
        LOGPERF((0, "PVSCHED: ...Cancel took %d Ticks", OsclTickCount::TickCount() - ticks));
#endif

        OsclError::LeaveIfError(err);
    }

    //Set request idle and remove from ready Q.
    //The AO will not run
    pvbase->iBusy = false;

    START_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);
    iReadyQ.Remove(pvbase);
    END_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);
}




////////////////////////////////////////
// PV Scheduling Loop Implementation
////////////////////////////////////////


void OsclExecSchedulerCommonBase::CallRunExec(PVActiveBase *pvactive)
//Run a PV AO.
{
    //Set this AO inactive.  This AO may be from the
    //ready Q or the pending Timer Q.
    //The dequeing is done by the caller.
    pvactive->iBusy = false;

    //start stats
#if (PV_SCHED_ENABLE_AO_STATS)
    iPVStats = pvactive->iPVActiveStats;//save value now since it may change in the Run call.
    iDelta = 0;
    INIT_TICK(iTime);
#endif

    SET_TICK(iTime);

    //Call the Run under a trap harness.
    //Pass the ErrorTrapImp pointer to reduce overhead of the Try call.
    //We already did a null ptr check on iErrorTrapImp so it's safe to de-ref here.
    int32 err;
    OSCL_TRY_NO_TLS(iErrorTrapImp, err, pvactive->Run(););

    //end stats
    DIFF_TICK(iTime, iDelta);
    UPDATE_RUNL_TIME(iPVStats, iDelta);
    UPDATE_LEAVE_CODE(iPVStats, err);

#if(PV_SCHED_ENABLE_PERF_LOGGING)
    //show AO time.
    IncLogPerf(iDelta);
    LOGPERF((0, "PVSCHED: %s Run %d %s AO %s", iLogPerfIndentStr, (int32)iDelta, TICKSTR, pvactive->iName.Str()));
#endif

    //check for a leave in the Run...
    if (err != OsclErrNone)
    {
        //start stats
        SET_TICK(iTime);

        //call the AO error handler
        err = pvactive->RunError(err);

        //end stats
        DIFF_TICK(iTime, iDelta);
        UPDATE_RUNERROR_TIME(iPVStats, iDelta);

        //If the AO did not handle the error, indicated by returning
        //ErrNone, then call the scheduler error handler.
        if (err != OsclErrNone)
        {
            LOGERROR((0, "PVSCHED:Scheduler '%s', Thread 0x%x: Error! AO %s Error %d not handled"
                      , iName.Str(), PVThreadContext::Id()
                      , pvactive->iName.Str(), err));
            fprintf(stderr, "PVSCHED:Scheduler '%s', Thread 0x%x: Error! AO %s Error %d not handled\n"
                    , iName.Str(), PVThreadContext::Id()
                    , pvactive->iName.Str(), err);

            Error(err);
        }
    }

}


void OsclExecSchedulerCommonBase::BlockingLoopL()
//Blocking scheduling loop.
//Will leave if any AO leaves.
{

    PVActiveBase* pvactive;


    while (!iDoStop)
    {
        //Get the next AO to run.  This call may block until an AO is ready.
        pvactive = WaitForReadyAO();

        if (pvactive)
        {
            CallRunExec(pvactive);
        }
        else
        {
            OsclError::Leave(OsclErrCorrupt);//EExecStrayEvent
        }

        //check for a suspend signal..
        if (iDoSuspend)
        {
            iSuspended = true;
            iDoSuspend = false;
            iResumeSem.Wait();
            iSuspended = false;
        }

    }//while !dostop

    iDoStop = false;
}


PVActiveBase* OsclExecSchedulerCommonBase::WaitForReadyAO()
//Find the next AO to run-- non-Symbian version.
{
    DECLARE_LOOP_STATS;

    //First process timers.
    //All ready timers will get moved to the run Q.
    uint32 waitTicks;
    START_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);
    PVActiveBase* pvtimer = UpdateTimers(waitTicks);
    END_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);

    //Check for a ready AO.
    START_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);
    PVActiveBase* pvactive = iReadyQ.PopTop();
    END_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);

    if (pvactive)
    {
        //An AO is ready.
        return pvactive;
    }
    else if (pvtimer)
    {
        //No AO is ready, but at least one timer is pending.
        //Wait on shortest timer expiration or a new request.

        //reset the perf logging indent each time scheduler gives up CPU.
        RESET_LOG_PERF((0, "PVSCHED: Waiting on timer... Ticks %d AO %s", waitTicks, pvtimer->iName.Str()));

        START_LOOP_STATS(iOtherExecStats[EOtherExecStats_WaitTime]);
        pvactive = iReadyQ.WaitAndPopTop(OsclTickCount::TicksToMsec(waitTicks));
        END_LOOP_STATS(iOtherExecStats[EOtherExecStats_WaitTime]);

        if (pvactive)
        {
            //Another AO's request completed while we were waiting.
            //Run that one instead of the timer.
            return pvactive;
        }
        else
        {
            //It's time to complete this timer's request and run it.
            //To save overhead, don't move it to the ReadyQ, just pop it
            //from its current location on the top of the timer queue.

            pvtimer->iStatus = OSCL_REQUEST_ERR_NONE;

            START_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);
            iExecTimerQ.Pop(pvtimer);
            END_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);

            return pvtimer;
        }
    }
    else
    {
        //Nothing is ready and no timer is pending.
        //Wait on a request to be completed by another thread.

        //reset the perf logging indent each time scheduler gives up CPU.
        RESET_LOG_PERF((0, "PVSCHED: Waiting on any request..."));

        START_LOOP_STATS(iOtherExecStats[EOtherExecStats_WaitTime]);
        pvactive = iReadyQ.WaitAndPopTop();
        END_LOOP_STATS(iOtherExecStats[EOtherExecStats_WaitTime]);

        return pvactive;
    }
}

////////////////////////////////////////
// PVSchedulerStopper
////////////////////////////////////////
PVSchedulerStopper::PVSchedulerStopper()
        : OsclActiveObject((int32)OsclActiveObject::EPriorityHighest, "Stopper")
{
}

PVSchedulerStopper::~PVSchedulerStopper()
{
}

void PVSchedulerStopper::Run()
//This AO just waits for a signal to suspend or stop the scheduler.
{
    //Stop
    switch (Status())
    {
        case STOPPER_REQUEST_STOP_NATIVE:
            break;
        case STOPPER_REQUEST_STOP_PV:
        {
            //stop my scheduling loop
            OsclExecSchedulerCommonBase* myscheduler = OsclExecScheduler::GetScheduler();
            if (myscheduler)
                myscheduler->iDoStop = true;
        }
        break;
        case STOPPER_REQUEST_SUSPEND:
        {
            //suspend my scheduling loop
            OsclExecSchedulerCommonBase* myscheduler = OsclExecScheduler::GetScheduler();
            if (myscheduler)
                myscheduler->iDoSuspend = true;

            //re-schedule ourself
            PendForExec();
        }
        break;
        default:
            break;
    }
}

////////////////////////////////////////
// Symbian Coe Scheduler
////////////////////////////////////////




