1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 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: 14 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. 17 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. 24 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. 28 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. 33 */ 34 35 #include <Debug.h> 36 #include <InterfaceDefs.h> 37 38 #include "AutoLock.h" 39 #include "TaskLoop.h" 40 41 42 const float kIdleTreshold = 0.15f; 43 44 const bigtime_t kInfinity = B_INFINITE_TIMEOUT; 45 46 47 static bigtime_t 48 ActivityLevel() 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 66 class AccumulatedOneShotDelayedTask : public OneShotDelayedTask { 67 // supports accumulating functors 68 public: 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 107 private: 108 int32 maxAccumulateCount; 109 int32 accumulateCount; 110 bigtime_t maxAccumulatingTime; 111 bigtime_t initialTime; 112 }; 113 114 115 // #pragma mark - DelayedTask 116 117 118 DelayedTask::DelayedTask(bigtime_t delay) 119 : 120 fRunAfter(system_time() + delay) 121 { 122 } 123 124 125 DelayedTask::~DelayedTask() 126 { 127 } 128 129 130 // #pragma mark - OneShotDelayedTask 131 132 133 OneShotDelayedTask::OneShotDelayedTask(FunctionObject* functor, 134 bigtime_t delay) 135 : 136 DelayedTask(delay), 137 fFunctor(functor) 138 { 139 } 140 141 142 OneShotDelayedTask::~OneShotDelayedTask() 143 { 144 delete fFunctor; 145 } 146 147 148 bool 149 OneShotDelayedTask::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 162 PeriodicDelayedTask::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 173 PeriodicDelayedTask::~PeriodicDelayedTask() 174 { 175 delete fFunctor; 176 } 177 178 179 bool 180 PeriodicDelayedTask::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 191 PeriodicDelayedTaskWithTimeout::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 201 bool 202 PeriodicDelayedTaskWithTimeout::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 220 RunWhenIdleTask::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 233 RunWhenIdleTask::~RunWhenIdleTask() 234 { 235 } 236 237 238 bool 239 RunWhenIdleTask::RunIfNeeded(bigtime_t currentTime) 240 { 241 if (currentTime < fRunAfter) 242 return false; 243 244 fRunAfter = currentTime + fPeriod; 245 // PRINT(("runWhenIdle: runAfter %Ld, current time %Ld, period %Ld\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 264 void 265 RunWhenIdleTask::ResetIdleTimer(bigtime_t currentTime) 266 { 267 fActivityLevel = ActivityLevel(); 268 fActivityLevelStart = currentTime; 269 fLastCPUTooBusyTime = currentTime; 270 fState = kInitialIdleWait; 271 } 272 273 274 bool 275 RunWhenIdleTask::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 %Ld, %Ld\n", load, 294 // currentTime - fLastCPUTooBusyTime, 295 // idle_time())); 296 idle = false; 297 } 298 299 #if xDEBUG 300 else 301 PRINT(("load %f, idle for %Ld sec, go\n", load, 302 (currentTime - fLastCPUTooBusyTime) / 1000000)); 303 #endif 304 305 return idle; 306 } 307 308 309 bool 310 RunWhenIdleTask::IdleTimerExpired(bigtime_t currentTime) 311 { 312 return IsIdle(currentTime, 0); 313 } 314 315 316 bool 317 RunWhenIdleTask::StillIdle(bigtime_t currentTime) 318 { 319 return IsIdle(currentTime, kIdleTreshold); 320 } 321 322 323 // #pragma mark - TaskLoop 324 325 326 TaskLoop::TaskLoop(bigtime_t heartBeat) 327 : 328 fTaskList(10, true), 329 fHeartBeat(heartBeat) 330 { 331 } 332 333 334 TaskLoop::~TaskLoop() 335 { 336 } 337 338 339 void 340 TaskLoop::RunLater(DelayedTask* task) 341 { 342 AddTask(task); 343 } 344 345 346 void 347 TaskLoop::RunLater(FunctionObject* functor, bigtime_t delay) 348 { 349 RunLater(new OneShotDelayedTask(functor, delay)); 350 } 351 352 353 void 354 TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor, 355 bigtime_t delay, bigtime_t period) 356 { 357 RunLater(new PeriodicDelayedTask(functor, delay, period)); 358 } 359 360 361 void 362 TaskLoop::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 370 void 371 TaskLoop::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 381 void 382 TaskLoop::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 407 bool 408 TaskLoop::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 430 bigtime_t 431 TaskLoop::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 463 void 464 TaskLoop::RemoveTask(DelayedTask* task) 465 { 466 ASSERT(fLock.IsLocked()); 467 // remove the task 468 fTaskList.RemoveItem(task); 469 } 470 471 void 472 TaskLoop::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 488 StandAloneTaskLoop::StandAloneTaskLoop(bool keepThread, bigtime_t heartBeat) 489 : 490 TaskLoop(heartBeat), 491 fNeedToQuit(false), 492 fScanThread(-1), 493 fKeepThread(keepThread) 494 { 495 } 496 497 498 StandAloneTaskLoop::~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 528 void 529 StandAloneTaskLoop::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 541 bool 542 StandAloneTaskLoop::KeepPulsingWhenEmpty() const 543 { 544 return fKeepThread; 545 } 546 547 548 status_t 549 StandAloneTaskLoop::RunBinder(void* castToThis) 550 { 551 StandAloneTaskLoop* self = (StandAloneTaskLoop*)castToThis; 552 self->Run(); 553 return B_OK; 554 } 555 556 557 void 558 StandAloneTaskLoop::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 596 void 597 StandAloneTaskLoop::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 617 PiggybackTaskLoop::PiggybackTaskLoop(bigtime_t heartBeat) 618 : 619 TaskLoop(heartBeat), 620 fNextHeartBeatTime(0), 621 fPulseMe(false) 622 { 623 } 624 625 626 PiggybackTaskLoop::~PiggybackTaskLoop() 627 { 628 } 629 630 631 void 632 PiggybackTaskLoop::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 647 bool 648 PiggybackTaskLoop::KeepPulsingWhenEmpty() const 649 { 650 return false; 651 } 652 653 654 void 655 PiggybackTaskLoop::StartPulsingIfNeeded() 656 { 657 fPulseMe = true; 658 } 659