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 kTaskOverhead = 0.01f; 43 // this should really be specified by the task itself 44 const float kIdleTreshold = 0.15f; 45 46 const bigtime_t kInfinity = B_INFINITE_TIMEOUT; 47 48 49 static bigtime_t 50 ActivityLevel() 51 { 52 // stolen from roster server 53 bigtime_t time = 0; 54 system_info sinfo; 55 get_system_info(&sinfo); 56 57 cpu_info* cpuInfos = new cpu_info[sinfo.cpu_count]; 58 get_cpu_info(0, sinfo.cpu_count, cpuInfos); 59 60 for (uint32 index = 0; index < sinfo.cpu_count; index++) 61 time += cpuInfos[index].active_time; 62 63 delete[] cpuInfos; 64 return time / ((bigtime_t) sinfo.cpu_count); 65 } 66 67 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) 74 : 75 OneShotDelayedTask(functor, delay), 76 maxAccumulateCount(maxAccumulateCount), 77 accumulateCount(1), 78 maxAccumulatingTime(maxAccumulatingTime), 79 initialTime(system_time()) 80 { 81 } 82 83 bool CanAccumulate(const AccumulatingFunctionObject* accumulateThis) const 84 { 85 if (maxAccumulateCount && accumulateCount > maxAccumulateCount) 86 // don't accumulate if too may accumulated already 87 return false; 88 89 if (maxAccumulatingTime && system_time() > initialTime 90 + maxAccumulatingTime) { 91 // don't accumulate if too late past initial task 92 return false; 93 } 94 95 return static_cast<AccumulatingFunctionObject*>(fFunctor)-> 96 CanAccumulate(accumulateThis); 97 } 98 99 virtual void Accumulate(AccumulatingFunctionObject* accumulateThis, 100 bigtime_t delay) 101 { 102 fRunAfter = system_time() + delay; 103 // reset fRunAfter 104 accumulateCount++; 105 static_cast<AccumulatingFunctionObject*>(fFunctor)-> 106 Accumulate(accumulateThis); 107 } 108 109 private: 110 int32 maxAccumulateCount; 111 int32 accumulateCount; 112 bigtime_t maxAccumulatingTime; 113 bigtime_t initialTime; 114 }; 115 116 117 // #pragma mark - DelayedTask 118 119 120 DelayedTask::DelayedTask(bigtime_t delay) 121 : 122 fRunAfter(system_time() + delay) 123 { 124 } 125 126 127 DelayedTask::~DelayedTask() 128 { 129 } 130 131 132 // #pragma mark - OneShotDelayedTask 133 134 135 OneShotDelayedTask::OneShotDelayedTask(FunctionObject* functor, 136 bigtime_t delay) 137 : 138 DelayedTask(delay), 139 fFunctor(functor) 140 { 141 } 142 143 144 OneShotDelayedTask::~OneShotDelayedTask() 145 { 146 delete fFunctor; 147 } 148 149 150 bool 151 OneShotDelayedTask::RunIfNeeded(bigtime_t currentTime) 152 { 153 if (currentTime < fRunAfter) 154 return false; 155 156 (*fFunctor)(); 157 return true; 158 } 159 160 161 // #pragma mark - PeriodicDelayedTask 162 163 164 PeriodicDelayedTask::PeriodicDelayedTask( 165 FunctionObjectWithResult<bool>* functor, bigtime_t initialDelay, 166 bigtime_t period) 167 : 168 DelayedTask(initialDelay), 169 fPeriod(period), 170 fFunctor(functor) 171 { 172 } 173 174 175 PeriodicDelayedTask::~PeriodicDelayedTask() 176 { 177 delete fFunctor; 178 } 179 180 181 bool 182 PeriodicDelayedTask::RunIfNeeded(bigtime_t currentTime) 183 { 184 if (!currentTime < fRunAfter) 185 return false; 186 187 fRunAfter = currentTime + fPeriod; 188 (*fFunctor)(); 189 return fFunctor->Result(); 190 } 191 192 193 PeriodicDelayedTaskWithTimeout::PeriodicDelayedTaskWithTimeout( 194 FunctionObjectWithResult<bool>* functor, bigtime_t initialDelay, 195 bigtime_t period, bigtime_t timeout) 196 : 197 PeriodicDelayedTask(functor, initialDelay, period), 198 fTimeoutAfter(system_time() + timeout) 199 { 200 } 201 202 203 bool 204 PeriodicDelayedTaskWithTimeout::RunIfNeeded(bigtime_t currentTime) 205 { 206 if (currentTime < fRunAfter) 207 return false; 208 209 fRunAfter = currentTime + fPeriod; 210 (*fFunctor)(); 211 if (fFunctor->Result()) 212 return true; 213 214 // if call didn't terminate the task yet, check if timeout is due 215 return currentTime > fTimeoutAfter; 216 } 217 218 219 // #pragma mark - RunWhenIdleTask 220 221 222 RunWhenIdleTask::RunWhenIdleTask(FunctionObjectWithResult<bool>* functor, 223 bigtime_t initialDelay, bigtime_t idleFor, bigtime_t heartBeat) 224 : 225 PeriodicDelayedTask(functor, initialDelay, heartBeat), 226 fIdleFor(idleFor), 227 fState(kInitialDelay), 228 fActivityLevelStart(0), 229 fActivityLevel(0), 230 fLastCPUTooBusyTime(0) 231 { 232 } 233 234 235 RunWhenIdleTask::~RunWhenIdleTask() 236 { 237 } 238 239 240 bool 241 RunWhenIdleTask::RunIfNeeded(bigtime_t currentTime) 242 { 243 if (currentTime < fRunAfter) 244 return false; 245 246 fRunAfter = currentTime + fPeriod; 247 // PRINT(("runWhenIdle: runAfter %Ld, current time %Ld, period %Ld\n", 248 // fRunAfter, currentTime, fPeriod)); 249 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(); 260 } 261 262 return false; 263 } 264 265 266 void 267 RunWhenIdleTask::ResetIdleTimer(bigtime_t currentTime) 268 { 269 fActivityLevel = ActivityLevel(); 270 fActivityLevelStart = currentTime; 271 fLastCPUTooBusyTime = currentTime; 272 fState = kInitialIdleWait; 273 } 274 275 276 bool 277 RunWhenIdleTask::IsIdle(bigtime_t currentTime, float taskOverhead) 278 { 279 bigtime_t currentActivityLevel = ActivityLevel(); 280 float load = (float)(currentActivityLevel - fActivityLevel) 281 / (float)(currentTime - fActivityLevelStart); 282 283 fActivityLevel = currentActivityLevel; 284 fActivityLevelStart = currentTime; 285 286 load -= taskOverhead; 287 288 bool idle = true; 289 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; 299 } 300 301 #if xDEBUG 302 else 303 PRINT(("load %f, idle for %Ld sec, go\n", load, 304 (currentTime - fLastCPUTooBusyTime) / 1000000)); 305 #endif 306 307 return idle; 308 } 309 310 311 bool 312 RunWhenIdleTask::IdleTimerExpired(bigtime_t currentTime) 313 { 314 return IsIdle(currentTime, 0); 315 } 316 317 318 bool 319 RunWhenIdleTask::StillIdle(bigtime_t currentTime) 320 { 321 return IsIdle(currentTime, kIdleTreshold); 322 } 323 324 325 // #pragma mark - TaskLoop 326 327 328 TaskLoop::TaskLoop(bigtime_t heartBeat) 329 : 330 fTaskList(10, true), 331 fHeartBeat(heartBeat) 332 { 333 } 334 335 336 TaskLoop::~TaskLoop() 337 { 338 } 339 340 341 void 342 TaskLoop::RunLater(DelayedTask* task) 343 { 344 AddTask(task); 345 } 346 347 348 void 349 TaskLoop::RunLater(FunctionObject* functor, bigtime_t delay) 350 { 351 RunLater(new OneShotDelayedTask(functor, delay)); 352 } 353 354 355 void 356 TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor, 357 bigtime_t delay, bigtime_t period) 358 { 359 RunLater(new PeriodicDelayedTask(functor, delay, period)); 360 } 361 362 363 void 364 TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor, bigtime_t delay, 365 bigtime_t period, bigtime_t timeout) 366 { 367 RunLater(new PeriodicDelayedTaskWithTimeout(functor, delay, period, 368 timeout)); 369 } 370 371 372 void 373 TaskLoop::RunWhenIdle(FunctionObjectWithResult<bool>* functor, 374 bigtime_t initialDelay, bigtime_t idleTime, bigtime_t heartBeat) 375 { 376 RunLater(new RunWhenIdleTask(functor, initialDelay, idleTime, heartBeat)); 377 } 378 379 380 // #pragma mark - TaskLoop 381 382 383 void 384 TaskLoop::AccumulatedRunLater(AccumulatingFunctionObject* functor, 385 bigtime_t delay, bigtime_t maxAccumulatingTime, int32 maxAccumulateCount) 386 { 387 AutoLock<BLocker> autoLock(&fLock); 388 if (!autoLock.IsLocked()) 389 return; 390 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; 401 } 402 } 403 404 RunLater(new AccumulatedOneShotDelayedTask(functor, delay, 405 maxAccumulatingTime, maxAccumulateCount)); 406 } 407 408 409 bool 410 TaskLoop::Pulse() 411 { 412 ASSERT(fLock.IsLocked()); 413 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++; 426 } 427 } 428 return count == 0 && !KeepPulsingWhenEmpty(); 429 } 430 431 432 bigtime_t 433 TaskLoop::LatestRunTime() const 434 { 435 ASSERT(fLock.IsLocked()); 436 bigtime_t result = kInfinity; 437 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; 446 447 #if xDEBUG 448 nextTask = fTaskList.ItemAt(index); 449 #endif 450 } 451 } 452 453 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 460 461 return result; 462 } 463 464 465 void 466 TaskLoop::RemoveTask(DelayedTask* task) 467 { 468 ASSERT(fLock.IsLocked()); 469 // remove the task 470 fTaskList.RemoveItem(task); 471 } 472 473 void 474 TaskLoop::AddTask(DelayedTask* task) 475 { 476 AutoLock<BLocker> autoLock(&fLock); 477 if (!autoLock.IsLocked()) { 478 delete task; 479 return; 480 } 481 482 fTaskList.AddItem(task); 483 StartPulsingIfNeeded(); 484 } 485 486 487 // #pragma mark - StandAloneTaskLoop 488 489 490 StandAloneTaskLoop::StandAloneTaskLoop(bool keepThread, bigtime_t heartBeat) 491 : 492 TaskLoop(heartBeat), 493 fNeedToQuit(false), 494 fScanThread(-1), 495 fKeepThread(keepThread) 496 { 497 } 498 499 500 StandAloneTaskLoop::~StandAloneTaskLoop() 501 { 502 fLock.Lock(); 503 fNeedToQuit = true; 504 bool easyOut = (fScanThread == -1); 505 fLock.Unlock(); 506 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 511 512 if (!timeout) { 513 PRINT(("StandAloneTaskLoop timed out, quitting abruptly")); 514 break; 515 } 516 517 bool done; 518 519 fLock.Lock(); 520 done = (fScanThread == -1); 521 fLock.Unlock(); 522 if (done) 523 break; 524 525 snooze(1000); 526 } 527 } 528 529 530 void 531 StandAloneTaskLoop::StartPulsingIfNeeded() 532 { 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); 539 } 540 } 541 542 543 bool 544 StandAloneTaskLoop::KeepPulsingWhenEmpty() const 545 { 546 return fKeepThread; 547 } 548 549 550 status_t 551 StandAloneTaskLoop::RunBinder(void* castToThis) 552 { 553 StandAloneTaskLoop* self = (StandAloneTaskLoop*)castToThis; 554 self->Run(); 555 return B_OK; 556 } 557 558 559 void 560 StandAloneTaskLoop::Run() 561 { 562 for(;;) { 563 AutoLock<BLocker> autoLock(&fLock); 564 if (!autoLock) 565 return; 566 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; 572 } 573 574 if (Pulse()) { 575 fScanThread = -1; 576 return; 577 } 578 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; 587 588 autoLock.Unlock(); 589 590 if (snoozeTill > now) 591 snooze_until(snoozeTill, B_SYSTEM_TIMEBASE); 592 else 593 snooze(1000); 594 } 595 } 596 597 598 void 599 StandAloneTaskLoop::AddTask(DelayedTask* delayedTask) 600 { 601 _inherited::AddTask(delayedTask); 602 if (fScanThread < 0) 603 return; 604 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); 612 } 613 } 614 615 616 // #pragma mark - PiggybackTaskLoop 617 618 619 PiggybackTaskLoop::PiggybackTaskLoop(bigtime_t heartBeat) 620 : 621 TaskLoop(heartBeat), 622 fNextHeartBeatTime(0), 623 fPulseMe(false) 624 { 625 } 626 627 628 PiggybackTaskLoop::~PiggybackTaskLoop() 629 { 630 } 631 632 633 void 634 PiggybackTaskLoop::PulseMe() 635 { 636 if (!fPulseMe) 637 return; 638 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; 645 } 646 } 647 648 649 bool 650 PiggybackTaskLoop::KeepPulsingWhenEmpty() const 651 { 652 return false; 653 } 654 655 656 void 657 PiggybackTaskLoop::StartPulsingIfNeeded() 658 { 659 fPulseMe = true; 660 } 661