6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
36 #include <InterfaceDefs.h>
42 const float kTaskOverhead
= 0.01f
;
43 // this should really be specified by the task itself
44 const float kIdleTreshold
= 0.15f
;
46 const bigtime_t kInfinity
= B_INFINITE_TIMEOUT
;
52 // stolen from roster server
55 get_system_info(&sinfo
);
57 cpu_info
* cpuInfos
= new cpu_info
[sinfo
.cpu_count
];
58 get_cpu_info(0, sinfo
.cpu_count
, cpuInfos
);
60 for (uint32 index
= 0; index
< sinfo
.cpu_count
; index
++)
61 time
+= cpuInfos
[index
].active_time
;
64 return time
/ ((bigtime_t
) sinfo
.cpu_count
);
68 class AccumulatedOneShotDelayedTask
: public OneShotDelayedTask
{
69 // supports accumulating functors
71 AccumulatedOneShotDelayedTask(AccumulatingFunctionObject
* functor
,
72 bigtime_t delay
, bigtime_t maxAccumulatingTime
= 0,
73 int32 maxAccumulateCount
= 0)
75 OneShotDelayedTask(functor
, delay
),
76 maxAccumulateCount(maxAccumulateCount
),
78 maxAccumulatingTime(maxAccumulatingTime
),
79 initialTime(system_time())
83 bool CanAccumulate(const AccumulatingFunctionObject
* accumulateThis
) const
85 if (maxAccumulateCount
&& accumulateCount
> maxAccumulateCount
)
86 // don't accumulate if too may accumulated already
89 if (maxAccumulatingTime
&& system_time() > initialTime
90 + maxAccumulatingTime
) {
91 // don't accumulate if too late past initial task
95 return static_cast<AccumulatingFunctionObject
*>(fFunctor
)->
96 CanAccumulate(accumulateThis
);
99 virtual void Accumulate(AccumulatingFunctionObject
* accumulateThis
,
102 fRunAfter
= system_time() + delay
;
105 static_cast<AccumulatingFunctionObject
*>(fFunctor
)->
106 Accumulate(accumulateThis
);
110 int32 maxAccumulateCount
;
111 int32 accumulateCount
;
112 bigtime_t maxAccumulatingTime
;
113 bigtime_t initialTime
;
117 // #pragma mark - DelayedTask
120 DelayedTask::DelayedTask(bigtime_t delay
)
122 fRunAfter(system_time() + delay
)
127 DelayedTask::~DelayedTask()
132 // #pragma mark - OneShotDelayedTask
135 OneShotDelayedTask::OneShotDelayedTask(FunctionObject
* functor
,
144 OneShotDelayedTask::~OneShotDelayedTask()
151 OneShotDelayedTask::RunIfNeeded(bigtime_t currentTime
)
153 if (currentTime
< fRunAfter
)
161 // #pragma mark - PeriodicDelayedTask
164 PeriodicDelayedTask::PeriodicDelayedTask(
165 FunctionObjectWithResult
<bool>* functor
, bigtime_t initialDelay
,
168 DelayedTask(initialDelay
),
175 PeriodicDelayedTask::~PeriodicDelayedTask()
182 PeriodicDelayedTask::RunIfNeeded(bigtime_t currentTime
)
184 if (currentTime
< fRunAfter
)
187 fRunAfter
= currentTime
+ fPeriod
;
189 return fFunctor
->Result();
193 PeriodicDelayedTaskWithTimeout::PeriodicDelayedTaskWithTimeout(
194 FunctionObjectWithResult
<bool>* functor
, bigtime_t initialDelay
,
195 bigtime_t period
, bigtime_t timeout
)
197 PeriodicDelayedTask(functor
, initialDelay
, period
),
198 fTimeoutAfter(system_time() + timeout
)
204 PeriodicDelayedTaskWithTimeout::RunIfNeeded(bigtime_t currentTime
)
206 if (currentTime
< fRunAfter
)
209 fRunAfter
= currentTime
+ fPeriod
;
211 if (fFunctor
->Result())
214 // if call didn't terminate the task yet, check if timeout is due
215 return currentTime
> fTimeoutAfter
;
219 // #pragma mark - RunWhenIdleTask
222 RunWhenIdleTask::RunWhenIdleTask(FunctionObjectWithResult
<bool>* functor
,
223 bigtime_t initialDelay
, bigtime_t idleFor
, bigtime_t heartBeat
)
225 PeriodicDelayedTask(functor
, initialDelay
, heartBeat
),
227 fState(kInitialDelay
),
228 fActivityLevelStart(0),
230 fLastCPUTooBusyTime(0)
235 RunWhenIdleTask::~RunWhenIdleTask()
241 RunWhenIdleTask::RunIfNeeded(bigtime_t currentTime
)
243 if (currentTime
< fRunAfter
)
246 fRunAfter
= currentTime
+ fPeriod
;
247 // PRINT(("runWhenIdle: runAfter %Ld, current time %Ld, period %Ld\n",
248 // fRunAfter, currentTime, fPeriod));
250 if (fState
== kInitialDelay
) {
251 // PRINT(("run when idle task - past intial delay\n"));
252 ResetIdleTimer(currentTime
);
253 } else if (fState
== kInIdleState
&& !StillIdle(currentTime
)) {
254 fState
= kInitialIdleWait
;
255 ResetIdleTimer(currentTime
);
256 } else if (fState
!= kInitialIdleWait
|| IdleTimerExpired(currentTime
)) {
257 fState
= kInIdleState
;
259 return fFunctor
->Result();
267 RunWhenIdleTask::ResetIdleTimer(bigtime_t currentTime
)
269 fActivityLevel
= ActivityLevel();
270 fActivityLevelStart
= currentTime
;
271 fLastCPUTooBusyTime
= currentTime
;
272 fState
= kInitialIdleWait
;
277 RunWhenIdleTask::IsIdle(bigtime_t currentTime
, float taskOverhead
)
279 bigtime_t currentActivityLevel
= ActivityLevel();
280 float load
= (float)(currentActivityLevel
- fActivityLevel
)
281 / (float)(currentTime
- fActivityLevelStart
);
283 fActivityLevel
= currentActivityLevel
;
284 fActivityLevelStart
= currentTime
;
286 load
-= taskOverhead
;
290 if (load
> kIdleTreshold
) {
291 // PRINT(("not idle enough %f\n", load));
293 } else if ((currentTime
- fLastCPUTooBusyTime
) < fIdleFor
294 || idle_time() < fIdleFor
) {
295 // PRINT(("load %f, not idle long enough %Ld, %Ld\n", load,
296 // currentTime - fLastCPUTooBusyTime,
303 PRINT(("load %f, idle for %Ld sec, go\n", load
,
304 (currentTime
- fLastCPUTooBusyTime
) / 1000000));
312 RunWhenIdleTask::IdleTimerExpired(bigtime_t currentTime
)
314 return IsIdle(currentTime
, 0);
319 RunWhenIdleTask::StillIdle(bigtime_t currentTime
)
321 return IsIdle(currentTime
, kIdleTreshold
);
325 // #pragma mark - TaskLoop
328 TaskLoop::TaskLoop(bigtime_t heartBeat
)
331 fHeartBeat(heartBeat
)
336 TaskLoop::~TaskLoop()
342 TaskLoop::RunLater(DelayedTask
* task
)
349 TaskLoop::RunLater(FunctionObject
* functor
, bigtime_t delay
)
351 RunLater(new OneShotDelayedTask(functor
, delay
));
356 TaskLoop::RunLater(FunctionObjectWithResult
<bool>* functor
,
357 bigtime_t delay
, bigtime_t period
)
359 RunLater(new PeriodicDelayedTask(functor
, delay
, period
));
364 TaskLoop::RunLater(FunctionObjectWithResult
<bool>* functor
, bigtime_t delay
,
365 bigtime_t period
, bigtime_t timeout
)
367 RunLater(new PeriodicDelayedTaskWithTimeout(functor
, delay
, period
,
373 TaskLoop::RunWhenIdle(FunctionObjectWithResult
<bool>* functor
,
374 bigtime_t initialDelay
, bigtime_t idleTime
, bigtime_t heartBeat
)
376 RunLater(new RunWhenIdleTask(functor
, initialDelay
, idleTime
, heartBeat
));
380 // #pragma mark - TaskLoop
384 TaskLoop::AccumulatedRunLater(AccumulatingFunctionObject
* functor
,
385 bigtime_t delay
, bigtime_t maxAccumulatingTime
, int32 maxAccumulateCount
)
387 AutoLock
<BLocker
> autoLock(&fLock
);
388 if (!autoLock
.IsLocked())
391 int32 count
= fTaskList
.CountItems();
392 for (int32 index
= 0; index
< count
; index
++) {
393 AccumulatedOneShotDelayedTask
* task
394 = dynamic_cast<AccumulatedOneShotDelayedTask
*>(
395 fTaskList
.ItemAt(index
));
398 else if (task
->CanAccumulate(functor
)) {
399 task
->Accumulate(functor
, delay
);
404 RunLater(new AccumulatedOneShotDelayedTask(functor
, delay
,
405 maxAccumulatingTime
, maxAccumulateCount
));
412 ASSERT(fLock
.IsLocked());
414 int32 count
= fTaskList
.CountItems();
416 bigtime_t currentTime
= system_time();
417 for (int32 index
= 0; index
< count
; ) {
418 DelayedTask
* task
= fTaskList
.ItemAt(index
);
419 // give every task a try
420 if (task
->RunIfNeeded(currentTime
)) {
421 // if done, remove from list
428 return count
== 0 && !KeepPulsingWhenEmpty();
433 TaskLoop::LatestRunTime() const
435 ASSERT(fLock
.IsLocked());
436 bigtime_t result
= kInfinity
;
439 DelayedTask
* nextTask
= 0;
441 int32 count
= fTaskList
.CountItems();
442 for (int32 index
= 0; index
< count
; index
++) {
443 bigtime_t runAfter
= fTaskList
.ItemAt(index
)->RunAfterTime();
444 if (runAfter
< result
) {
448 nextTask
= fTaskList
.ItemAt(index
);
456 PRINT(("latestRunTime : next task %s\n", typeid(*nextTask
).name
));
458 PRINT(("latestRunTime : no next task\n"));
466 TaskLoop::RemoveTask(DelayedTask
* task
)
468 ASSERT(fLock
.IsLocked());
470 fTaskList
.RemoveItem(task
);
474 TaskLoop::AddTask(DelayedTask
* task
)
476 AutoLock
<BLocker
> autoLock(&fLock
);
477 if (!autoLock
.IsLocked()) {
482 fTaskList
.AddItem(task
);
483 StartPulsingIfNeeded();
487 // #pragma mark - StandAloneTaskLoop
490 StandAloneTaskLoop::StandAloneTaskLoop(bool keepThread
, bigtime_t heartBeat
)
495 fKeepThread(keepThread
)
500 StandAloneTaskLoop::~StandAloneTaskLoop()
504 bool easyOut
= (fScanThread
== -1);
508 for (int32 timeout
= 10000; ; timeout
--) {
509 // use a 10 sec timeout value in case the spawned
510 // thread is stuck somewhere
513 PRINT(("StandAloneTaskLoop timed out, quitting abruptly"));
520 done
= (fScanThread
== -1);
531 StandAloneTaskLoop::StartPulsingIfNeeded()
533 ASSERT(fLock
.IsLocked());
534 if (fScanThread
< 0) {
535 // no loop thread yet, spawn one
536 fScanThread
= spawn_thread(StandAloneTaskLoop::RunBinder
,
537 "TrackerTaskLoop", B_LOW_PRIORITY
, this);
538 resume_thread(fScanThread
);
544 StandAloneTaskLoop::KeepPulsingWhenEmpty() const
551 StandAloneTaskLoop::RunBinder(void* castToThis
)
553 StandAloneTaskLoop
* self
= (StandAloneTaskLoop
*)castToThis
;
560 StandAloneTaskLoop::Run()
563 AutoLock
<BLocker
> autoLock(&fLock
);
568 // task loop being deleted, let go of the thread allowing the
569 // to go through deletion
579 // figure out when to run next by checking out when the different
580 // tasks wan't to be woken up, snooze until a little bit before that
582 bigtime_t now
= system_time();
583 bigtime_t latestRunTime
= LatestRunTime() - 1000;
584 bigtime_t afterHeartBeatTime
= now
+ fHeartBeat
;
585 bigtime_t snoozeTill
= latestRunTime
< afterHeartBeatTime
?
586 latestRunTime
: afterHeartBeatTime
;
590 if (snoozeTill
> now
)
591 snooze_until(snoozeTill
, B_SYSTEM_TIMEBASE
);
599 StandAloneTaskLoop::AddTask(DelayedTask
* delayedTask
)
601 _inherited::AddTask(delayedTask
);
605 // wake up the loop thread if it is asleep
607 get_thread_info(fScanThread
, &info
);
608 if (info
.state
== B_THREAD_ASLEEP
) {
609 suspend_thread(fScanThread
);
610 snooze(1000); // snooze because BeBook sez so
611 resume_thread(fScanThread
);
616 // #pragma mark - PiggybackTaskLoop
619 PiggybackTaskLoop::PiggybackTaskLoop(bigtime_t heartBeat
)
622 fNextHeartBeatTime(0),
628 PiggybackTaskLoop::~PiggybackTaskLoop()
634 PiggybackTaskLoop::PulseMe()
639 bigtime_t time
= system_time();
640 if (fNextHeartBeatTime
< time
) {
641 AutoLock
<BLocker
> autoLock(&fLock
);
644 fNextHeartBeatTime
= time
+ fHeartBeat
;
650 PiggybackTaskLoop::KeepPulsingWhenEmpty() const
657 PiggybackTaskLoop::StartPulsingIfNeeded()