xref: /haiku/src/kits/tracker/TaskLoop.cpp (revision 425ac1b60a56f4df7a0e88bd784545c0ec4fa01f)
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