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 RunWhenIdleTask::RunWhenIdleTask(FunctionObjectWithResult<bool>* functor, 220 bigtime_t initialDelay, bigtime_t idleFor, bigtime_t heartBeat) 221 : 222 PeriodicDelayedTask(functor, initialDelay, heartBeat), 223 fIdleFor(idleFor), 224 fState(kInitialDelay) 225 { 226 } 227 228 229 // #pragma mark - RunWhenIdleTask 230 231 232 RunWhenIdleTask::~RunWhenIdleTask() 233 { 234 } 235 236 237 bool 238 RunWhenIdleTask::RunIfNeeded(bigtime_t currentTime) 239 { 240 if (currentTime < fRunAfter) 241 return false; 242 243 fRunAfter = currentTime + fPeriod; 244 // PRINT(("runWhenIdle: runAfter %Ld, current time %Ld, period %Ld\n", 245 // fRunAfter, currentTime, fPeriod)); 246 247 if (fState == kInitialDelay) { 248 // PRINT(("run when idle task - past intial delay\n")); 249 ResetIdleTimer(currentTime); 250 } else if (fState == kInIdleState && !StillIdle(currentTime)) { 251 fState = kInitialIdleWait; 252 ResetIdleTimer(currentTime); 253 } else if (fState != kInitialIdleWait || IdleTimerExpired(currentTime)) { 254 fState = kInIdleState; 255 (*fFunctor)(); 256 return fFunctor->Result(); 257 } 258 259 return false; 260 } 261 262 263 void 264 RunWhenIdleTask::ResetIdleTimer(bigtime_t currentTime) 265 { 266 fActivityLevel = ActivityLevel(); 267 fActivityLevelStart = currentTime; 268 fLastCPUTooBusyTime = currentTime; 269 fState = kInitialIdleWait; 270 } 271 272 273 bool 274 RunWhenIdleTask::IsIdle(bigtime_t currentTime, float taskOverhead) 275 { 276 bigtime_t currentActivityLevel = ActivityLevel(); 277 float load = (float)(currentActivityLevel - fActivityLevel) 278 / (float)(currentTime - fActivityLevelStart); 279 280 fActivityLevel = currentActivityLevel; 281 fActivityLevelStart = currentTime; 282 283 load -= taskOverhead; 284 285 bool idle = true; 286 287 if (load > kIdleTreshold) { 288 // PRINT(("not idle enough %f\n", load)); 289 idle = false; 290 } else if ((currentTime - fLastCPUTooBusyTime) < fIdleFor 291 || idle_time() < fIdleFor) { 292 // PRINT(("load %f, not idle long enough %Ld, %Ld\n", load, 293 // currentTime - fLastCPUTooBusyTime, 294 // idle_time())); 295 idle = false; 296 } 297 298 #if xDEBUG 299 else 300 PRINT(("load %f, idle for %Ld sec, go\n", load, 301 (currentTime - fLastCPUTooBusyTime) / 1000000)); 302 #endif 303 304 return idle; 305 } 306 307 308 bool 309 RunWhenIdleTask::IdleTimerExpired(bigtime_t currentTime) 310 { 311 return IsIdle(currentTime, 0); 312 } 313 314 315 bool 316 RunWhenIdleTask::StillIdle(bigtime_t currentTime) 317 { 318 return IsIdle(currentTime, kIdleTreshold); 319 } 320 321 322 // #pragma mark - TaskLoop 323 324 325 TaskLoop::TaskLoop(bigtime_t heartBeat) 326 : 327 fTaskList(10, true), 328 fHeartBeat(heartBeat) 329 { 330 } 331 332 333 TaskLoop::~TaskLoop() 334 { 335 } 336 337 338 void 339 TaskLoop::RunLater(DelayedTask* task) 340 { 341 AddTask(task); 342 } 343 344 345 void 346 TaskLoop::RunLater(FunctionObject* functor, bigtime_t delay) 347 { 348 RunLater(new OneShotDelayedTask(functor, delay)); 349 } 350 351 352 void 353 TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor, 354 bigtime_t delay, bigtime_t period) 355 { 356 RunLater(new PeriodicDelayedTask(functor, delay, period)); 357 } 358 359 360 void 361 TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor, bigtime_t delay, 362 bigtime_t period, bigtime_t timeout) 363 { 364 RunLater(new PeriodicDelayedTaskWithTimeout(functor, delay, period, 365 timeout)); 366 } 367 368 369 void 370 TaskLoop::RunWhenIdle(FunctionObjectWithResult<bool>* functor, 371 bigtime_t initialDelay, bigtime_t idleTime, bigtime_t heartBeat) 372 { 373 RunLater(new RunWhenIdleTask(functor, initialDelay, idleTime, heartBeat)); 374 } 375 376 377 // #pragma mark - TaskLoop 378 379 380 void 381 TaskLoop::AccumulatedRunLater(AccumulatingFunctionObject* functor, 382 bigtime_t delay, bigtime_t maxAccumulatingTime, int32 maxAccumulateCount) 383 { 384 AutoLock<BLocker> autoLock(&fLock); 385 if (!autoLock.IsLocked()) { 386 return; 387 } 388 int32 count = fTaskList.CountItems(); 389 for (int32 index = 0; index < count; index++) { 390 AccumulatedOneShotDelayedTask* task 391 = dynamic_cast<AccumulatedOneShotDelayedTask*>( 392 fTaskList.ItemAt(index)); 393 if (!task) 394 continue; 395 396 if (task->CanAccumulate(functor)) { 397 task->Accumulate(functor, delay); 398 return; 399 } 400 } 401 RunLater(new AccumulatedOneShotDelayedTask(functor, delay, 402 maxAccumulatingTime, maxAccumulateCount)); 403 } 404 405 406 bool 407 TaskLoop::Pulse() 408 { 409 ASSERT(fLock.IsLocked()); 410 411 int32 count = fTaskList.CountItems(); 412 if (count > 0) { 413 bigtime_t currentTime = system_time(); 414 for (int32 index = 0; index < count; ) { 415 DelayedTask* task = fTaskList.ItemAt(index); 416 // give every task a try 417 if (task->RunIfNeeded(currentTime)) { 418 // if done, remove from list 419 RemoveTask(task); 420 count--; 421 } else 422 index++; 423 } 424 } 425 return count == 0 && !KeepPulsingWhenEmpty(); 426 } 427 428 429 bigtime_t 430 TaskLoop::LatestRunTime() const 431 { 432 ASSERT(fLock.IsLocked()); 433 bigtime_t result = kInfinity; 434 435 #if xDEBUG 436 DelayedTask* nextTask = 0; 437 #endif 438 int32 count = fTaskList.CountItems(); 439 for (int32 index = 0; index < count; index++) { 440 bigtime_t runAfter = fTaskList.ItemAt(index)->RunAfterTime(); 441 if (runAfter < result) { 442 result = runAfter; 443 444 #if xDEBUG 445 nextTask = fTaskList.ItemAt(index); 446 #endif 447 } 448 } 449 450 451 #if xDEBUG 452 if (nextTask) 453 PRINT(("latestRunTime : next task %s\n", typeid(*nextTask).name)); 454 else 455 PRINT(("latestRunTime : no next task\n")); 456 #endif 457 458 return result; 459 } 460 461 462 void 463 TaskLoop::RemoveTask(DelayedTask* task) 464 { 465 ASSERT(fLock.IsLocked()); 466 // remove the task 467 fTaskList.RemoveItem(task); 468 } 469 470 void 471 TaskLoop::AddTask(DelayedTask* task) 472 { 473 AutoLock<BLocker> autoLock(&fLock); 474 if (!autoLock.IsLocked()) { 475 delete task; 476 return; 477 } 478 479 fTaskList.AddItem(task); 480 StartPulsingIfNeeded(); 481 } 482 483 484 // #pragma mark - StandAloneTaskLoop 485 486 487 StandAloneTaskLoop::StandAloneTaskLoop(bool keepThread, bigtime_t heartBeat) 488 : 489 TaskLoop(heartBeat), 490 fNeedToQuit(false), 491 fScanThread(-1), 492 fKeepThread(keepThread) 493 { 494 } 495 496 497 StandAloneTaskLoop::~StandAloneTaskLoop() 498 { 499 fLock.Lock(); 500 fNeedToQuit = true; 501 bool easyOut = (fScanThread == -1); 502 fLock.Unlock(); 503 504 if (!easyOut) 505 for (int32 timeout = 10000; ; timeout--) { 506 // use a 10 sec timeout value in case the spawned 507 // thread is stuck somewhere 508 509 if (!timeout) { 510 PRINT(("StandAloneTaskLoop timed out, quitting abruptly")); 511 break; 512 } 513 514 bool done; 515 516 fLock.Lock(); 517 done = (fScanThread == -1); 518 fLock.Unlock(); 519 if (done) 520 break; 521 522 snooze(1000); 523 } 524 } 525 526 527 void 528 StandAloneTaskLoop::StartPulsingIfNeeded() 529 { 530 ASSERT(fLock.IsLocked()); 531 if (fScanThread < 0) { 532 // no loop thread yet, spawn one 533 fScanThread = spawn_thread(StandAloneTaskLoop::RunBinder, 534 "TrackerTaskLoop", B_LOW_PRIORITY, this); 535 resume_thread(fScanThread); 536 } 537 } 538 539 540 bool 541 StandAloneTaskLoop::KeepPulsingWhenEmpty() const 542 { 543 return fKeepThread; 544 } 545 546 547 status_t 548 StandAloneTaskLoop::RunBinder(void* castToThis) 549 { 550 StandAloneTaskLoop* self = (StandAloneTaskLoop*)castToThis; 551 self->Run(); 552 return B_OK; 553 } 554 555 556 void 557 StandAloneTaskLoop::Run() 558 { 559 for(;;) { 560 AutoLock<BLocker> autoLock(&fLock); 561 if (!autoLock) 562 return; 563 564 if (fNeedToQuit) { 565 // task loop being deleted, let go of the thread allowing the 566 // to go through deletion 567 fScanThread = -1; 568 return; 569 } 570 571 if (Pulse()) { 572 fScanThread = -1; 573 return; 574 } 575 576 // figure out when to run next by checking out when the different 577 // tasks wan't to be woken up, snooze until a little bit before that 578 // time 579 bigtime_t now = system_time(); 580 bigtime_t latestRunTime = LatestRunTime() - 1000; 581 bigtime_t afterHeartBeatTime = now + fHeartBeat; 582 bigtime_t snoozeTill = latestRunTime < afterHeartBeatTime ? 583 latestRunTime : afterHeartBeatTime; 584 585 autoLock.Unlock(); 586 587 if (snoozeTill > now) 588 snooze_until(snoozeTill, B_SYSTEM_TIMEBASE); 589 else 590 snooze(1000); 591 } 592 } 593 594 595 void 596 StandAloneTaskLoop::AddTask(DelayedTask* delayedTask) 597 { 598 _inherited::AddTask(delayedTask); 599 if (fScanThread < 0) 600 return; 601 602 // wake up the loop thread if it is asleep 603 thread_info info; 604 get_thread_info(fScanThread, &info); 605 if (info.state == B_THREAD_ASLEEP) { 606 suspend_thread(fScanThread); 607 snooze(1000); // snooze because BeBook sez so 608 resume_thread(fScanThread); 609 } 610 } 611 612 613 // #pragma mark - PiggybackTaskLoop 614 615 616 PiggybackTaskLoop::PiggybackTaskLoop(bigtime_t heartBeat) 617 : 618 TaskLoop(heartBeat), 619 fNextHeartBeatTime(0), 620 fPulseMe(false) 621 { 622 } 623 624 625 PiggybackTaskLoop::~PiggybackTaskLoop() 626 { 627 } 628 629 630 void 631 PiggybackTaskLoop::PulseMe() 632 { 633 if (!fPulseMe) 634 return; 635 636 bigtime_t time = system_time(); 637 if (fNextHeartBeatTime < time) { 638 AutoLock<BLocker> autoLock(&fLock); 639 if (Pulse()) 640 fPulseMe = false; 641 fNextHeartBeatTime = time + fHeartBeat; 642 } 643 } 644 645 646 bool 647 PiggybackTaskLoop::KeepPulsingWhenEmpty() const 648 { 649 return false; 650 } 651 652 653 void 654 PiggybackTaskLoop::StartPulsingIfNeeded() 655 { 656 fPulseMe = true; 657 } 658