| /* ------------------------------------------------------------------ |
| * 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. |
| * ------------------------------------------------------------------- |
| */ |
| |
| |
| #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; |
| sched->~OsclExecSchedulerCommonBase(); |
| 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 |
| |
| OsclMemAllocator OsclExecSchedulerCommonBase::iDefAlloc; |
| |
| #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) && !defined(NDEBUG) |
| //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 |
| //////////////////////////////////////// |
| |
| |
| |
| |