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
ActivityLevel()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:
AccumulatedOneShotDelayedTask(AccumulatingFunctionObject * functor,bigtime_t delay,bigtime_t maxAccumulatingTime=0,int32 maxAccumulateCount=0)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
CanAccumulate(const AccumulatingFunctionObject * accumulateThis) const81 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
Accumulate(AccumulatingFunctionObject * accumulateThis,bigtime_t delay)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
DelayedTask(bigtime_t delay)118 DelayedTask::DelayedTask(bigtime_t delay)
119 :
120 fRunAfter(system_time() + delay)
121 {
122 }
123
124
~DelayedTask()125 DelayedTask::~DelayedTask()
126 {
127 }
128
129
130 // #pragma mark - OneShotDelayedTask
131
132
OneShotDelayedTask(FunctionObject * functor,bigtime_t delay)133 OneShotDelayedTask::OneShotDelayedTask(FunctionObject* functor,
134 bigtime_t delay)
135 :
136 DelayedTask(delay),
137 fFunctor(functor)
138 {
139 }
140
141
~OneShotDelayedTask()142 OneShotDelayedTask::~OneShotDelayedTask()
143 {
144 delete fFunctor;
145 }
146
147
148 bool
RunIfNeeded(bigtime_t currentTime)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
PeriodicDelayedTask(FunctionObjectWithResult<bool> * functor,bigtime_t initialDelay,bigtime_t period)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
~PeriodicDelayedTask()173 PeriodicDelayedTask::~PeriodicDelayedTask()
174 {
175 delete fFunctor;
176 }
177
178
179 bool
RunIfNeeded(bigtime_t currentTime)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
PeriodicDelayedTaskWithTimeout(FunctionObjectWithResult<bool> * functor,bigtime_t initialDelay,bigtime_t period,bigtime_t timeout)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
RunIfNeeded(bigtime_t currentTime)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
RunWhenIdleTask(FunctionObjectWithResult<bool> * functor,bigtime_t initialDelay,bigtime_t idleFor,bigtime_t heartBeat)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
~RunWhenIdleTask()233 RunWhenIdleTask::~RunWhenIdleTask()
234 {
235 }
236
237
238 bool
RunIfNeeded(bigtime_t currentTime)239 RunWhenIdleTask::RunIfNeeded(bigtime_t currentTime)
240 {
241 if (currentTime < fRunAfter)
242 return false;
243
244 fRunAfter = currentTime + fPeriod;
245 // PRINT(("runWhenIdle: runAfter %lld, current time %lld, period %lld\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
ResetIdleTimer(bigtime_t currentTime)265 RunWhenIdleTask::ResetIdleTimer(bigtime_t currentTime)
266 {
267 fActivityLevel = ActivityLevel();
268 fActivityLevelStart = currentTime;
269 fLastCPUTooBusyTime = currentTime;
270 fState = kInitialIdleWait;
271 }
272
273
274 bool
IsIdle(bigtime_t currentTime,float taskOverhead)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 %lld, %lld\n", load,
294 // currentTime - fLastCPUTooBusyTime,
295 // idle_time()));
296 idle = false;
297 }
298
299 #if xDEBUG
300 else
301 PRINT(("load %f, idle for %lld sec, go\n", load,
302 (currentTime - fLastCPUTooBusyTime) / 1000000));
303 #endif
304
305 return idle;
306 }
307
308
309 bool
IdleTimerExpired(bigtime_t currentTime)310 RunWhenIdleTask::IdleTimerExpired(bigtime_t currentTime)
311 {
312 return IsIdle(currentTime, 0);
313 }
314
315
316 bool
StillIdle(bigtime_t currentTime)317 RunWhenIdleTask::StillIdle(bigtime_t currentTime)
318 {
319 return IsIdle(currentTime, kIdleTreshold);
320 }
321
322
323 // #pragma mark - TaskLoop
324
325
TaskLoop(bigtime_t heartBeat)326 TaskLoop::TaskLoop(bigtime_t heartBeat)
327 :
328 fTaskList(10, true),
329 fHeartBeat(heartBeat)
330 {
331 }
332
333
~TaskLoop()334 TaskLoop::~TaskLoop()
335 {
336 }
337
338
339 void
RunLater(DelayedTask * task)340 TaskLoop::RunLater(DelayedTask* task)
341 {
342 AddTask(task);
343 }
344
345
346 void
RunLater(FunctionObject * functor,bigtime_t delay)347 TaskLoop::RunLater(FunctionObject* functor, bigtime_t delay)
348 {
349 RunLater(new OneShotDelayedTask(functor, delay));
350 }
351
352
353 void
RunLater(FunctionObjectWithResult<bool> * functor,bigtime_t delay,bigtime_t period)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
RunLater(FunctionObjectWithResult<bool> * functor,bigtime_t delay,bigtime_t period,bigtime_t timeout)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
RunWhenIdle(FunctionObjectWithResult<bool> * functor,bigtime_t initialDelay,bigtime_t idleTime,bigtime_t heartBeat)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
AccumulatedRunLater(AccumulatingFunctionObject * functor,bigtime_t delay,bigtime_t maxAccumulatingTime,int32 maxAccumulateCount)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
Pulse()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
LatestRunTime() const431 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
RemoveTask(DelayedTask * task)464 TaskLoop::RemoveTask(DelayedTask* task)
465 {
466 ASSERT(fLock.IsLocked());
467 // remove the task
468 fTaskList.RemoveItem(task);
469 }
470
471 void
AddTask(DelayedTask * task)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
StandAloneTaskLoop(bool keepThread,bigtime_t heartBeat)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
~StandAloneTaskLoop()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
StartPulsingIfNeeded()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
KeepPulsingWhenEmpty() const542 StandAloneTaskLoop::KeepPulsingWhenEmpty() const
543 {
544 return fKeepThread;
545 }
546
547
548 status_t
RunBinder(void * castToThis)549 StandAloneTaskLoop::RunBinder(void* castToThis)
550 {
551 StandAloneTaskLoop* self = (StandAloneTaskLoop*)castToThis;
552 self->Run();
553 return B_OK;
554 }
555
556
557 void
Run()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
AddTask(DelayedTask * delayedTask)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
PiggybackTaskLoop(bigtime_t heartBeat)617 PiggybackTaskLoop::PiggybackTaskLoop(bigtime_t heartBeat)
618 :
619 TaskLoop(heartBeat),
620 fNextHeartBeatTime(0),
621 fPulseMe(false)
622 {
623 }
624
625
~PiggybackTaskLoop()626 PiggybackTaskLoop::~PiggybackTaskLoop()
627 {
628 }
629
630
631 void
PulseMe()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
KeepPulsingWhenEmpty() const648 PiggybackTaskLoop::KeepPulsingWhenEmpty() const
649 {
650 return false;
651 }
652
653
654 void
StartPulsingIfNeeded()655 PiggybackTaskLoop::StartPulsingIfNeeded()
656 {
657 fPulseMe = true;
658 }
659