1/* 2Open Tracker License 3 4Terms and Conditions 5 6Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8Permission is hereby granted, free of charge, to any person obtaining a copy of 9this software and associated documentation files (the "Software"), to deal in 10the Software without restriction, including without limitation the rights to 11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12of the Software, and to permit persons to whom the Software is furnished to do 13so, subject to the following conditions: 14 15The above copyright notice and this permission notice applies to all licensees 16and shall be included in all copies or substantial portions of the Software. 17 18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25Except as contained in this notice, the name of Be Incorporated shall not be 26used in advertising or otherwise to promote the sale, use or other dealings in 27this Software without prior written authorization from Be Incorporated. 28 29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30of Be Incorporated in the United States and other countries. Other brand product 31names are registered trademarks or trademarks of their respective holders. 32All rights reserved. 33*/ 34 35#include <Debug.h> 36#include <InterfaceDefs.h> 37 38#include "AutoLock.h" 39#include "TaskLoop.h" 40 41 42DelayedTask::DelayedTask(bigtime_t delay) 43 : fRunAfter(system_time() + delay) 44{ 45} 46 47DelayedTask::~DelayedTask() 48{ 49} 50 51OneShotDelayedTask::OneShotDelayedTask(FunctionObject* functor, 52 bigtime_t delay) 53 : DelayedTask(delay), 54 fFunctor(functor) 55{ 56} 57 58 59OneShotDelayedTask::~OneShotDelayedTask() 60{ 61 delete fFunctor; 62} 63 64 65bool 66OneShotDelayedTask::RunIfNeeded(bigtime_t currentTime) 67{ 68 if (currentTime < fRunAfter) 69 return false; 70 71 (*fFunctor)(); 72 return true; 73} 74 75 76PeriodicDelayedTask::PeriodicDelayedTask( 77 FunctionObjectWithResult<bool>* functor, bigtime_t initialDelay, 78 bigtime_t period) 79 : DelayedTask(initialDelay), 80 fPeriod(period), 81 fFunctor(functor) 82{ 83} 84 85 86PeriodicDelayedTask::~PeriodicDelayedTask() 87{ 88 delete fFunctor; 89} 90 91 92bool 93PeriodicDelayedTask::RunIfNeeded(bigtime_t currentTime) 94{ 95 if (!currentTime < fRunAfter) 96 return false; 97 98 fRunAfter = currentTime + fPeriod; 99 (*fFunctor)(); 100 return fFunctor->Result(); 101} 102 103 104PeriodicDelayedTaskWithTimeout::PeriodicDelayedTaskWithTimeout( 105 FunctionObjectWithResult<bool>* functor, bigtime_t initialDelay, 106 bigtime_t period, bigtime_t timeout) 107 : PeriodicDelayedTask(functor, initialDelay, period), 108 fTimeoutAfter(system_time() + timeout) 109{ 110} 111 112 113bool 114PeriodicDelayedTaskWithTimeout::RunIfNeeded(bigtime_t currentTime) 115{ 116 if (currentTime < fRunAfter) 117 return false; 118 119 fRunAfter = currentTime + fPeriod; 120 (*fFunctor)(); 121 if (fFunctor->Result()) 122 return true; 123 124 // if call didn't terminate the task yet, check if timeout is due 125 return currentTime > fTimeoutAfter; 126} 127 128 129RunWhenIdleTask::RunWhenIdleTask(FunctionObjectWithResult<bool>* functor, 130 bigtime_t initialDelay, bigtime_t idleFor, bigtime_t heartBeat) 131 : PeriodicDelayedTask(functor, initialDelay, heartBeat), 132 fIdleFor(idleFor), 133 fState(kInitialDelay) 134{ 135} 136 137 138RunWhenIdleTask::~RunWhenIdleTask() 139{ 140} 141 142 143bool 144RunWhenIdleTask::RunIfNeeded(bigtime_t currentTime) 145{ 146 if (currentTime < fRunAfter) 147 return false; 148 149 fRunAfter = currentTime + fPeriod; 150 // PRINT(("runWhenIdle: runAfter %Ld, current time %Ld, period %Ld\n", 151 // fRunAfter, currentTime, fPeriod)); 152 153 if (fState == kInitialDelay) { 154// PRINT(("run when idle task - past intial delay\n")); 155 ResetIdleTimer(currentTime); 156 } else if (fState == kInIdleState && !StillIdle(currentTime)) { 157 fState = kInitialIdleWait; 158 ResetIdleTimer(currentTime); 159 } else if (fState != kInitialIdleWait || IdleTimerExpired(currentTime)) { 160 fState = kInIdleState; 161 (*fFunctor)(); 162 return fFunctor->Result(); 163 } 164 return false; 165} 166 167 168static bigtime_t 169ActivityLevel() 170{ 171 // stolen from roster server 172 bigtime_t time = 0; 173 system_info sinfo; 174 get_system_info(&sinfo); 175 for (int32 index = 0; index < sinfo.cpu_count; index++) 176 time += sinfo.cpu_infos[index].active_time; 177 return time / ((bigtime_t) sinfo.cpu_count); 178} 179 180 181void 182RunWhenIdleTask::ResetIdleTimer(bigtime_t currentTime) 183{ 184 fActivityLevel = ActivityLevel(); 185 fActivityLevelStart = currentTime; 186 fLastCPUTooBusyTime = currentTime; 187 fState = kInitialIdleWait; 188} 189 190const float kTaskOverhead = 0.01f; 191 // this should really be specified by the task itself 192const float kIdleTreshold = 0.15f; 193 194bool 195RunWhenIdleTask::IsIdle(bigtime_t currentTime, float taskOverhead) 196{ 197 bigtime_t currentActivityLevel = ActivityLevel(); 198 float load = (float)(currentActivityLevel - fActivityLevel) 199 / (float)(currentTime - fActivityLevelStart); 200 201 fActivityLevel = currentActivityLevel; 202 fActivityLevelStart = currentTime; 203 204 load -= taskOverhead; 205 206 bool idle = true; 207 208 if (load > kIdleTreshold) { 209// PRINT(("not idle enough %f\n", load)); 210 idle = false; 211 } else if ((currentTime - fLastCPUTooBusyTime) < fIdleFor 212 || idle_time() < fIdleFor) { 213// PRINT(("load %f, not idle long enough %Ld, %Ld\n", load, 214// currentTime - fLastCPUTooBusyTime, 215// idle_time())); 216 idle = false; 217 } 218 219#if xDEBUG 220 else 221 PRINT(("load %f, idle for %Ld sec, go\n", load, 222 (currentTime - fLastCPUTooBusyTime) / 1000000)); 223#endif 224 225 return idle; 226} 227 228 229bool 230RunWhenIdleTask::IdleTimerExpired(bigtime_t currentTime) 231{ 232 return IsIdle(currentTime, 0); 233} 234 235 236bool 237RunWhenIdleTask::StillIdle(bigtime_t currentTime) 238{ 239 return IsIdle(currentTime, kIdleTreshold); 240} 241 242 243TaskLoop::TaskLoop(bigtime_t heartBeat) 244 : fTaskList(10, true), 245 fHeartBeat(heartBeat) 246{ 247} 248 249 250TaskLoop::~TaskLoop() 251{ 252} 253 254 255void 256TaskLoop::RunLater(DelayedTask* task) 257{ 258 AddTask(task); 259} 260 261 262void 263TaskLoop::RunLater(FunctionObject* functor, bigtime_t delay) 264{ 265 RunLater(new OneShotDelayedTask(functor, delay)); 266} 267 268 269void 270TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor, 271 bigtime_t delay, bigtime_t period) 272{ 273 RunLater(new PeriodicDelayedTask(functor, delay, period)); 274} 275 276 277void 278TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor, bigtime_t delay, 279 bigtime_t period, bigtime_t timeout) 280{ 281 RunLater(new PeriodicDelayedTaskWithTimeout(functor, delay, period, 282 timeout)); 283} 284 285 286void 287TaskLoop::RunWhenIdle(FunctionObjectWithResult<bool>* functor, 288 bigtime_t initialDelay, bigtime_t idleTime, bigtime_t heartBeat) 289{ 290 RunLater(new RunWhenIdleTask(functor, initialDelay, idleTime, heartBeat)); 291} 292 293 294class AccumulatedOneShotDelayedTask : public OneShotDelayedTask { 295 // supports accumulating functors 296public: 297 AccumulatedOneShotDelayedTask(AccumulatingFunctionObject* functor, 298 bigtime_t delay, bigtime_t maxAccumulatingTime = 0, 299 int32 maxAccumulateCount = 0) 300 : OneShotDelayedTask(functor, delay), 301 maxAccumulateCount(maxAccumulateCount), 302 accumulateCount(1), 303 maxAccumulatingTime(maxAccumulatingTime), 304 initialTime(system_time()) 305 {} 306 307 bool CanAccumulate(const AccumulatingFunctionObject* accumulateThis) const 308 { 309 if (maxAccumulateCount && accumulateCount > maxAccumulateCount) 310 // don't accumulate if too may accumulated already 311 return false; 312 313 if (maxAccumulatingTime && system_time() > initialTime 314 + maxAccumulatingTime) { 315 // don't accumulate if too late past initial task 316 return false; 317 } 318 319 return static_cast<AccumulatingFunctionObject*>(fFunctor)-> 320 CanAccumulate(accumulateThis); 321 } 322 323 virtual void Accumulate(AccumulatingFunctionObject* accumulateThis, 324 bigtime_t delay) 325 { 326 fRunAfter = system_time() + delay; 327 // reset fRunAfter 328 accumulateCount++; 329 static_cast<AccumulatingFunctionObject*>(fFunctor)-> 330 Accumulate(accumulateThis); 331 } 332 333private: 334 int32 maxAccumulateCount; 335 int32 accumulateCount; 336 bigtime_t maxAccumulatingTime; 337 bigtime_t initialTime; 338}; 339 340void 341TaskLoop::AccumulatedRunLater(AccumulatingFunctionObject* functor, 342 bigtime_t delay, bigtime_t maxAccumulatingTime, int32 maxAccumulateCount) 343{ 344 AutoLock<BLocker> autoLock(&fLock); 345 if (!autoLock.IsLocked()) { 346 return; 347 } 348 int32 count = fTaskList.CountItems(); 349 for (int32 index = 0; index < count; index++) { 350 AccumulatedOneShotDelayedTask* task 351 = dynamic_cast<AccumulatedOneShotDelayedTask*>( 352 fTaskList.ItemAt(index)); 353 if (!task) 354 continue; 355 356 if (task->CanAccumulate(functor)) { 357 task->Accumulate(functor, delay); 358 return; 359 } 360 } 361 RunLater(new AccumulatedOneShotDelayedTask(functor, delay, 362 maxAccumulatingTime, maxAccumulateCount)); 363} 364 365 366bool 367TaskLoop::Pulse() 368{ 369 ASSERT(fLock.IsLocked()); 370 371 int32 count = fTaskList.CountItems(); 372 if (count > 0) { 373 bigtime_t currentTime = system_time(); 374 for (int32 index = 0; index < count; ) { 375 DelayedTask* task = fTaskList.ItemAt(index); 376 // give every task a try 377 if (task->RunIfNeeded(currentTime)) { 378 // if done, remove from list 379 RemoveTask(task); 380 count--; 381 } else 382 index++; 383 } 384 } 385 return count == 0 && !KeepPulsingWhenEmpty(); 386} 387 388const bigtime_t kInfinity = B_INFINITE_TIMEOUT; 389 390bigtime_t 391TaskLoop::LatestRunTime() const 392{ 393 ASSERT(fLock.IsLocked()); 394 bigtime_t result = kInfinity; 395 396#if xDEBUG 397 DelayedTask* nextTask = 0; 398#endif 399 int32 count = fTaskList.CountItems(); 400 for (int32 index = 0; index < count; index++) { 401 bigtime_t runAfter = fTaskList.ItemAt(index)->RunAfterTime(); 402 if (runAfter < result) { 403 result = runAfter; 404 405#if xDEBUG 406 nextTask = fTaskList.ItemAt(index); 407#endif 408 } 409 } 410 411 412#if xDEBUG 413 if (nextTask) 414 PRINT(("latestRunTime : next task %s\n", typeid(*nextTask).name)); 415 else 416 PRINT(("latestRunTime : no next task\n")); 417#endif 418 419 return result; 420} 421 422 423void 424TaskLoop::RemoveTask(DelayedTask* task) 425{ 426 ASSERT(fLock.IsLocked()); 427 // remove the task 428 fTaskList.RemoveItem(task); 429} 430 431void 432TaskLoop::AddTask(DelayedTask* task) 433{ 434 AutoLock<BLocker> autoLock(&fLock); 435 if (!autoLock.IsLocked()) { 436 delete task; 437 return; 438 } 439 440 fTaskList.AddItem(task); 441 StartPulsingIfNeeded(); 442} 443 444 445StandAloneTaskLoop::StandAloneTaskLoop(bool keepThread, bigtime_t heartBeat) 446 : TaskLoop(heartBeat), 447 fNeedToQuit(false), 448 fScanThread(-1), 449 fKeepThread(keepThread) 450{ 451} 452 453 454StandAloneTaskLoop::~StandAloneTaskLoop() 455{ 456 fLock.Lock(); 457 fNeedToQuit = true; 458 bool easyOut = (fScanThread == -1); 459 fLock.Unlock(); 460 461 if (!easyOut) 462 for (int32 timeout = 10000; ; timeout--) { 463 // use a 10 sec timeout value in case the spawned 464 // thread is stuck somewhere 465 466 if (!timeout) { 467 PRINT(("StandAloneTaskLoop timed out, quitting abruptly")); 468 break; 469 } 470 471 bool done; 472 473 fLock.Lock(); 474 done = (fScanThread == -1); 475 fLock.Unlock(); 476 if (done) 477 break; 478 479 snooze(1000); 480 } 481} 482 483void 484StandAloneTaskLoop::StartPulsingIfNeeded() 485{ 486 ASSERT(fLock.IsLocked()); 487 if (fScanThread < 0) { 488 // no loop thread yet, spawn one 489 fScanThread = spawn_thread(StandAloneTaskLoop::RunBinder, 490 "TrackerTaskLoop", B_LOW_PRIORITY, this); 491 resume_thread(fScanThread); 492 } 493} 494 495bool 496StandAloneTaskLoop::KeepPulsingWhenEmpty() const 497{ 498 return fKeepThread; 499} 500 501status_t 502StandAloneTaskLoop::RunBinder(void* castToThis) 503{ 504 StandAloneTaskLoop* self = (StandAloneTaskLoop*)castToThis; 505 self->Run(); 506 return B_OK; 507} 508 509void 510StandAloneTaskLoop::Run() 511{ 512 for(;;) { 513 AutoLock<BLocker> autoLock(&fLock); 514 if (!autoLock) 515 return; 516 517 if (fNeedToQuit) { 518 // task loop being deleted, let go of the thread allowing the 519 // to go through deletion 520 fScanThread = -1; 521 return; 522 } 523 524 if (Pulse()) { 525 fScanThread = -1; 526 return; 527 } 528 529 // figure out when to run next by checking out when the different 530 // tasks wan't to be woken up, snooze until a little bit before that 531 // time 532 bigtime_t now = system_time(); 533 bigtime_t latestRunTime = LatestRunTime() - 1000; 534 bigtime_t afterHeartBeatTime = now + fHeartBeat; 535 bigtime_t snoozeTill = latestRunTime < afterHeartBeatTime ? 536 latestRunTime : afterHeartBeatTime; 537 538 autoLock.Unlock(); 539 540 if (snoozeTill > now) 541 snooze_until(snoozeTill, B_SYSTEM_TIMEBASE); 542 else 543 snooze(1000); 544 } 545} 546 547void 548StandAloneTaskLoop::AddTask(DelayedTask* delayedTask) 549{ 550 _inherited::AddTask(delayedTask); 551 if (fScanThread < 0) 552 return; 553 554 // wake up the loop thread if it is asleep 555 thread_info info; 556 get_thread_info(fScanThread, &info); 557 if (info.state == B_THREAD_ASLEEP) { 558 suspend_thread(fScanThread); 559 snooze(1000); // snooze because BeBook sez so 560 resume_thread(fScanThread); 561 } 562} 563 564PiggybackTaskLoop::PiggybackTaskLoop(bigtime_t heartBeat) 565 : TaskLoop(heartBeat), 566 fNextHeartBeatTime(0), 567 fPulseMe(false) 568{ 569} 570 571 572PiggybackTaskLoop::~PiggybackTaskLoop() 573{ 574} 575 576void 577PiggybackTaskLoop::PulseMe() 578{ 579 if (!fPulseMe) 580 return; 581 582 bigtime_t time = system_time(); 583 if (fNextHeartBeatTime < time) { 584 AutoLock<BLocker> autoLock(&fLock); 585 if (Pulse()) 586 fPulseMe = false; 587 fNextHeartBeatTime = time + fHeartBeat; 588 } 589} 590 591bool 592PiggybackTaskLoop::KeepPulsingWhenEmpty() const 593{ 594 return false; 595} 596 597void 598PiggybackTaskLoop::StartPulsingIfNeeded() 599{ 600 fPulseMe = true; 601} 602