• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* ------------------------------------------------------------------
2  * Copyright (C) 1998-2009 PacketVideo
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
13  * express or implied.
14  * See the License for the specific language governing permissions
15  * and limitations under the License.
16  * -------------------------------------------------------------------
17  */
18 
19 
20 #include "oscl_scheduler.h"
21 
22 
23 #include "oscl_error.h"
24 #include "oscl_tickcount.h"
25 #include "pvlogger.h"
26 #include "oscl_error_trapcleanup.h"
27 #include "pvlogger.h"
28 #include "oscl_tls.h"
29 #include "oscl_int64_utils.h"
30 
31 #define OSCL_DISABLE_WARNING_CONDITIONAL_IS_CONSTANT
32 #include "osclconfig_compiler_warnings.h"
33 
34 
35 #include "oscl_scheduler_tuneables.h"
36 
37 
38 /////////////////////////////////////
39 // Logger Macros
40 /////////////////////////////////////
41 
42 //LOGERROR is for scheduler errors.
43 //This logging also goes to stderr on platforms with ANSI stdio.
44 #define LOGERROR(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_REL,iLogger,PVLOGMSG_ERR,m);
45 
46 //LOGNOTICE is for scheduler start/stop, install/uninstall notices.
47 #define LOGNOTICE(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG,iLogger,PVLOGMSG_NOTICE,m);
48 
49 #if(PV_SCHED_ENABLE_PERF_LOGGING)
50 //LOGSTATS is for logging the AO summary statistics.  These are loggged either
51 //when the AO is deleted, or when the scheduling ends.
52 #define LOGSTATS(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF,iLogger,PVLOGMSG_INFO,m);
53 //LOGPERF is for detailed performance logging.
54 #define LOGPERF_LEVEL PVLOGMSG_INFO
55 #define LOGPERF(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF,iLogger,LOGPERF_LEVEL,m);
56 //LOGPERF2 is for highest detail on scheduler activity.
57 #define LOGPERF2_LEVEL PVLOGMSG_INFO+1
58 #define LOGPERF2(m) PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF,iLogger,LOGPERF2_LEVEL,m);
59 //max perf log string length
60 #define LOGPERFMAXSTR 64
61 #define RESET_LOG_PERF(m)\
62     ResetLogPerf();\
63     LOGPERF(m);
64 #else
65 #define LOGSTATS(m)
66 #define LOGPERF(m)
67 #define LOGPERF2(m)
68 #define LOGPERFMAXSTR 64
69 #define RESET_LOG_PERF(m)
70 #endif//(PV_SCHED_ENABLE_PERF_LOGGING)
71 
72 /////////////////////////////////////
73 // DLL Entry
74 /////////////////////////////////////
75 #ifndef OSCL_COMBINED_DLL
76 #include "oscl_dll.h"
OSCL_DLL_ENTRY_POINT_DEFAULT()77 OSCL_DLL_ENTRY_POINT_DEFAULT()
78 #endif
79 
80 /////////////////////////////////////
81 // OsclScheduler
82 /////////////////////////////////////
83 
84 OSCL_EXPORT_REF void OsclScheduler::Init(const char *name, Oscl_DefAlloc *alloc, int nreserve)
85 //Init the scheduler for this thread.
86 {
87     int32 err;
88     OSCL_TRY(err,
89              OsclExecScheduler *sched = OsclExecScheduler::NewL(name, alloc, nreserve);
90              sched->InstallScheduler(););
91     if (err != OsclErrNone)
92         OsclError::Leave(OsclErrNotInstalled);
93 }
94 
95 
96 
Cleanup()97 OSCL_EXPORT_REF void OsclScheduler::Cleanup()
98 //Cleanup the scheduler for this thread.
99 {
100     OsclExecSchedulerCommonBase *sched = OsclExecSchedulerCommonBase::GetScheduler();
101     if (!sched)
102         OsclError::Leave(OsclErrNotInstalled);
103     sched->UninstallScheduler();
104     Oscl_DefAlloc *alloc = sched->iAlloc;
105     sched->~OsclExecSchedulerCommonBase();
106     alloc->deallocate(sched);
107 }
108 
109 /////////////////////////////////////
110 // OsclExecSchedulerCommonBase
111 /////////////////////////////////////
112 
113 /////////////////////////////////////
114 // OsclExecScheduler
115 /////////////////////////////////////
116 
117 //use the TLS registry, or the singleton registry if no TLS.
118 //Note: singleton registry only works for single-threaded
119 //scenarios, since this implementation assumes a per-thread registry.
120 #include "oscl_error.h"
121 #define PVSCHEDULER_REGISTRY OsclTLSRegistryEx
122 #define PVSCHEDULER_REGISTRY_ID OSCL_TLS_ID_PVSCHEDULER
123 
124 
125 /////////////////////////////////////
126 //For AO statistics.
127 /////////////////////////////////////
128 
129 #if !(PV_SCHED_ENABLE_AO_STATS)
130 //no stats
131 #define PVTICK uint32
132 #define PVTICK_INT uint32
133 #define INIT_TICK(tick)
134 #define SET_TICK(tick)
135 #define GET_TICKFREQ(tick)
136 #define TICK_INT(tick1)
137 #define TICKSTR ""
138 #define TICK_INT_STR ""
139 #define TICK_INT_EXPR(x)
140 #else
141 //else use the oscl timer.
142 #define PVTICK uint32
143 #define PVTICK_INT uint32
144 #define INIT_TICK(tick) tick=0
145 #define SET_TICK(tick) tick=OsclTickCount::TickCount()
146 #define GET_TICKFREQ(tick) tick=OsclTickCount::TickCountFrequency()
147 #define TICK_INT(tick1) tick1
148 #define TICKSTR "Ticks"
149 #define TICK_INT_STR "%u"
150 #define TICK_INT_EXPR(x) x
151 #endif
152 
153 #if !(PV_SCHED_ENABLE_AO_STATS)
154 #define DIFF_TICK(tick1,diff)
155 #define UPDATE_RUNERROR_TIME(x,y)
156 #define UPDATE_RUNL_TIME(x,y)
157 #define UPDATE_LEAVE_CODE(x,err)
158 #else
159 #define DIFF_TICK(tick1,diff) PVTICK _now;SET_TICK(_now);diff=TICK_INT(_now)-TICK_INT(tick1)
160 #define UPDATE_RUNERROR_TIME(stats,delta)\
161         if(stats->i64Valid) stats->i64TotalTicksInRun+=delta;\
162         else stats->iTotalTicksInRun+=delta;\
163         if(delta>stats->iMaxTicksInRun) stats->iMaxTicksInRun=delta;\
164         stats->iNumRunError++
165 #define UPDATE_RUNL_TIME(stats,delta)\
166         if(stats->i64Valid) stats->i64TotalTicksInRun+=delta;\
167         else stats->iTotalTicksInRun+=delta;\
168         if(delta>stats->iMaxTicksInRun) stats->iMaxTicksInRun=delta;\
169         stats->iNumRun++;
170 #define UPDATE_LEAVE_CODE(stats,err)if (err!=OsclErrNone)stats->iLeave=err
171 #endif
172 
173 #if (PV_SCHED_ENABLE_LOOP_STATS)
174 #define DECLARE_LOOP_STATS int64 loopdelta = 0; PVTICK looptime;
175 
176 #define START_LOOP_STATS(stats)SET_TICK(looptime);
177 
178 #define START_WAIT_LOOP_STATS(rc,stats)\
179         if (rc<1)\
180             SET_TICK(looptime);
181 
182 #define END_LOOP_STATS(stats)\
183         {\
184             DIFF_TICK(looptime,loopdelta);\
185             if(stats->i64Valid) stats->i64TotalTicksInRun+=loopdelta;\
186             else stats->iTotalTicksInRun+=Oscl_Int64_Utils::get_int64_lower32(loopdelta);\
187             stats->iNumRun++;\
188             LOGPERF((0,"PVSCHED: Run %d %s AO %s",(int32)loopdelta,TICKSTR,stats->iAOName.get_cstr()));\
189         }
190 
191 #define END_WAIT_LOOP_STATS(rc,stats)\
192         if (rc<1)\
193         {\
194             DIFF_TICK(looptime,loopdelta);\
195             if(stats->i64Valid) stats->i64TotalTicksInRun+=loopdelta;\
196             else stats->iTotalTicksInRun+=Oscl_Int64_Utils::get_int64_lower32(loopdelta);\
197             stats->iNumRun++;\
198             LOGPERF((0,"PVSCHED: Run %d %s AO %s",(int32)loopdelta,TICKSTR,stats->iAOName.get_cstr()));\
199         }
200 
201 #else
202 #define DECLARE_LOOP_STATS
203 #define START_LOOP_STATS(stats)
204 #define START_WAIT_LOOP_STATS(rc,stats)
205 #define END_LOOP_STATS(stats)
206 #define END_WAIT_LOOP_STATS(rc,stats)
207 
208 #endif
209 
210 OsclMemAllocator OsclExecSchedulerCommonBase::iDefAlloc;
211 
212 #if(PV_SCHED_ENABLE_PERF_LOGGING)
ResetLogPerf()213 void OsclExecSchedulerCommonBase::ResetLogPerf()
214 {
215     //print total time spend in prior interval of continuous Run calls.
216     if (iLogPerfTotal > 0)
217     {
218         LOGPERF((0, "PVSCHED: Prior Interval %d %s", iLogPerfTotal, TICKSTR));
219     }
220     //reset interval.
221     iLogPerfTotal = 0;
222     //reset indentation to zero.
223     iLogPerfIndentStrLen = 0;
224     if (iLogPerfIndentStr)
225         iLogPerfIndentStr[iLogPerfIndentStrLen] = '\0';
226 }
227 
IncLogPerf(uint32 delta)228 void OsclExecSchedulerCommonBase::IncLogPerf(uint32 delta)
229 {
230     //add to total interval time.
231     iLogPerfTotal += delta;
232     //add a space to the indent string up to the max.
233     if (iLogPerfIndentStr
234             && iLogPerfIndentStrLen < LOGPERFMAXSTR)
235     {
236         iLogPerfIndentStr[iLogPerfIndentStrLen++] = ' ';
237         iLogPerfIndentStr[iLogPerfIndentStrLen] = '\0';
238     }
239 }
240 #endif//(PV_SCHED_ENABLE_PERF_LOGGING)
241 
GetScheduler()242 OsclExecSchedulerCommonBase* OsclExecSchedulerCommonBase::GetScheduler()
243 //static function to get currently installed scheduler
244 //for this thread.
245 {
246     OsclExecSchedulerCommonBase *current = (OsclExecSchedulerCommonBase*)PVSCHEDULER_REGISTRY::getInstance(PVSCHEDULER_REGISTRY_ID);
247     return current;
248 }
249 
SetScheduler(OsclExecSchedulerCommonBase * a)250 OsclExecSchedulerCommonBase* OsclExecSchedulerCommonBase::SetScheduler(OsclExecSchedulerCommonBase *a)
251 //static function to set currently installed scheduler
252 //for this thread. return previous scheduler, if any.
253 {
254     OsclExecSchedulerCommonBase* temp = GetScheduler();
255     PVSCHEDULER_REGISTRY::registerInstance(a, PVSCHEDULER_REGISTRY_ID);
256     return temp;
257 }
258 
GetName()259 OSCL_EXPORT_REF OsclNameString<PVSCHEDNAMELEN> *OsclExecSchedulerCommonBase::GetName()
260 //static function to get scheduler name for this thread.
261 {
262     OsclExecSchedulerCommonBase *sched = GetScheduler();
263     if (sched)
264         return &sched->iName;
265     else
266         return NULL;
267 }
268 
GetId()269 OSCL_EXPORT_REF uint32 OsclExecSchedulerCommonBase::GetId()
270 {
271     return PVThreadContext::Id();
272 }
273 
NewL(const char * name,Oscl_DefAlloc * alloc,int nreserve)274 OsclExecScheduler * OsclExecScheduler::NewL(const char *name, Oscl_DefAlloc *alloc, int nreserve)
275 {
276     OsclExecScheduler *self;
277     OsclMemAllocator defalloc;
278     OsclAny* ptr = (alloc) ? alloc->ALLOCATE(sizeof(OsclExecScheduler))
279                    : defalloc.ALLOCATE(sizeof(OsclExecScheduler));
280     OsclError::LeaveIfNull(ptr);
281     self = OSCL_PLACEMENT_NEW(ptr, OsclExecScheduler(alloc));
282     OsclError::PushL(self);
283     self->ConstructL(name, nreserve);
284     OsclError::Pop();
285     return self;
286 }
287 
~OsclExecSchedulerCommonBase()288 OsclExecSchedulerCommonBase::~OsclExecSchedulerCommonBase()
289 {
290     //make sure scheduler is not currently installed in
291     //any thread.
292     if (IsInstalled())
293         OsclError::Leave(OsclErrInvalidState);//scheduler not stopped
294 
295     if (iStopper)
296     {
297         iStopper->~PVSchedulerStopper();
298         iAlloc->deallocate(iStopper);
299     }
300 #if(PV_SCHED_ENABLE_PERF_LOGGING)
301     if (iLogPerfIndentStr)
302         _oscl_free(iLogPerfIndentStr);
303 #endif
304 }
305 
~OsclExecScheduler()306 OsclExecScheduler::~OsclExecScheduler()
307 {
308 }
309 
OsclExecSchedulerCommonBase(Oscl_DefAlloc * alloc)310 OsclExecSchedulerCommonBase::OsclExecSchedulerCommonBase(Oscl_DefAlloc *alloc)
311 {
312     iAlloc = (alloc) ? alloc : &iDefAlloc;
313 #if(PV_SCHED_ENABLE_PERF_LOGGING)
314     iLogPerfIndentStr = NULL;
315     iLogPerfTotal = 0;
316 #endif
317 }
318 
OsclExecScheduler(Oscl_DefAlloc * alloc)319 OsclExecScheduler::OsclExecScheduler(Oscl_DefAlloc *alloc)
320         : OsclExecSchedulerCommonBase(alloc)
321 {
322 }
323 
ConstructL(const char * name,int nreserve)324 void OsclExecSchedulerCommonBase::ConstructL(const char *name, int nreserve)
325 {
326     iNumAOAdded = 1;
327 
328     OsclAny* ptr = iAlloc->ALLOCATE(sizeof(PVSchedulerStopper));
329     OsclError::LeaveIfNull(ptr);
330     iStopper = new(ptr) PVSchedulerStopper;
331 
332 #if(PV_SCHED_ENABLE_AO_STATS)
333     ConstructStatQ();
334 #endif
335 
336     InitExecQ(nreserve);
337 
338     iBlockingMode = false;
339     iNativeMode = false;
340     iName.Set(name);
341     iLogger = PVLogger::GetLoggerObject("pvscheduler");
342 
343 #if (PV_SCHED_ENABLE_PERF_LOGGING)
344     iLogPerfIndentStr = (char*)_oscl_malloc(LOGPERFMAXSTR + 1);
345     OsclError::LeaveIfNull(iLogPerfIndentStr);
346     ResetLogPerf();
347 #endif
348 }
349 
ConstructL(const char * name,int nreserve)350 void OsclExecScheduler::ConstructL(const char *name, int nreserve)
351 {
352     OsclExecSchedulerCommonBase::ConstructL(name, nreserve);
353 }
354 
InstallScheduler()355 void OsclExecSchedulerCommonBase::InstallScheduler()
356 {
357     //make sure this scheduler is not installed in
358     //any thread.
359     if (IsInstalled())
360         OsclError::Leave(OsclErrAlreadyInstalled);
361     //make sure no scheduler is installed in this thread.
362     if (GetScheduler())
363         OsclError::Leave(OsclErrAlreadyInstalled);
364 
365     SetScheduler(this);
366 
367     iThreadContext.EnterThreadContext();
368 
369     iErrorTrapImp = OsclErrorTrap::GetErrorTrapImp();
370     if (!iErrorTrapImp)
371         OsclError::Leave(OsclErrNotInstalled);//error trap not installed.
372 
373     if (iStopperCrit.Create() != OsclProcStatus::SUCCESS_ERROR)
374         OsclError::Leave(OsclErrSystemCallFailed);//mutex error
375 
376     iResumeSem.Create();
377     iDoStop = iDoSuspend = iSuspended = false;
378 
379     iReadyQ.ThreadLogon();
380 
381     LOGNOTICE((0, "PVSCHED:Scheduler '%s', Thread 0x%x: Installed", iName.Str(), PVThreadContext::Id()));
382 
383 #if (PV_SCHED_ENABLE_PERF_LOGGING)
384     //print tick frequencies that will show up in the perf log.
385     PVTICK f;
386     GET_TICKFREQ(f);
387     PVTICK_INT tickint = TICK_INT(f);
388     LOGPERF((0, "PVSCHED: %s frequency %s", TICKSTR, TICK_INT_STR, TICK_INT_EXPR(tickint)));
389     OSCL_UNUSED_ARG(tickint);
390 #endif
391 }
392 
UninstallScheduler()393 void OsclExecSchedulerCommonBase::UninstallScheduler()
394 {
395     //make sure this scheduler is currently installed in
396     //this thread.
397     if (!IsInstalled() || GetScheduler() != this)
398         OsclError::Leave(OsclErrNotInstalled);
399 
400 
401     if (iBlockingMode)
402     {
403         //in case a thread error happened, go ahead and end
404         //scheduling.
405         OsclErrorTrapImp *trap = OsclErrorTrapImp::GetErrorTrap();
406         if (trap
407                 && trap->iLeave != OsclErrNone)
408             EndScheduling();
409         //make sure scheduler is stopped.  If not, leave instead.
410         if (IsStarted())
411             OsclError::Leave(OsclErrInvalidState);//scheduler not stopped
412     }
413     else if (IsStarted())
414     {
415         //end non-blocking scheduling
416         EndScheduling();
417     }
418 
419     SetScheduler(NULL);
420 
421     iThreadContext.ExitThreadContext();
422 
423     CleanupExecQ();
424 
425     //Cleanup the stat queue.
426 #if(PV_SCHED_ENABLE_AO_STATS)
427     CleanupStatQ();
428 #endif
429 
430     if (iStopperCrit.Close() != OsclProcStatus::SUCCESS_ERROR)
431         OsclError::Leave(OsclErrSystemCallFailed);//mutex error
432 
433     iReadyQ.ThreadLogoff();
434     iResumeSem.Close();
435 
436     LOGNOTICE((0, "PVSCHED:Scheduler '%s', Thread 0x%x: Uninstalled", iName.Str(), PVThreadContext::Id()));
437 }
438 
Current()439 OSCL_EXPORT_REF OsclExecScheduler* OsclExecScheduler::Current()
440 //static routine to get current scheduler.
441 {
442     return (OsclExecScheduler*)GetScheduler();
443 }
444 
IsStarted()445 bool OsclExecSchedulerCommonBase::IsStarted()
446 {
447     iStopperCrit.Lock();
448     bool val = (iStopper->IsAdded()) ? true : false;
449     iStopperCrit.Unlock();
450     return val;
451 }
452 
IsInstalled()453 inline bool OsclExecSchedulerCommonBase::IsInstalled()
454 {
455     return iThreadContext.iOpen;
456 }
457 
BeginScheduling(bool blocking,bool native)458 void OsclExecSchedulerCommonBase::BeginScheduling(bool blocking, bool native)
459 //called before entering scheduling loop.
460 {
461     //make sure scheduler is installed...
462     if (!IsInstalled() || GetScheduler() != this)
463         OsclError::Leave(OsclErrNotInstalled);
464 
465     //make sure scheduler is idle...
466     if (IsStarted())
467         OsclError::Leave(OsclErrInvalidState);
468 
469     iBlockingMode = blocking;
470     iNativeMode = native;
471 
472     //Add stopper AO to scheduler.
473     iStopperCrit.Lock();
474     {
475         iStopper->AddToScheduler();
476         iStopper->PendForExec();
477     }
478     iStopperCrit.Unlock();
479 
480 #if(PV_SCHED_ENABLE_PERF_LOGGING)
481     ResetLogPerf();
482 #endif
483 #if(PV_SCHED_ENABLE_AO_STATS)
484     BeginStats();
485 #endif
486 }
487 
488 
EndScheduling()489 void OsclExecSchedulerCommonBase::EndScheduling()
490 //called after exiting scheduling loop.
491 {
492     //see if it's already stopped..
493     if (!IsStarted())
494         return;
495 
496     //remove stopper AO.
497     iStopperCrit.Lock();
498     iStopper->RemoveFromScheduler();
499     iStopperCrit.Unlock();
500 
501 #if(PV_SCHED_ENABLE_AO_STATS)
502     EndStats();
503 #endif
504 }
505 
506 #if(PV_SCHED_ENABLE_AO_STATS)
ConstructStatQ()507 void OsclExecSchedulerCommonBase::ConstructStatQ()
508 {
509     //create a placeholder for summary stats for
510     //all AOs that are not PVActiveBase.
511     for (uint32 i = 0; i < EOtherExecStats_Last; i++)
512         iOtherExecStats[i] = NULL;
513 
514     OsclAny* ptr = iAlloc->ALLOCATE(sizeof(PVActiveStats));
515     OsclError::LeaveIfNull(ptr);
516     iOtherExecStats[EOtherExecStats_NativeOS] = OSCL_PLACEMENT_NEW(ptr, PVActiveStats(this, "Sched_TotalNativeOS", NULL));
517     //init the stat queue offset.
518     {
519         int offset = (int) & (iOtherExecStats[EOtherExecStats_NativeOS])->iPVStatQLink - (int)(iOtherExecStats[EOtherExecStats_NativeOS]);
520         iPVStatQ.SetOffset(offset);
521     }
522 
523 #if(PV_SCHED_ENABLE_LOOP_STATS)
524     //create nodes for summary stats for scheduler loop time
525 
526     ptr = iAlloc->ALLOCATE(sizeof(PVActiveStats));
527     OsclError::LeaveIfNull(ptr);
528     iOtherExecStats[EOtherExecStats_QueueTime] = OSCL_PLACEMENT_NEW(ptr, PVActiveStats(this, "Sched_QueueTime", NULL));
529 
530     ptr = iAlloc->ALLOCATE(sizeof(PVActiveStats));
531     OsclError::LeaveIfNull(ptr);
532     iOtherExecStats[EOtherExecStats_WaitTime] = OSCL_PLACEMENT_NEW(ptr, PVActiveStats(this, "Sched_WaitTime", NULL));
533 
534 #endif
535     //add the non-AO stats nodes to the stat Q.
536     {
537         for (uint32 i = 0; i < EOtherExecStats_Last; i++)
538         {
539             if (iOtherExecStats[i])
540                 iPVStatQ.InsertTail(*iOtherExecStats[i]);
541         }
542     }
543 }
544 
CleanupStatQ()545 void OsclExecSchedulerCommonBase::CleanupStatQ()
546 {
547     while (!iPVStatQ.IsEmpty())
548     {
549         PVActiveStats* first = iPVStatQ.Head();
550         first->iPVStatQLink.Remove();
551         first->~PVActiveStats();
552         first->iScheduler->iAlloc->deallocate(first);
553     }
554 }
555 
ShowStats(PVActiveStats * active)556 void OsclExecSchedulerCommonBase::ShowStats(PVActiveStats *active)
557 //static routine to print stats for a PV AO.
558 {
559     if (!active)
560         return;
561 
562     //don't print any AOs that never ran.
563     if ((active->iNumRun + active->iNumCancel) == 0)
564         return;
565 
566     PVLogger* iLogger = PVLogger::GetLoggerObject("pvscheduler");
567     if (active->i64Valid)
568     {
569         int64 avgTicksPerRun = (active->iNumRun == 0) ? 0 : active->i64TotalTicksInRun / (int64)active->iNumRun;
570 
571         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"
572                   , active->iScheduler->iName.Str()
573                   , active->iAOName.get_cstr()
574                   , active->iPriority
575                   , active->iNumRun
576                   , active->iMaxTicksInRun
577                   , Oscl_Int64_Utils::get_int64_upper32(avgTicksPerRun)
578                   , Oscl_Int64_Utils::get_int64_lower32(avgTicksPerRun)
579                   , TICKSTR
580                   , active->iNumCancel
581                   , active->iNumRunError
582                   , active->iLeave
583                  ));
584     }
585     else
586     {
587         uint32 avgTicksPerRun = (active->iNumRun == 0) ? 0 : active->iTotalTicksInRun / active->iNumRun;
588         LOGSTATS((0, "PVSCHED:Scheduler '%s', AO '%s': Pri %d NumRun %d, MaxTicksInRun %d, AvgTicksPerRun %d Units %s, NumCancel %d, NumError %d, LeaveCode %d"
589                   , active->iScheduler->iName.Str()
590                   , active->iAOName.get_cstr()
591                   , active->iPriority
592                   , active->iNumRun
593                   , active->iMaxTicksInRun
594                   , avgTicksPerRun
595                   , TICKSTR
596                   , active->iNumCancel
597                   , active->iNumRunError
598                   , active->iLeave
599                  ));
600     }
601 }
602 
ShowSummaryStats(PVActiveStats * active,PVLogger * logger,int64 total,int64 & aGrandTotal,float & aTotalPercent)603 void OsclExecSchedulerCommonBase::ShowSummaryStats(PVActiveStats *active, PVLogger*logger, int64 total, int64& aGrandTotal, float& aTotalPercent)
604 //static routine to print stats for a PV AO.
605 {
606     if (total == (int64)0)
607         return;//to avoid divide by zero
608 
609     if (!active)
610         return;
611 
612     //don't print any AO's that never ran.
613     if ((active->iNumRun + active->iNumCancel) == 0)
614         return;
615 
616     //calculate percent of the total time that was spent in this AO.
617     if (active->i64Valid)
618     {
619         active->iPercent = 100.0 * active->i64TotalTicksInRun / total;
620         aGrandTotal += active->i64TotalTicksInRun;
621     }
622     else
623     {
624         active->iPercent = 100.0 * active->iTotalTicksInRun / total;
625         aGrandTotal += active->iTotalTicksInRun;
626     }
627     aTotalPercent += active->iPercent;
628 
629     int32 fraction = (int32)active->iPercent;
630     float decimal = active->iPercent - fraction;
631     decimal *= 100.0;
632 
633     //print results
634     if (active->i64Valid)
635     {
636         PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO
637                         , (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"
638                            , (int32)active->iPercent, (int32)decimal
639                            , active->iAOName.get_cstr()
640                            , active->iPriority
641                            , active->iNumRun
642                            , active->iMaxTicksInRun
643                            , Oscl_Int64_Utils::get_int64_upper32(active->i64TotalTicksInRun)
644                            , Oscl_Int64_Utils::get_int64_lower32(active->i64TotalTicksInRun)
645                            , active->iNumCancel
646                            , active->iNumRunError
647                            , active->iLeave
648                            , active->iNumInstances
649                           ));
650     }
651     else
652     {
653         PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO
654                         , (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"
655                            , (int32)active->iPercent, (int32)decimal
656                            , active->iAOName.get_cstr()
657                            , active->iPriority
658                            , active->iNumRun
659                            , active->iMaxTicksInRun
660                            , (int32)active->iTotalTicksInRun
661                            , active->iNumCancel
662                            , active->iNumRunError
663                            , active->iLeave
664                            , active->iNumInstances
665                           ));
666     }
667 }
668 
BeginStats()669 void OsclExecSchedulerCommonBase::BeginStats()
670 //Begin stats for all AOs.
671 {
672     iTotalTicksTemp = (uint8*)OSCL_MALLOC(sizeof(PVTICK));
673     SET_TICK(*((PVTICK*)iTotalTicksTemp));
674 }
675 
EndStats()676 void OsclExecSchedulerCommonBase::EndStats()
677 //End stats for all AOs.
678 {
679     //get the end time for the scheduler run.
680     int64 total;
681     DIFF_TICK((*((PVTICK*)iTotalTicksTemp)), total);
682     OSCL_FREE(iTotalTicksTemp);
683 
684     //there may be multiple entries per AO in the stats table, so combine them now.
685     if (!iPVStatQ.IsEmpty())
686     {
687         OsclDoubleRunner<PVActiveStats> iter(iPVStatQ);
688         PVActiveStats *item;
689         for (iter.SetToHead(); ; iter++)
690         {
691             item = iter;
692             //find all subsequent entries in the list that have
693             //the same AO name as this entry.
694             if (item->iNumInstances > 0
695                     && !iPVStatQ.IsTail(item))
696             {
697                 OsclDoubleRunner<PVActiveStats> iter2(iPVStatQ);
698                 PVActiveStats* item2;
699                 for (iter2 = iter, iter2++; ; iter2++)
700                 {
701                     item2 = iter2;
702                     if (item2->iAOName == item->iAOName)
703                     {
704                         item->Combine(*item2);
705                         //mark this entry to ignore in further processing.
706                         item2->iNumInstances = 0;
707                     }
708                     if (iPVStatQ.IsTail(item2))
709                         break;
710                 }
711             }
712             if (iPVStatQ.IsTail(item))
713                 break;
714         }
715     }
716     //end of multiple-instance combine.
717 
718     QUE_ITER_BEGIN(PVActiveStats, iPVStatQ)
719     {
720         if (item
721                 && item->iNumInstances > 0)
722             OsclExecScheduler::ShowStats(item);
723     }
724     QUE_ITER_END(iPVStatQ)
725 
726     //Show summary stats
727 
728     PVLogger* logger = PVLogger::GetLoggerObject("OsclSchedulerPerfStats");
729 
730     PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO,
731                     (0, "OSCL SCHEDULER SUMMARY STATISTICS FOR SCHEDULER '%s'", iName.Str())
732                    )
733 
734     iGrandTotalTicks = 0;
735     iTotalPercent = 0.0;
736     QUE_ITER_BEGIN(PVActiveStats, iPVStatQ)
737     {
738         if (item
739                 && item->iNumInstances > 0)
740             OsclExecScheduler::ShowSummaryStats(item, logger, total, iGrandTotalTicks, iTotalPercent);
741     }
742     QUE_ITER_END(iPVStatQ)
743 
744     //split total percent into whole & decimal parts.
745     int32 fraction = (int32)iTotalPercent;
746     float decimal = iTotalPercent - fraction;
747     decimal *= 100.0;
748 
749     PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO,
750                     (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)
751                    )
752     PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO,
753                     (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)
754                    )
755     PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO,
756                     (0, "  Total Percent Accounted: %d.%02d", (int32)iTotalPercent, (int32)decimal)
757                    )
758 
759     PVTICK f;
760     GET_TICKFREQ(f);
761     PVTICK_INT tickint = TICK_INT(f);
762     PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO,
763                     (0, "  Tick Units: %s Frequency: %s", TICKSTR, TICK_INT_STR, TICK_INT_EXPR(tickint))
764                    )
765 
766     PVLOGGER_LOGMSG(PVLOGMSG_INST_PROF, logger, PVLOGMSG_INFO,
767                     (0, "END OSCL SCHEDULER SUMMARY STATISTICS FOR SCHEDULER '%s'", iName.Str())
768                    )
769 
770 }
771 #endif //PV_SCHED_ENABLE_AO_STATS
772 
773 
Error(int32 anError) const774 void OsclExecSchedulerCommonBase::Error(int32 anError) const
775 //call this when any AO leaves and its error handler does not handle the error.
776 {
777     LOGERROR((0, "PVSCHED:Scheduler '%s', Thread 0x%x: Error! Reason %d", iName.Str(), PVThreadContext::Id(), anError));
778     fprintf(stderr, "PVSCHED:Scheduler '%s', Thread 0x%x: Error! Reason %d\n", iName.Str(), PVThreadContext::Id(), anError);
779 
780     //propagate the leave
781     OsclError::Leave(anError);
782 }
783 
StartScheduler(OsclSemaphore * aSignal)784 OSCL_EXPORT_REF void OsclExecSchedulerCommonBase::StartScheduler(OsclSemaphore *aSignal)
785 //blocking call to start PV scheduler.
786 //Will leave if any AO leaves.
787 {
788 
789     BeginScheduling(true, false);//blocking, non-native
790     if (aSignal)
791         aSignal->Signal();
792 
793 
794     LOGNOTICE((0, "PVSCHED:Scheduler '%s', Thread 0x%x: Starting PV Scheduling Loop", iName.Str(), PVThreadContext::Id()));
795 
796     int32 err;
797     OSCL_TRY(err, BlockingLoopL(););
798 
799     LOGNOTICE((0, "PVSCHED:Scheduler '%s', Thread 0x%x: Exited PV Scheduling Loop", iName.Str(), PVThreadContext::Id()));
800 
801     EndScheduling();
802 
803     if (err)
804         OsclError::Leave(err);
805 
806 }
807 
StartNativeScheduler()808 OSCL_EXPORT_REF void OsclExecSchedulerCommonBase::StartNativeScheduler()
809 //blocking call to start native scheduler.
810 //Will leave if any AO leaves.
811 {
812     OsclError::Leave(OsclErrNotSupported);//native scheduler not supported.
813 }
814 
815 //scheduler stopper request status codes.
816 #define STOPPER_REQUEST_STOP_NATIVE 0
817 #define STOPPER_REQUEST_STOP_PV 1
818 #define STOPPER_REQUEST_SUSPEND 2
819 
StopScheduler()820 OSCL_EXPORT_REF void OsclExecSchedulerCommonBase::StopScheduler()
821 //any thread can use this to stop the blocking scheduler.
822 {
823     if (!IsInstalled())
824         OsclError::Leave(OsclErrNotInstalled);
825 
826     if (!iBlockingMode)
827         OsclError::Leave(OsclErrNotReady);
828     if (!IsStarted())
829         return ;
830 
831     if (iStopper->iStatus != OSCL_REQUEST_PENDING)
832         OsclError::Leave(OsclErrNotReady);
833 
834     //in case scheduler is in the suspend loop...
835     if (iDoSuspend || iSuspended)
836         iResumeSem.Signal();
837 
838     if (iNativeMode)
839         iStopper->PendComplete(STOPPER_REQUEST_STOP_NATIVE);
840     else
841         iStopper->PendComplete(STOPPER_REQUEST_STOP_PV);
842 
843 }
844 
SuspendScheduler()845 OSCL_EXPORT_REF void OsclExecSchedulerCommonBase::SuspendScheduler()
846 //any thread can use this to suspend the blocking scheduler.
847 {
848     if (!IsInstalled())
849         OsclError::Leave(OsclErrNotInstalled);
850 
851     if (iNativeMode)
852         OsclError::Leave(OsclErrNotSupported);
853 
854     if (!iBlockingMode)
855         OsclError::Leave(OsclErrNotSupported);
856 
857     if (!IsStarted())
858         return;
859 
860     if (iStopper->iStatus != OSCL_REQUEST_PENDING)
861         OsclError::Leave(OsclErrNotReady);
862 
863     iStopper->PendComplete(STOPPER_REQUEST_SUSPEND);
864 
865 }
866 
ResumeScheduler()867 OSCL_EXPORT_REF void OsclExecSchedulerCommonBase::ResumeScheduler()
868 //any thread can use this to resume the blocking scheduler.
869 {
870     if (!IsInstalled())
871         OsclError::Leave(OsclErrNotInstalled);
872 
873     if (iDoSuspend || iSuspended)
874     {
875         iResumeSem.Signal();
876         return ;
877     }
878     else
879         OsclError::Leave(OsclErrNotReady); //not suspended.
880 }
881 
RunSchedulerNonBlocking(int32 aCount,int32 & aReady,uint32 & aShortestDelay)882 OSCL_EXPORT_REF void OsclExecScheduler::RunSchedulerNonBlocking(int32 aCount, int32 &aReady, uint32 &aShortestDelay)
883 //run scheduler in non-blocking mode.
884 //Will leave if any AO leaves.
885 {
886 
887 
888     aReady = 0;
889     aShortestDelay = 0;
890 
891     //make sure this scheduler is installed.
892     if (!IsInstalled())
893         OsclError::Leave(OsclErrNotInstalled);
894 
895 #if !(OSCL_RELEASE_BUILD)  && !defined(NDEBUG)
896     //make sure this scheduler is really installed in this
897     //thread.
898     if (GetScheduler() != this)
899         OsclError::Leave(OsclErrNotInstalled);
900 #endif
901 
902     //start scheduling if needed.
903     if (!IsStarted())
904     {
905         BeginScheduling(false, false);//nonblocking, non-native
906     }
907     else if (iBlockingMode || iNativeMode)
908         OsclError::Leave(OsclErrInvalidState);//not stopped
909 
910     //Process timers.  All ready timers will get
911     //moved to the ready queue.
912     UpdateTimersMsec(aShortestDelay);
913 
914     //Run until the requested count is reached, or there
915     //aren't any AOs ready.
916     for (int32 count = 0; count < aCount;)
917     {
918         //find highest pri ready AO.
919         PVActiveBase* pvactive = iReadyQ.PopTop();
920         if (pvactive)
921         {
922             //run it
923             count++;
924             CallRunExec(pvactive);
925 
926             //re-evaluate timers
927             UpdateTimersMsec(aShortestDelay);
928         }
929         else
930             break;//nothing ready
931     }
932 
933     //at this point, either nothing else is ready or the target count was reached.
934 
935     aReady = iReadyQ.Depth();
936 
937 }
938 
RegisterForCallback(OsclSchedulerObserver * aCallback,OsclAny * aCallbackContext)939 OSCL_EXPORT_REF void OsclExecScheduler::RegisterForCallback(OsclSchedulerObserver* aCallback, OsclAny* aCallbackContext)
940 {
941     //Update the callback pointers.
942     iReadyQ.RegisterForCallback(aCallback, aCallbackContext);
943 }
944 
945 ////////////////////////////////////////
946 // Queue Management
947 ////////////////////////////////////////
948 
949 
FindPVBase(PVActiveBase * active,OsclDoubleList<PVActiveBase> & q)950 PVActiveBase * OsclExecSchedulerCommonBase::FindPVBase(PVActiveBase *active, OsclDoubleList<PVActiveBase> &q)
951 //Search a PVActiveBase queue, given a PVActiveBase ptr.
952 {
953     QUE_ITER_BEGIN(PVActiveBase, q)
954     {
955         if (item == active)
956             return item;
957     }
958     QUE_ITER_END(q)
959     return NULL;
960 }
961 
CleanupExecQ()962 void OsclExecSchedulerCommonBase::CleanupExecQ()
963 {
964     //Cleanup timers.
965     {
966         PVActiveBase *top;
967         while ((top = iExecTimerQ.PopTop()))
968             top->RemoveFromScheduler();
969     }
970     //Cleanup ready AOs.
971     PVActiveBase* top = iReadyQ.Top();
972     while (top)
973     {
974         top->RemoveFromScheduler();
975         top = iReadyQ.Top();
976     }
977 }
978 
InitExecQ(int nreserve)979 void OsclExecSchedulerCommonBase::InitExecQ(int nreserve)
980 //init the pvactive queues.
981 {
982     iExecTimerQ.Construct(nreserve);
983     iReadyQ.Construct(nreserve);
984 }
985 
986 ////////////////////////////////////////
987 // Non-Symbian queue management
988 ////////////////////////////////////////
989 
AddToExecTimerQ(PVActiveBase * anActive,uint32 aDelayMicrosec)990 void OsclExecSchedulerCommonBase::AddToExecTimerQ(PVActiveBase* anActive, uint32 aDelayMicrosec)
991 //timer implementation.
992 //Add an AO to the pending timer queue.
993 {
994     OSCL_ASSERT(anActive);//EExecNull
995 
996     //make sure this AO is not already added.
997     if (anActive->IsInAnyQ())
998         OsclError::Leave(OsclErrInvalidState);//EExecAlreadyAdded
999 
1000     //Set time in ticks when AO should run.
1001 
1002     uint32 tickperiod = OsclTickCount::TickCountPeriod();
1003     OSCL_ASSERT(tickperiod != 0);
1004 
1005     //Round to the nearest integer with the computation:
1006     //floor((2*Interval_usec/ticks_per_usec +  1)/2)
1007     //
1008     //The computed time may rollover the 32-bit value-- that's OK, because
1009     //the tick count will also rollover.
1010     uint32 timenow = OsclTickCount::TickCount();
1011     anActive->iPVReadyQLink.iTimeToRunTicks = timenow + (aDelayMicrosec * 2 / tickperiod + 1) / 2;
1012 
1013     if (aDelayMicrosec > 0)
1014     {
1015         LOGPERF2((0, "PVSCHED:%s AO %s Timer delay %d TimeToRunTicks %d Timenow %d"
1016                   , iLogPerfIndentStr, anActive->iName.Str()
1017                   , aDelayMicrosec, anActive->iPVReadyQLink.iTimeToRunTicks
1018                   , timenow));
1019     }
1020 
1021     //queue it
1022     iExecTimerQ.Add(anActive);
1023 
1024     //if this AO is in the front of the queue now, we need to do a
1025     //callback, because the shortest delay interval has changed.
1026     if (iReadyQ.Callback()
1027             && anActive == iExecTimerQ.Top())
1028     {
1029         iReadyQ.TimerCallback(aDelayMicrosec);
1030     }
1031 }
1032 
1033 //For 32-bit time comparisons with rollover handling.
1034 //This value is (2^31)-1
1035 const uint32 OsclExecSchedulerCommonBase::iTimeCompareThreshold = 0x7fffffff;
1036 
UpdateTimers(uint32 & aShortestDelay)1037 PVActiveBase* OsclExecSchedulerCommonBase::UpdateTimers(uint32 &aShortestDelay)
1038 //timer processing.
1039 //Complete requests for all timers that are ready now,
1040 //then return the pending timer with the shortest delay if any.
1041 //If any pending timer is returned it's the top of the queue so
1042 //it can be discarded later with Pop.
1043 {
1044     aShortestDelay = 0;
1045 
1046     PVActiveBase *top = iExecTimerQ.Top();
1047 
1048     if (!top)
1049         return NULL;
1050 
1051     uint32 timenow = OsclTickCount::TickCount();
1052 
1053     //Find all timers that are ready, and the first
1054     //timer that isn't.  The list is sorted by
1055     //time then priority.
1056     for (; top; top = iExecTimerQ.Top())
1057     {
1058         //calculate time to run <= timenow, taking possible rollover into account
1059         uint32 deltaTicks = timenow - top->iPVReadyQLink.iTimeToRunTicks;
1060         if (deltaTicks <= iTimeCompareThreshold)
1061         {
1062             //this timer is ready
1063             iExecTimerQ.Pop(top);
1064 
1065             PendComplete(top, OSCL_REQUEST_ERR_NONE, EPVThreadContext_InThread);
1066         }
1067         else
1068         {
1069             //we found the pending timer with the shortest delay.
1070             //get the delay value
1071             int32 delayTicks = deltaTicks;
1072             if (delayTicks < 0)
1073                 delayTicks = (-delayTicks);
1074             aShortestDelay = delayTicks;
1075             return top;
1076         }
1077     }
1078 
1079     return NULL;//no pending timers.
1080 }
1081 
UpdateTimersMsec(uint32 & aShortestDelay)1082 PVActiveBase* OsclExecSchedulerCommonBase::UpdateTimersMsec(uint32 &aShortestDelay)
1083 //Identical to UpdateTimers except the delay returned is milliseconds instead
1084 //of ticks.
1085 {
1086     aShortestDelay = 0;
1087 
1088     PVActiveBase *top = iExecTimerQ.Top();
1089 
1090     if (!top)
1091         return NULL;
1092 
1093     uint32 timenow = OsclTickCount::TickCount();
1094 
1095     //Find all timers that are ready, and the first
1096     //timer that isn't.  The list is sorted by
1097     //time then priority.
1098     for (; top; top = iExecTimerQ.Top())
1099     {
1100         //calculate time to run <= timenow, taking possible rollover into account
1101         uint32 deltaTicks = timenow - top->iPVReadyQLink.iTimeToRunTicks;
1102         if (deltaTicks <= iTimeCompareThreshold)
1103         {
1104             //this timer is ready
1105             iExecTimerQ.Pop(top);
1106 
1107             PendComplete(top, OSCL_REQUEST_ERR_NONE, EPVThreadContext_InThread);
1108         }
1109         else
1110         {
1111             //we found the pending timer with the shortest delay.
1112             //get the delay value
1113             int32 delayTicks = deltaTicks;
1114             if (delayTicks < 0)
1115                 delayTicks = (-delayTicks);
1116             aShortestDelay = OsclTickCount::TicksToMsec(delayTicks);
1117 
1118             //if delay became zero after the conversion from ticks to msec,
1119             //then just consider this timer to be ready now.
1120             if (aShortestDelay == 0)
1121             {
1122                 //this timer is ready
1123                 iExecTimerQ.Pop(top);
1124 
1125                 PendComplete(top, OSCL_REQUEST_ERR_NONE, EPVThreadContext_InThread);
1126             }
1127             else
1128             {
1129                 return top;
1130             }
1131         }
1132     }
1133 
1134     return NULL;//no pending timers.
1135 }
1136 
PendComplete(PVActiveBase * pvbase,int32 aReason,TPVThreadContext aThreadContext)1137 void OsclExecSchedulerCommonBase::PendComplete(PVActiveBase *pvbase, int32 aReason, TPVThreadContext aThreadContext)
1138 //complete a request for this scheduler.
1139 //Calling context can be any thread.
1140 {
1141     //During timer cancellation, the AO may still be in the ExecTimerQ.
1142     //Remove it now, since it won't get removed by the scheduler loop.
1143     //Check thread context first, to accessing timer queue from out-of-thread.
1144     if (aThreadContext == EPVThreadContext_InThread)
1145     {
1146         LOGPERF2((0, "PVSCHED: %s AO %s Request complete", iLogPerfIndentStr, pvbase->iName.Str()));
1147 
1148         if (iExecTimerQ.IsIn(pvbase))
1149             iExecTimerQ.Remove(pvbase);
1150     }
1151 
1152     //Pass this to the ReadyQ so it can do appropriate queue locks
1153     int32 err = iReadyQ.PendComplete(pvbase, aReason);
1154 
1155     OsclError::LeaveIfError(err);
1156 }
1157 
RequestCanceled(PVActiveBase * pvbase)1158 void OsclExecSchedulerCommonBase::RequestCanceled(PVActiveBase* pvbase)
1159 {
1160     LOGPERF2((0, "PVSCHED: %s AO %s Request canceled", iLogPerfIndentStr, pvbase->iName.Str()));
1161 
1162     //This gets called right after the AO's DoCancel was
1163     //called.  It must wait on the request to cancel, and remove it
1164     //from the ready Q.
1165 
1166     //Calling context is always in-thread, since Cancel context is always
1167     //in-thread.
1168 
1169     //See if the request was completed by the DoCancel.
1170     bool complete = iReadyQ.IsIn(pvbase);
1171 
1172     DECLARE_LOOP_STATS;
1173 
1174     if (!complete)
1175     {
1176         //If request is still pending after DoCancel is called, it
1177         //means some other thread will complete the request cancellation.
1178         //If the AO does not have a proper DoCancel, this wait will hang up.
1179 
1180         //reset the perf indent when scheduler gives up CPU...
1181         RESET_LOG_PERF((0, "PVSCHED: Waiting on cancel... AO '%s'", pvbase->iName.Str()));
1182 
1183 #if (PV_SCHED_ENABLE_PERF_LOGGING)
1184         uint32 ticks = OsclTickCount::TickCount();
1185 #endif
1186         START_LOOP_STATS(iOtherExecStats[EOtherExecStats_WaitTime]);
1187         int32 err = iReadyQ.WaitForRequestComplete(pvbase);
1188         END_LOOP_STATS(iOtherExecStats[EOtherExecStats_WaitTime]);
1189 
1190 #if (PV_SCHED_ENABLE_PERF_LOGGING)
1191         LOGPERF((0, "PVSCHED: ...Cancel took %d Ticks", OsclTickCount::TickCount() - ticks));
1192 #endif
1193 
1194         OsclError::LeaveIfError(err);
1195     }
1196 
1197     //Set request idle and remove from ready Q.
1198     //The AO will not run
1199     pvbase->iBusy = false;
1200 
1201     START_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);
1202     iReadyQ.Remove(pvbase);
1203     END_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);
1204 }
1205 
1206 
1207 
1208 
1209 ////////////////////////////////////////
1210 // PV Scheduling Loop Implementation
1211 ////////////////////////////////////////
1212 
1213 
CallRunExec(PVActiveBase * pvactive)1214 void OsclExecSchedulerCommonBase::CallRunExec(PVActiveBase *pvactive)
1215 //Run a PV AO.
1216 {
1217     //Set this AO inactive.  This AO may be from the
1218     //ready Q or the pending Timer Q.
1219     //The dequeing is done by the caller.
1220     pvactive->iBusy = false;
1221 
1222     //start stats
1223 #if (PV_SCHED_ENABLE_AO_STATS)
1224     iPVStats = pvactive->iPVActiveStats;//save value now since it may change in the Run call.
1225     iDelta = 0;
1226     INIT_TICK(iTime);
1227 #endif
1228 
1229     SET_TICK(iTime);
1230 
1231     //Call the Run under a trap harness.
1232     //Pass the ErrorTrapImp pointer to reduce overhead of the Try call.
1233     //We already did a null ptr check on iErrorTrapImp so it's safe to de-ref here.
1234     int32 err;
1235     OSCL_TRY_NO_TLS(iErrorTrapImp, err, pvactive->Run(););
1236 
1237     //end stats
1238     DIFF_TICK(iTime, iDelta);
1239     UPDATE_RUNL_TIME(iPVStats, iDelta);
1240     UPDATE_LEAVE_CODE(iPVStats, err);
1241 
1242 #if(PV_SCHED_ENABLE_PERF_LOGGING)
1243     //show AO time.
1244     IncLogPerf(iDelta);
1245     LOGPERF((0, "PVSCHED: %s Run %d %s AO %s", iLogPerfIndentStr, (int32)iDelta, TICKSTR, pvactive->iName.Str()));
1246 #endif
1247 
1248     //check for a leave in the Run...
1249     if (err != OsclErrNone)
1250     {
1251         //start stats
1252         SET_TICK(iTime);
1253 
1254         //call the AO error handler
1255         err = pvactive->RunError(err);
1256 
1257         //end stats
1258         DIFF_TICK(iTime, iDelta);
1259         UPDATE_RUNERROR_TIME(iPVStats, iDelta);
1260 
1261         //If the AO did not handle the error, indicated by returning
1262         //ErrNone, then call the scheduler error handler.
1263         if (err != OsclErrNone)
1264         {
1265             LOGERROR((0, "PVSCHED:Scheduler '%s', Thread 0x%x: Error! AO %s Error %d not handled"
1266                       , iName.Str(), PVThreadContext::Id()
1267                       , pvactive->iName.Str(), err));
1268             fprintf(stderr, "PVSCHED:Scheduler '%s', Thread 0x%x: Error! AO %s Error %d not handled\n"
1269                     , iName.Str(), PVThreadContext::Id()
1270                     , pvactive->iName.Str(), err);
1271 
1272             Error(err);
1273         }
1274     }
1275 
1276 }
1277 
1278 
BlockingLoopL()1279 void OsclExecSchedulerCommonBase::BlockingLoopL()
1280 //Blocking scheduling loop.
1281 //Will leave if any AO leaves.
1282 {
1283 
1284     PVActiveBase* pvactive;
1285 
1286 
1287     while (!iDoStop)
1288     {
1289         //Get the next AO to run.  This call may block until an AO is ready.
1290         pvactive = WaitForReadyAO();
1291 
1292         if (pvactive)
1293         {
1294             CallRunExec(pvactive);
1295         }
1296         else
1297         {
1298             OsclError::Leave(OsclErrCorrupt);//EExecStrayEvent
1299         }
1300 
1301         //check for a suspend signal..
1302         if (iDoSuspend)
1303         {
1304             iSuspended = true;
1305             iDoSuspend = false;
1306             iResumeSem.Wait();
1307             iSuspended = false;
1308         }
1309 
1310     }//while !dostop
1311 
1312     iDoStop = false;
1313 }
1314 
1315 
WaitForReadyAO()1316 PVActiveBase* OsclExecSchedulerCommonBase::WaitForReadyAO()
1317 //Find the next AO to run-- non-Symbian version.
1318 {
1319     DECLARE_LOOP_STATS;
1320 
1321     //First process timers.
1322     //All ready timers will get moved to the run Q.
1323     uint32 waitTicks;
1324     START_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);
1325     PVActiveBase* pvtimer = UpdateTimers(waitTicks);
1326     END_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);
1327 
1328     //Check for a ready AO.
1329     START_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);
1330     PVActiveBase* pvactive = iReadyQ.PopTop();
1331     END_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);
1332 
1333     if (pvactive)
1334     {
1335         //An AO is ready.
1336         return pvactive;
1337     }
1338     else if (pvtimer)
1339     {
1340         //No AO is ready, but at least one timer is pending.
1341         //Wait on shortest timer expiration or a new request.
1342 
1343         //reset the perf logging indent each time scheduler gives up CPU.
1344         RESET_LOG_PERF((0, "PVSCHED: Waiting on timer... Ticks %d AO %s", waitTicks, pvtimer->iName.Str()));
1345 
1346         START_LOOP_STATS(iOtherExecStats[EOtherExecStats_WaitTime]);
1347         pvactive = iReadyQ.WaitAndPopTop(OsclTickCount::TicksToMsec(waitTicks));
1348         END_LOOP_STATS(iOtherExecStats[EOtherExecStats_WaitTime]);
1349 
1350         if (pvactive)
1351         {
1352             //Another AO's request completed while we were waiting.
1353             //Run that one instead of the timer.
1354             return pvactive;
1355         }
1356         else
1357         {
1358             //It's time to complete this timer's request and run it.
1359             //To save overhead, don't move it to the ReadyQ, just pop it
1360             //from its current location on the top of the timer queue.
1361 
1362             pvtimer->iStatus = OSCL_REQUEST_ERR_NONE;
1363 
1364             START_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);
1365             iExecTimerQ.Pop(pvtimer);
1366             END_LOOP_STATS(iOtherExecStats[EOtherExecStats_QueueTime]);
1367 
1368             return pvtimer;
1369         }
1370     }
1371     else
1372     {
1373         //Nothing is ready and no timer is pending.
1374         //Wait on a request to be completed by another thread.
1375 
1376         //reset the perf logging indent each time scheduler gives up CPU.
1377         RESET_LOG_PERF((0, "PVSCHED: Waiting on any request..."));
1378 
1379         START_LOOP_STATS(iOtherExecStats[EOtherExecStats_WaitTime]);
1380         pvactive = iReadyQ.WaitAndPopTop();
1381         END_LOOP_STATS(iOtherExecStats[EOtherExecStats_WaitTime]);
1382 
1383         return pvactive;
1384     }
1385 }
1386 
1387 ////////////////////////////////////////
1388 // PVSchedulerStopper
1389 ////////////////////////////////////////
PVSchedulerStopper()1390 PVSchedulerStopper::PVSchedulerStopper()
1391         : OsclActiveObject((int32)OsclActiveObject::EPriorityHighest, "Stopper")
1392 {
1393 }
1394 
~PVSchedulerStopper()1395 PVSchedulerStopper::~PVSchedulerStopper()
1396 {
1397 }
1398 
Run()1399 void PVSchedulerStopper::Run()
1400 //This AO just waits for a signal to suspend or stop the scheduler.
1401 {
1402     //Stop
1403     switch (Status())
1404     {
1405         case STOPPER_REQUEST_STOP_NATIVE:
1406             break;
1407         case STOPPER_REQUEST_STOP_PV:
1408         {
1409             //stop my scheduling loop
1410             OsclExecSchedulerCommonBase* myscheduler = OsclExecScheduler::GetScheduler();
1411             if (myscheduler)
1412                 myscheduler->iDoStop = true;
1413         }
1414         break;
1415         case STOPPER_REQUEST_SUSPEND:
1416         {
1417             //suspend my scheduling loop
1418             OsclExecSchedulerCommonBase* myscheduler = OsclExecScheduler::GetScheduler();
1419             if (myscheduler)
1420                 myscheduler->iDoSuspend = true;
1421 
1422             //re-schedule ourself
1423             PendForExec();
1424         }
1425         break;
1426         default:
1427             break;
1428     }
1429 }
1430 
1431 ////////////////////////////////////////
1432 // Symbian Coe Scheduler
1433 ////////////////////////////////////////
1434 
1435 
1436 
1437 
1438