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