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