btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / kits / tracker / TaskLoop.cpp
blob482b09e03cb9306465fb6f50f0e61ef94462c803
1 /*
2 Open Tracker License
4 Terms and Conditions
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.
32 All rights reserved.
35 #include <Debug.h>
36 #include <InterfaceDefs.h>
38 #include "AutoLock.h"
39 #include "TaskLoop.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;
49 static bigtime_t
50 ActivityLevel()
52 // stolen from roster server
53 bigtime_t time = 0;
54 system_info sinfo;
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;
63 delete[] cpuInfos;
64 return time / ((bigtime_t) sinfo.cpu_count);
68 class AccumulatedOneShotDelayedTask : public OneShotDelayedTask {
69 // supports accumulating functors
70 public:
71 AccumulatedOneShotDelayedTask(AccumulatingFunctionObject* functor,
72 bigtime_t delay, bigtime_t maxAccumulatingTime = 0,
73 int32 maxAccumulateCount = 0)
75 OneShotDelayedTask(functor, delay),
76 maxAccumulateCount(maxAccumulateCount),
77 accumulateCount(1),
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
87 return false;
89 if (maxAccumulatingTime && system_time() > initialTime
90 + maxAccumulatingTime) {
91 // don't accumulate if too late past initial task
92 return false;
95 return static_cast<AccumulatingFunctionObject*>(fFunctor)->
96 CanAccumulate(accumulateThis);
99 virtual void Accumulate(AccumulatingFunctionObject* accumulateThis,
100 bigtime_t delay)
102 fRunAfter = system_time() + delay;
103 // reset fRunAfter
104 accumulateCount++;
105 static_cast<AccumulatingFunctionObject*>(fFunctor)->
106 Accumulate(accumulateThis);
109 private:
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,
136 bigtime_t delay)
138 DelayedTask(delay),
139 fFunctor(functor)
144 OneShotDelayedTask::~OneShotDelayedTask()
146 delete fFunctor;
150 bool
151 OneShotDelayedTask::RunIfNeeded(bigtime_t currentTime)
153 if (currentTime < fRunAfter)
154 return false;
156 (*fFunctor)();
157 return true;
161 // #pragma mark - PeriodicDelayedTask
164 PeriodicDelayedTask::PeriodicDelayedTask(
165 FunctionObjectWithResult<bool>* functor, bigtime_t initialDelay,
166 bigtime_t period)
168 DelayedTask(initialDelay),
169 fPeriod(period),
170 fFunctor(functor)
175 PeriodicDelayedTask::~PeriodicDelayedTask()
177 delete fFunctor;
181 bool
182 PeriodicDelayedTask::RunIfNeeded(bigtime_t currentTime)
184 if (currentTime < fRunAfter)
185 return false;
187 fRunAfter = currentTime + fPeriod;
188 (*fFunctor)();
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)
203 bool
204 PeriodicDelayedTaskWithTimeout::RunIfNeeded(bigtime_t currentTime)
206 if (currentTime < fRunAfter)
207 return false;
209 fRunAfter = currentTime + fPeriod;
210 (*fFunctor)();
211 if (fFunctor->Result())
212 return true;
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),
226 fIdleFor(idleFor),
227 fState(kInitialDelay),
228 fActivityLevelStart(0),
229 fActivityLevel(0),
230 fLastCPUTooBusyTime(0)
235 RunWhenIdleTask::~RunWhenIdleTask()
240 bool
241 RunWhenIdleTask::RunIfNeeded(bigtime_t currentTime)
243 if (currentTime < fRunAfter)
244 return false;
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;
258 (*fFunctor)();
259 return fFunctor->Result();
262 return false;
266 void
267 RunWhenIdleTask::ResetIdleTimer(bigtime_t currentTime)
269 fActivityLevel = ActivityLevel();
270 fActivityLevelStart = currentTime;
271 fLastCPUTooBusyTime = currentTime;
272 fState = kInitialIdleWait;
276 bool
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;
288 bool idle = true;
290 if (load > kIdleTreshold) {
291 // PRINT(("not idle enough %f\n", load));
292 idle = false;
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,
297 // idle_time()));
298 idle = false;
301 #if xDEBUG
302 else
303 PRINT(("load %f, idle for %Ld sec, go\n", load,
304 (currentTime - fLastCPUTooBusyTime) / 1000000));
305 #endif
307 return idle;
311 bool
312 RunWhenIdleTask::IdleTimerExpired(bigtime_t currentTime)
314 return IsIdle(currentTime, 0);
318 bool
319 RunWhenIdleTask::StillIdle(bigtime_t currentTime)
321 return IsIdle(currentTime, kIdleTreshold);
325 // #pragma mark - TaskLoop
328 TaskLoop::TaskLoop(bigtime_t heartBeat)
330 fTaskList(10, true),
331 fHeartBeat(heartBeat)
336 TaskLoop::~TaskLoop()
341 void
342 TaskLoop::RunLater(DelayedTask* task)
344 AddTask(task);
348 void
349 TaskLoop::RunLater(FunctionObject* functor, bigtime_t delay)
351 RunLater(new OneShotDelayedTask(functor, delay));
355 void
356 TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor,
357 bigtime_t delay, bigtime_t period)
359 RunLater(new PeriodicDelayedTask(functor, delay, period));
363 void
364 TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor, bigtime_t delay,
365 bigtime_t period, bigtime_t timeout)
367 RunLater(new PeriodicDelayedTaskWithTimeout(functor, delay, period,
368 timeout));
372 void
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
383 void
384 TaskLoop::AccumulatedRunLater(AccumulatingFunctionObject* functor,
385 bigtime_t delay, bigtime_t maxAccumulatingTime, int32 maxAccumulateCount)
387 AutoLock<BLocker> autoLock(&fLock);
388 if (!autoLock.IsLocked())
389 return;
391 int32 count = fTaskList.CountItems();
392 for (int32 index = 0; index < count; index++) {
393 AccumulatedOneShotDelayedTask* task
394 = dynamic_cast<AccumulatedOneShotDelayedTask*>(
395 fTaskList.ItemAt(index));
396 if (task == NULL)
397 continue;
398 else if (task->CanAccumulate(functor)) {
399 task->Accumulate(functor, delay);
400 return;
404 RunLater(new AccumulatedOneShotDelayedTask(functor, delay,
405 maxAccumulatingTime, maxAccumulateCount));
409 bool
410 TaskLoop::Pulse()
412 ASSERT(fLock.IsLocked());
414 int32 count = fTaskList.CountItems();
415 if (count > 0) {
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
422 RemoveTask(task);
423 count--;
424 } else
425 index++;
428 return count == 0 && !KeepPulsingWhenEmpty();
432 bigtime_t
433 TaskLoop::LatestRunTime() const
435 ASSERT(fLock.IsLocked());
436 bigtime_t result = kInfinity;
438 #if xDEBUG
439 DelayedTask* nextTask = 0;
440 #endif
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) {
445 result = runAfter;
447 #if xDEBUG
448 nextTask = fTaskList.ItemAt(index);
449 #endif
454 #if xDEBUG
455 if (nextTask)
456 PRINT(("latestRunTime : next task %s\n", typeid(*nextTask).name));
457 else
458 PRINT(("latestRunTime : no next task\n"));
459 #endif
461 return result;
465 void
466 TaskLoop::RemoveTask(DelayedTask* task)
468 ASSERT(fLock.IsLocked());
469 // remove the task
470 fTaskList.RemoveItem(task);
473 void
474 TaskLoop::AddTask(DelayedTask* task)
476 AutoLock<BLocker> autoLock(&fLock);
477 if (!autoLock.IsLocked()) {
478 delete task;
479 return;
482 fTaskList.AddItem(task);
483 StartPulsingIfNeeded();
487 // #pragma mark - StandAloneTaskLoop
490 StandAloneTaskLoop::StandAloneTaskLoop(bool keepThread, bigtime_t heartBeat)
492 TaskLoop(heartBeat),
493 fNeedToQuit(false),
494 fScanThread(-1),
495 fKeepThread(keepThread)
500 StandAloneTaskLoop::~StandAloneTaskLoop()
502 fLock.Lock();
503 fNeedToQuit = true;
504 bool easyOut = (fScanThread == -1);
505 fLock.Unlock();
507 if (!easyOut)
508 for (int32 timeout = 10000; ; timeout--) {
509 // use a 10 sec timeout value in case the spawned
510 // thread is stuck somewhere
512 if (!timeout) {
513 PRINT(("StandAloneTaskLoop timed out, quitting abruptly"));
514 break;
517 bool done;
519 fLock.Lock();
520 done = (fScanThread == -1);
521 fLock.Unlock();
522 if (done)
523 break;
525 snooze(1000);
530 void
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);
543 bool
544 StandAloneTaskLoop::KeepPulsingWhenEmpty() const
546 return fKeepThread;
550 status_t
551 StandAloneTaskLoop::RunBinder(void* castToThis)
553 StandAloneTaskLoop* self = (StandAloneTaskLoop*)castToThis;
554 self->Run();
555 return B_OK;
559 void
560 StandAloneTaskLoop::Run()
562 for(;;) {
563 AutoLock<BLocker> autoLock(&fLock);
564 if (!autoLock)
565 return;
567 if (fNeedToQuit) {
568 // task loop being deleted, let go of the thread allowing the
569 // to go through deletion
570 fScanThread = -1;
571 return;
574 if (Pulse()) {
575 fScanThread = -1;
576 return;
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
581 // time
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;
588 autoLock.Unlock();
590 if (snoozeTill > now)
591 snooze_until(snoozeTill, B_SYSTEM_TIMEBASE);
592 else
593 snooze(1000);
598 void
599 StandAloneTaskLoop::AddTask(DelayedTask* delayedTask)
601 _inherited::AddTask(delayedTask);
602 if (fScanThread < 0)
603 return;
605 // wake up the loop thread if it is asleep
606 thread_info info;
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)
621 TaskLoop(heartBeat),
622 fNextHeartBeatTime(0),
623 fPulseMe(false)
628 PiggybackTaskLoop::~PiggybackTaskLoop()
633 void
634 PiggybackTaskLoop::PulseMe()
636 if (!fPulseMe)
637 return;
639 bigtime_t time = system_time();
640 if (fNextHeartBeatTime < time) {
641 AutoLock<BLocker> autoLock(&fLock);
642 if (Pulse())
643 fPulseMe = false;
644 fNextHeartBeatTime = time + fHeartBeat;
649 bool
650 PiggybackTaskLoop::KeepPulsingWhenEmpty() const
652 return false;
656 void
657 PiggybackTaskLoop::StartPulsingIfNeeded()
659 fPulseMe = true;