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 DelayedTask::DelayedTask(bigtime_t delay) 43 : fRunAfter(system_time() + delay) 44 { 45 } 46 47 DelayedTask::~DelayedTask() 48 { 49 } 50 51 OneShotDelayedTask::OneShotDelayedTask(FunctionObject* functor, 52 bigtime_t delay) 53 : DelayedTask(delay), 54 fFunctor(functor) 55 { 56 } 57 58 59 OneShotDelayedTask::~OneShotDelayedTask() 60 { 61 delete fFunctor; 62 } 63 64 65 bool 66 OneShotDelayedTask::RunIfNeeded(bigtime_t currentTime) 67 { 68 if (currentTime < fRunAfter) 69 return false; 70 71 (*fFunctor)(); 72 return true; 73 } 74 75 76 PeriodicDelayedTask::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 86 PeriodicDelayedTask::~PeriodicDelayedTask() 87 { 88 delete fFunctor; 89 } 90 91 92 bool 93 PeriodicDelayedTask::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 104 PeriodicDelayedTaskWithTimeout::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 113 bool 114 PeriodicDelayedTaskWithTimeout::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 129 RunWhenIdleTask::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 138 RunWhenIdleTask::~RunWhenIdleTask() 139 { 140 } 141 142 143 bool 144 RunWhenIdleTask::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 168 static bigtime_t 169 ActivityLevel() 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 181 void 182 RunWhenIdleTask::ResetIdleTimer(bigtime_t currentTime) 183 { 184 fActivityLevel = ActivityLevel(); 185 fActivityLevelStart = currentTime; 186 fLastCPUTooBusyTime = currentTime; 187 fState = kInitialIdleWait; 188 } 189 190 const float kTaskOverhead = 0.01f; 191 // this should really be specified by the task itself 192 const float kIdleTreshold = 0.15f; 193 194 bool 195 RunWhenIdleTask::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 229 bool 230 RunWhenIdleTask::IdleTimerExpired(bigtime_t currentTime) 231 { 232 return IsIdle(currentTime, 0); 233 } 234 235 236 bool 237 RunWhenIdleTask::StillIdle(bigtime_t currentTime) 238 { 239 return IsIdle(currentTime, kIdleTreshold); 240 } 241 242 243 TaskLoop::TaskLoop(bigtime_t heartBeat) 244 : fTaskList(10, true), 245 fHeartBeat(heartBeat) 246 { 247 } 248 249 250 TaskLoop::~TaskLoop() 251 { 252 } 253 254 255 void 256 TaskLoop::RunLater(DelayedTask* task) 257 { 258 AddTask(task); 259 } 260 261 262 void 263 TaskLoop::RunLater(FunctionObject* functor, bigtime_t delay) 264 { 265 RunLater(new OneShotDelayedTask(functor, delay)); 266 } 267 268 269 void 270 TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor, 271 bigtime_t delay, bigtime_t period) 272 { 273 RunLater(new PeriodicDelayedTask(functor, delay, period)); 274 } 275 276 277 void 278 TaskLoop::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 286 void 287 TaskLoop::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 294 class AccumulatedOneShotDelayedTask : public OneShotDelayedTask { 295 // supports accumulating functors 296 public: 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 333 private: 334 int32 maxAccumulateCount; 335 int32 accumulateCount; 336 bigtime_t maxAccumulatingTime; 337 bigtime_t initialTime; 338 }; 339 340 void 341 TaskLoop::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 366 bool 367 TaskLoop::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 388 const bigtime_t kInfinity = B_INFINITE_TIMEOUT; 389 390 bigtime_t 391 TaskLoop::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 423 void 424 TaskLoop::RemoveTask(DelayedTask* task) 425 { 426 ASSERT(fLock.IsLocked()); 427 // remove the task 428 fTaskList.RemoveItem(task); 429 } 430 431 void 432 TaskLoop::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 445 StandAloneTaskLoop::StandAloneTaskLoop(bool keepThread, bigtime_t heartBeat) 446 : TaskLoop(heartBeat), 447 fNeedToQuit(false), 448 fScanThread(-1), 449 fKeepThread(keepThread) 450 { 451 } 452 453 454 StandAloneTaskLoop::~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 483 void 484 StandAloneTaskLoop::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 495 bool 496 StandAloneTaskLoop::KeepPulsingWhenEmpty() const 497 { 498 return fKeepThread; 499 } 500 501 status_t 502 StandAloneTaskLoop::RunBinder(void* castToThis) 503 { 504 StandAloneTaskLoop* self = (StandAloneTaskLoop*)castToThis; 505 self->Run(); 506 return B_OK; 507 } 508 509 void 510 StandAloneTaskLoop::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 547 void 548 StandAloneTaskLoop::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 564 PiggybackTaskLoop::PiggybackTaskLoop(bigtime_t heartBeat) 565 : TaskLoop(heartBeat), 566 fNextHeartBeatTime(0), 567 fPulseMe(false) 568 { 569 } 570 571 572 PiggybackTaskLoop::~PiggybackTaskLoop() 573 { 574 } 575 576 void 577 PiggybackTaskLoop::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 591 bool 592 PiggybackTaskLoop::KeepPulsingWhenEmpty() const 593 { 594 return false; 595 } 596 597 void 598 PiggybackTaskLoop::StartPulsingIfNeeded() 599 { 600 fPulseMe = true; 601 } 602