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 42const float kIdleTreshold = 0.15f; 43 44const bigtime_t kInfinity = B_INFINITE_TIMEOUT; 45 46 47static bigtime_t 48ActivityLevel() 49{ 50 // stolen from roster server 51 bigtime_t time = 0; 52 system_info sinfo; 53 get_system_info(&sinfo); 54 55 cpu_info* cpuInfos = new cpu_info[sinfo.cpu_count]; 56 get_cpu_info(0, sinfo.cpu_count, cpuInfos); 57 58 for (uint32 index = 0; index < sinfo.cpu_count; index++) 59 time += cpuInfos[index].active_time; 60 61 delete[] cpuInfos; 62 return time / ((bigtime_t) sinfo.cpu_count); 63} 64 65 66class AccumulatedOneShotDelayedTask : public OneShotDelayedTask { 67 // supports accumulating functors 68public: 69 AccumulatedOneShotDelayedTask(AccumulatingFunctionObject* functor, 70 bigtime_t delay, bigtime_t maxAccumulatingTime = 0, 71 int32 maxAccumulateCount = 0) 72 : 73 OneShotDelayedTask(functor, delay), 74 maxAccumulateCount(maxAccumulateCount), 75 accumulateCount(1), 76 maxAccumulatingTime(maxAccumulatingTime), 77 initialTime(system_time()) 78 { 79 } 80 81 bool CanAccumulate(const AccumulatingFunctionObject* accumulateThis) const 82 { 83 if (maxAccumulateCount && accumulateCount > maxAccumulateCount) 84 // don't accumulate if too may accumulated already 85 return false; 86 87 if (maxAccumulatingTime && system_time() > initialTime 88 + maxAccumulatingTime) { 89 // don't accumulate if too late past initial task 90 return false; 91 } 92 93 return static_cast<AccumulatingFunctionObject*>(fFunctor)-> 94 CanAccumulate(accumulateThis); 95 } 96 97 virtual void Accumulate(AccumulatingFunctionObject* accumulateThis, 98 bigtime_t delay) 99 { 100 fRunAfter = system_time() + delay; 101 // reset fRunAfter 102 accumulateCount++; 103 static_cast<AccumulatingFunctionObject*>(fFunctor)-> 104 Accumulate(accumulateThis); 105 } 106 107private: 108 int32 maxAccumulateCount; 109 int32 accumulateCount; 110 bigtime_t maxAccumulatingTime; 111 bigtime_t initialTime; 112}; 113 114 115// #pragma mark - DelayedTask 116 117 118DelayedTask::DelayedTask(bigtime_t delay) 119 : 120 fRunAfter(system_time() + delay) 121{ 122} 123 124 125DelayedTask::~DelayedTask() 126{ 127} 128 129 130// #pragma mark - OneShotDelayedTask 131 132 133OneShotDelayedTask::OneShotDelayedTask(FunctionObject* functor, 134 bigtime_t delay) 135 : 136 DelayedTask(delay), 137 fFunctor(functor) 138{ 139} 140 141 142OneShotDelayedTask::~OneShotDelayedTask() 143{ 144 delete fFunctor; 145} 146 147 148bool 149OneShotDelayedTask::RunIfNeeded(bigtime_t currentTime) 150{ 151 if (currentTime < fRunAfter) 152 return false; 153 154 (*fFunctor)(); 155 return true; 156} 157 158 159// #pragma mark - PeriodicDelayedTask 160 161 162PeriodicDelayedTask::PeriodicDelayedTask( 163 FunctionObjectWithResult<bool>* functor, bigtime_t initialDelay, 164 bigtime_t period) 165 : 166 DelayedTask(initialDelay), 167 fPeriod(period), 168 fFunctor(functor) 169{ 170} 171 172 173PeriodicDelayedTask::~PeriodicDelayedTask() 174{ 175 delete fFunctor; 176} 177 178 179bool 180PeriodicDelayedTask::RunIfNeeded(bigtime_t currentTime) 181{ 182 if (currentTime < fRunAfter) 183 return false; 184 185 fRunAfter = currentTime + fPeriod; 186 (*fFunctor)(); 187 return fFunctor->Result(); 188} 189 190 191PeriodicDelayedTaskWithTimeout::PeriodicDelayedTaskWithTimeout( 192 FunctionObjectWithResult<bool>* functor, bigtime_t initialDelay, 193 bigtime_t period, bigtime_t timeout) 194 : 195 PeriodicDelayedTask(functor, initialDelay, period), 196 fTimeoutAfter(system_time() + timeout) 197{ 198} 199 200 201bool 202PeriodicDelayedTaskWithTimeout::RunIfNeeded(bigtime_t currentTime) 203{ 204 if (currentTime < fRunAfter) 205 return false; 206 207 fRunAfter = currentTime + fPeriod; 208 (*fFunctor)(); 209 if (fFunctor->Result()) 210 return true; 211 212 // if call didn't terminate the task yet, check if timeout is due 213 return currentTime > fTimeoutAfter; 214} 215 216 217// #pragma mark - RunWhenIdleTask 218 219 220RunWhenIdleTask::RunWhenIdleTask(FunctionObjectWithResult<bool>* functor, 221 bigtime_t initialDelay, bigtime_t idleFor, bigtime_t heartBeat) 222 : 223 PeriodicDelayedTask(functor, initialDelay, heartBeat), 224 fIdleFor(idleFor), 225 fState(kInitialDelay), 226 fActivityLevelStart(0), 227 fActivityLevel(0), 228 fLastCPUTooBusyTime(0) 229{ 230} 231 232 233RunWhenIdleTask::~RunWhenIdleTask() 234{ 235} 236 237 238bool 239RunWhenIdleTask::RunIfNeeded(bigtime_t currentTime) 240{ 241 if (currentTime < fRunAfter) 242 return false; 243 244 fRunAfter = currentTime + fPeriod; 245// PRINT(("runWhenIdle: runAfter %lld, current time %lld, period %lld\n", 246// fRunAfter, currentTime, fPeriod)); 247 248 if (fState == kInitialDelay) { 249// PRINT(("run when idle task - past intial delay\n")); 250 ResetIdleTimer(currentTime); 251 } else if (fState == kInIdleState && !StillIdle(currentTime)) { 252 fState = kInitialIdleWait; 253 ResetIdleTimer(currentTime); 254 } else if (fState != kInitialIdleWait || IdleTimerExpired(currentTime)) { 255 fState = kInIdleState; 256 (*fFunctor)(); 257 return fFunctor->Result(); 258 } 259 260 return false; 261} 262 263 264void 265RunWhenIdleTask::ResetIdleTimer(bigtime_t currentTime) 266{ 267 fActivityLevel = ActivityLevel(); 268 fActivityLevelStart = currentTime; 269 fLastCPUTooBusyTime = currentTime; 270 fState = kInitialIdleWait; 271} 272 273 274bool 275RunWhenIdleTask::IsIdle(bigtime_t currentTime, float taskOverhead) 276{ 277 bigtime_t currentActivityLevel = ActivityLevel(); 278 float load = (float)(currentActivityLevel - fActivityLevel) 279 / (float)(currentTime - fActivityLevelStart); 280 281 fActivityLevel = currentActivityLevel; 282 fActivityLevelStart = currentTime; 283 284 load -= taskOverhead; 285 286 bool idle = true; 287 288 if (load > kIdleTreshold) { 289// PRINT(("not idle enough %f\n", load)); 290 idle = false; 291 } else if ((currentTime - fLastCPUTooBusyTime) < fIdleFor 292 || idle_time() < fIdleFor) { 293// PRINT(("load %f, not idle long enough %lld, %lld\n", load, 294// currentTime - fLastCPUTooBusyTime, 295// idle_time())); 296 idle = false; 297 } 298 299#if xDEBUG 300 else 301 PRINT(("load %f, idle for %lld sec, go\n", load, 302 (currentTime - fLastCPUTooBusyTime) / 1000000)); 303#endif 304 305 return idle; 306} 307 308 309bool 310RunWhenIdleTask::IdleTimerExpired(bigtime_t currentTime) 311{ 312 return IsIdle(currentTime, 0); 313} 314 315 316bool 317RunWhenIdleTask::StillIdle(bigtime_t currentTime) 318{ 319 return IsIdle(currentTime, kIdleTreshold); 320} 321 322 323// #pragma mark - TaskLoop 324 325 326TaskLoop::TaskLoop(bigtime_t heartBeat) 327 : 328 fTaskList(10, true), 329 fHeartBeat(heartBeat) 330{ 331} 332 333 334TaskLoop::~TaskLoop() 335{ 336} 337 338 339void 340TaskLoop::RunLater(DelayedTask* task) 341{ 342 AddTask(task); 343} 344 345 346void 347TaskLoop::RunLater(FunctionObject* functor, bigtime_t delay) 348{ 349 RunLater(new OneShotDelayedTask(functor, delay)); 350} 351 352 353void 354TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor, 355 bigtime_t delay, bigtime_t period) 356{ 357 RunLater(new PeriodicDelayedTask(functor, delay, period)); 358} 359 360 361void 362TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor, bigtime_t delay, 363 bigtime_t period, bigtime_t timeout) 364{ 365 RunLater(new PeriodicDelayedTaskWithTimeout(functor, delay, period, 366 timeout)); 367} 368 369 370void 371TaskLoop::RunWhenIdle(FunctionObjectWithResult<bool>* functor, 372 bigtime_t initialDelay, bigtime_t idleTime, bigtime_t heartBeat) 373{ 374 RunLater(new RunWhenIdleTask(functor, initialDelay, idleTime, heartBeat)); 375} 376 377 378// #pragma mark - TaskLoop 379 380 381void 382TaskLoop::AccumulatedRunLater(AccumulatingFunctionObject* functor, 383 bigtime_t delay, bigtime_t maxAccumulatingTime, int32 maxAccumulateCount) 384{ 385 AutoLock<BLocker> autoLock(&fLock); 386 if (!autoLock.IsLocked()) 387 return; 388 389 int32 count = fTaskList.CountItems(); 390 for (int32 index = 0; index < count; index++) { 391 AccumulatedOneShotDelayedTask* task 392 = dynamic_cast<AccumulatedOneShotDelayedTask*>( 393 fTaskList.ItemAt(index)); 394 if (task == NULL) 395 continue; 396 else if (task->CanAccumulate(functor)) { 397 task->Accumulate(functor, delay); 398 return; 399 } 400 } 401 402 RunLater(new AccumulatedOneShotDelayedTask(functor, delay, 403 maxAccumulatingTime, maxAccumulateCount)); 404} 405 406 407bool 408TaskLoop::Pulse() 409{ 410 ASSERT(fLock.IsLocked()); 411 412 int32 count = fTaskList.CountItems(); 413 if (count > 0) { 414 bigtime_t currentTime = system_time(); 415 for (int32 index = 0; index < count; ) { 416 DelayedTask* task = fTaskList.ItemAt(index); 417 // give every task a try 418 if (task->RunIfNeeded(currentTime)) { 419 // if done, remove from list 420 RemoveTask(task); 421 count--; 422 } else 423 index++; 424 } 425 } 426 return count == 0 && !KeepPulsingWhenEmpty(); 427} 428 429 430bigtime_t 431TaskLoop::LatestRunTime() const 432{ 433 ASSERT(fLock.IsLocked()); 434 bigtime_t result = kInfinity; 435 436#if xDEBUG 437 DelayedTask* nextTask = 0; 438#endif 439 int32 count = fTaskList.CountItems(); 440 for (int32 index = 0; index < count; index++) { 441 bigtime_t runAfter = fTaskList.ItemAt(index)->RunAfterTime(); 442 if (runAfter < result) { 443 result = runAfter; 444 445#if xDEBUG 446 nextTask = fTaskList.ItemAt(index); 447#endif 448 } 449 } 450 451 452#if xDEBUG 453 if (nextTask) 454 PRINT(("latestRunTime : next task %s\n", typeid(*nextTask).name)); 455 else 456 PRINT(("latestRunTime : no next task\n")); 457#endif 458 459 return result; 460} 461 462 463void 464TaskLoop::RemoveTask(DelayedTask* task) 465{ 466 ASSERT(fLock.IsLocked()); 467 // remove the task 468 fTaskList.RemoveItem(task); 469} 470 471void 472TaskLoop::AddTask(DelayedTask* task) 473{ 474 AutoLock<BLocker> autoLock(&fLock); 475 if (!autoLock.IsLocked()) { 476 delete task; 477 return; 478 } 479 480 fTaskList.AddItem(task); 481 StartPulsingIfNeeded(); 482} 483 484 485// #pragma mark - StandAloneTaskLoop 486 487 488StandAloneTaskLoop::StandAloneTaskLoop(bool keepThread, bigtime_t heartBeat) 489 : 490 TaskLoop(heartBeat), 491 fNeedToQuit(false), 492 fScanThread(-1), 493 fKeepThread(keepThread) 494{ 495} 496 497 498StandAloneTaskLoop::~StandAloneTaskLoop() 499{ 500 fLock.Lock(); 501 fNeedToQuit = true; 502 bool easyOut = (fScanThread == -1); 503 fLock.Unlock(); 504 505 if (!easyOut) 506 for (int32 timeout = 10000; ; timeout--) { 507 // use a 10 sec timeout value in case the spawned 508 // thread is stuck somewhere 509 510 if (!timeout) { 511 PRINT(("StandAloneTaskLoop timed out, quitting abruptly")); 512 break; 513 } 514 515 bool done; 516 517 fLock.Lock(); 518 done = (fScanThread == -1); 519 fLock.Unlock(); 520 if (done) 521 break; 522 523 snooze(1000); 524 } 525} 526 527 528void 529StandAloneTaskLoop::StartPulsingIfNeeded() 530{ 531 ASSERT(fLock.IsLocked()); 532 if (fScanThread < 0) { 533 // no loop thread yet, spawn one 534 fScanThread = spawn_thread(StandAloneTaskLoop::RunBinder, 535 "TrackerTaskLoop", B_LOW_PRIORITY, this); 536 resume_thread(fScanThread); 537 } 538} 539 540 541bool 542StandAloneTaskLoop::KeepPulsingWhenEmpty() const 543{ 544 return fKeepThread; 545} 546 547 548status_t 549StandAloneTaskLoop::RunBinder(void* castToThis) 550{ 551 StandAloneTaskLoop* self = (StandAloneTaskLoop*)castToThis; 552 self->Run(); 553 return B_OK; 554} 555 556 557void 558StandAloneTaskLoop::Run() 559{ 560 for(;;) { 561 AutoLock<BLocker> autoLock(&fLock); 562 if (!autoLock) 563 return; 564 565 if (fNeedToQuit) { 566 // task loop being deleted, let go of the thread allowing the 567 // to go through deletion 568 fScanThread = -1; 569 return; 570 } 571 572 if (Pulse()) { 573 fScanThread = -1; 574 return; 575 } 576 577 // figure out when to run next by checking out when the different 578 // tasks wan't to be woken up, snooze until a little bit before that 579 // time 580 bigtime_t now = system_time(); 581 bigtime_t latestRunTime = LatestRunTime() - 1000; 582 bigtime_t afterHeartBeatTime = now + fHeartBeat; 583 bigtime_t snoozeTill = latestRunTime < afterHeartBeatTime ? 584 latestRunTime : afterHeartBeatTime; 585 586 autoLock.Unlock(); 587 588 if (snoozeTill > now) 589 snooze_until(snoozeTill, B_SYSTEM_TIMEBASE); 590 else 591 snooze(1000); 592 } 593} 594 595 596void 597StandAloneTaskLoop::AddTask(DelayedTask* delayedTask) 598{ 599 _inherited::AddTask(delayedTask); 600 if (fScanThread < 0) 601 return; 602 603 // wake up the loop thread if it is asleep 604 thread_info info; 605 get_thread_info(fScanThread, &info); 606 if (info.state == B_THREAD_ASLEEP) { 607 suspend_thread(fScanThread); 608 snooze(1000); // snooze because BeBook sez so 609 resume_thread(fScanThread); 610 } 611} 612 613 614// #pragma mark - PiggybackTaskLoop 615 616 617PiggybackTaskLoop::PiggybackTaskLoop(bigtime_t heartBeat) 618 : 619 TaskLoop(heartBeat), 620 fNextHeartBeatTime(0), 621 fPulseMe(false) 622{ 623} 624 625 626PiggybackTaskLoop::~PiggybackTaskLoop() 627{ 628} 629 630 631void 632PiggybackTaskLoop::PulseMe() 633{ 634 if (!fPulseMe) 635 return; 636 637 bigtime_t time = system_time(); 638 if (fNextHeartBeatTime < time) { 639 AutoLock<BLocker> autoLock(&fLock); 640 if (Pulse()) 641 fPulseMe = false; 642 fNextHeartBeatTime = time + fHeartBeat; 643 } 644} 645 646 647bool 648PiggybackTaskLoop::KeepPulsingWhenEmpty() const 649{ 650 return false; 651} 652 653 654void 655PiggybackTaskLoop::StartPulsingIfNeeded() 656{ 657 fPulseMe = true; 658} 659