#include <Debug.h>
#include <InterfaceDefs.h>
#include "AutoLock.h"
#include "TaskLoop.h"
const float kIdleTreshold = 0.15f;
const bigtime_t kInfinity = B_INFINITE_TIMEOUT;
static bigtime_t
ActivityLevel()
{
bigtime_t time = 0;
system_info sinfo;
get_system_info(&sinfo);
cpu_info* cpuInfos = new cpu_info[sinfo.cpu_count];
get_cpu_info(0, sinfo.cpu_count, cpuInfos);
for (uint32 index = 0; index < sinfo.cpu_count; index++)
time += cpuInfos[index].active_time;
delete[] cpuInfos;
return time / ((bigtime_t) sinfo.cpu_count);
}
class AccumulatedOneShotDelayedTask : public OneShotDelayedTask {
public:
AccumulatedOneShotDelayedTask(AccumulatingFunctionObject* functor,
bigtime_t delay, bigtime_t maxAccumulatingTime = 0,
int32 maxAccumulateCount = 0)
:
OneShotDelayedTask(functor, delay),
maxAccumulateCount(maxAccumulateCount),
accumulateCount(1),
maxAccumulatingTime(maxAccumulatingTime),
initialTime(system_time())
{
}
bool CanAccumulate(const AccumulatingFunctionObject* accumulateThis) const
{
if (maxAccumulateCount && accumulateCount > maxAccumulateCount)
return false;
if (maxAccumulatingTime && system_time() > initialTime
+ maxAccumulatingTime) {
return false;
}
return static_cast<AccumulatingFunctionObject*>(fFunctor)->
CanAccumulate(accumulateThis);
}
virtual void Accumulate(AccumulatingFunctionObject* accumulateThis,
bigtime_t delay)
{
fRunAfter = system_time() + delay;
accumulateCount++;
static_cast<AccumulatingFunctionObject*>(fFunctor)->
Accumulate(accumulateThis);
}
private:
int32 maxAccumulateCount;
int32 accumulateCount;
bigtime_t maxAccumulatingTime;
bigtime_t initialTime;
};
DelayedTask::DelayedTask(bigtime_t delay)
:
fRunAfter(system_time() + delay)
{
}
DelayedTask::~DelayedTask()
{
}
OneShotDelayedTask::OneShotDelayedTask(FunctionObject* functor,
bigtime_t delay)
:
DelayedTask(delay),
fFunctor(functor)
{
}
OneShotDelayedTask::~OneShotDelayedTask()
{
delete fFunctor;
}
bool
OneShotDelayedTask::RunIfNeeded(bigtime_t currentTime)
{
if (currentTime < fRunAfter)
return false;
(*fFunctor)();
return true;
}
PeriodicDelayedTask::PeriodicDelayedTask(
FunctionObjectWithResult<bool>* functor, bigtime_t initialDelay,
bigtime_t period)
:
DelayedTask(initialDelay),
fPeriod(period),
fFunctor(functor)
{
}
PeriodicDelayedTask::~PeriodicDelayedTask()
{
delete fFunctor;
}
bool
PeriodicDelayedTask::RunIfNeeded(bigtime_t currentTime)
{
if (currentTime < fRunAfter)
return false;
fRunAfter = currentTime + fPeriod;
(*fFunctor)();
return fFunctor->Result();
}
PeriodicDelayedTaskWithTimeout::PeriodicDelayedTaskWithTimeout(
FunctionObjectWithResult<bool>* functor, bigtime_t initialDelay,
bigtime_t period, bigtime_t timeout)
:
PeriodicDelayedTask(functor, initialDelay, period),
fTimeoutAfter(system_time() + timeout)
{
}
bool
PeriodicDelayedTaskWithTimeout::RunIfNeeded(bigtime_t currentTime)
{
if (currentTime < fRunAfter)
return false;
fRunAfter = currentTime + fPeriod;
(*fFunctor)();
if (fFunctor->Result())
return true;
return currentTime > fTimeoutAfter;
}
RunWhenIdleTask::RunWhenIdleTask(FunctionObjectWithResult<bool>* functor,
bigtime_t initialDelay, bigtime_t idleFor, bigtime_t heartBeat)
:
PeriodicDelayedTask(functor, initialDelay, heartBeat),
fIdleFor(idleFor),
fState(kInitialDelay),
fActivityLevelStart(0),
fActivityLevel(0),
fLastCPUTooBusyTime(0)
{
}
RunWhenIdleTask::~RunWhenIdleTask()
{
}
bool
RunWhenIdleTask::RunIfNeeded(bigtime_t currentTime)
{
if (currentTime < fRunAfter)
return false;
fRunAfter = currentTime + fPeriod;
if (fState == kInitialDelay) {
ResetIdleTimer(currentTime);
} else if (fState == kInIdleState && !StillIdle(currentTime)) {
fState = kInitialIdleWait;
ResetIdleTimer(currentTime);
} else if (fState != kInitialIdleWait || IdleTimerExpired(currentTime)) {
fState = kInIdleState;
(*fFunctor)();
return fFunctor->Result();
}
return false;
}
void
RunWhenIdleTask::ResetIdleTimer(bigtime_t currentTime)
{
fActivityLevel = ActivityLevel();
fActivityLevelStart = currentTime;
fLastCPUTooBusyTime = currentTime;
fState = kInitialIdleWait;
}
bool
RunWhenIdleTask::IsIdle(bigtime_t currentTime, float taskOverhead)
{
bigtime_t currentActivityLevel = ActivityLevel();
float load = (float)(currentActivityLevel - fActivityLevel)
/ (float)(currentTime - fActivityLevelStart);
fActivityLevel = currentActivityLevel;
fActivityLevelStart = currentTime;
load -= taskOverhead;
bool idle = true;
if (load > kIdleTreshold) {
idle = false;
} else if ((currentTime - fLastCPUTooBusyTime) < fIdleFor
|| idle_time() < fIdleFor) {
idle = false;
}
#if xDEBUG
else
PRINT(("load %f, idle for %lld sec, go\n", load,
(currentTime - fLastCPUTooBusyTime) / 1000000));
#endif
return idle;
}
bool
RunWhenIdleTask::IdleTimerExpired(bigtime_t currentTime)
{
return IsIdle(currentTime, 0);
}
bool
RunWhenIdleTask::StillIdle(bigtime_t currentTime)
{
return IsIdle(currentTime, kIdleTreshold);
}
TaskLoop::TaskLoop(bigtime_t heartBeat)
:
fTaskList(10),
fHeartBeat(heartBeat)
{
}
TaskLoop::~TaskLoop()
{
}
void
TaskLoop::RunLater(DelayedTask* task)
{
AddTask(task);
}
void
TaskLoop::RunLater(FunctionObject* functor, bigtime_t delay)
{
RunLater(new OneShotDelayedTask(functor, delay));
}
void
TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor,
bigtime_t delay, bigtime_t period)
{
RunLater(new PeriodicDelayedTask(functor, delay, period));
}
void
TaskLoop::RunLater(FunctionObjectWithResult<bool>* functor, bigtime_t delay,
bigtime_t period, bigtime_t timeout)
{
RunLater(new PeriodicDelayedTaskWithTimeout(functor, delay, period,
timeout));
}
void
TaskLoop::RunWhenIdle(FunctionObjectWithResult<bool>* functor,
bigtime_t initialDelay, bigtime_t idleTime, bigtime_t heartBeat)
{
RunLater(new RunWhenIdleTask(functor, initialDelay, idleTime, heartBeat));
}
void
TaskLoop::AccumulatedRunLater(AccumulatingFunctionObject* functor,
bigtime_t delay, bigtime_t maxAccumulatingTime, int32 maxAccumulateCount)
{
AutoLock<BLocker> autoLock(&fLock);
if (!autoLock.IsLocked())
return;
int32 count = fTaskList.CountItems();
for (int32 index = 0; index < count; index++) {
AccumulatedOneShotDelayedTask* task
= dynamic_cast<AccumulatedOneShotDelayedTask*>(
fTaskList.ItemAt(index));
if (task == NULL)
continue;
else if (task->CanAccumulate(functor)) {
task->Accumulate(functor, delay);
return;
}
}
RunLater(new AccumulatedOneShotDelayedTask(functor, delay,
maxAccumulatingTime, maxAccumulateCount));
}
bool
TaskLoop::Pulse()
{
ASSERT(fLock.IsLocked());
int32 count = fTaskList.CountItems();
if (count > 0) {
bigtime_t currentTime = system_time();
for (int32 index = 0; index < count; ) {
DelayedTask* task = fTaskList.ItemAt(index);
if (task->RunIfNeeded(currentTime)) {
RemoveTask(task);
count--;
} else
index++;
}
}
return count == 0 && !KeepPulsingWhenEmpty();
}
bigtime_t
TaskLoop::LatestRunTime() const
{
ASSERT(fLock.IsLocked());
bigtime_t result = kInfinity;
#if xDEBUG
DelayedTask* nextTask = 0;
#endif
int32 count = fTaskList.CountItems();
for (int32 index = 0; index < count; index++) {
bigtime_t runAfter = fTaskList.ItemAt(index)->RunAfterTime();
if (runAfter < result) {
result = runAfter;
#if xDEBUG
nextTask = fTaskList.ItemAt(index);
#endif
}
}
#if xDEBUG
if (nextTask)
PRINT(("latestRunTime : next task %s\n", typeid(*nextTask).name));
else
PRINT(("latestRunTime : no next task\n"));
#endif
return result;
}
void
TaskLoop::RemoveTask(DelayedTask* task)
{
ASSERT(fLock.IsLocked());
fTaskList.RemoveItem(task);
}
void
TaskLoop::AddTask(DelayedTask* task)
{
AutoLock<BLocker> autoLock(&fLock);
if (!autoLock.IsLocked()) {
delete task;
return;
}
fTaskList.AddItem(task);
StartPulsingIfNeeded();
}
StandAloneTaskLoop::StandAloneTaskLoop(bool keepThread, bigtime_t heartBeat)
:
TaskLoop(heartBeat),
fNeedToQuit(false),
fScanThread(-1),
fKeepThread(keepThread)
{
}
StandAloneTaskLoop::~StandAloneTaskLoop()
{
fLock.Lock();
fNeedToQuit = true;
bool easyOut = (fScanThread == -1);
fLock.Unlock();
if (!easyOut)
for (int32 timeout = 10000; ; timeout--) {
if (!timeout) {
PRINT(("StandAloneTaskLoop timed out, quitting abruptly"));
break;
}
bool done;
fLock.Lock();
done = (fScanThread == -1);
fLock.Unlock();
if (done)
break;
snooze(1000);
}
}
void
StandAloneTaskLoop::StartPulsingIfNeeded()
{
ASSERT(fLock.IsLocked());
if (fScanThread < 0) {
fScanThread = spawn_thread(StandAloneTaskLoop::RunBinder,
"TrackerTaskLoop", B_LOW_PRIORITY, this);
resume_thread(fScanThread);
}
}
bool
StandAloneTaskLoop::KeepPulsingWhenEmpty() const
{
return fKeepThread;
}
status_t
StandAloneTaskLoop::RunBinder(void* castToThis)
{
StandAloneTaskLoop* self = (StandAloneTaskLoop*)castToThis;
self->Run();
return B_OK;
}
void
StandAloneTaskLoop::Run()
{
for(;;) {
AutoLock<BLocker> autoLock(&fLock);
if (!autoLock)
return;
if (fNeedToQuit) {
fScanThread = -1;
return;
}
if (Pulse()) {
fScanThread = -1;
return;
}
bigtime_t now = system_time();
bigtime_t latestRunTime = LatestRunTime() - 1000;
bigtime_t afterHeartBeatTime = now + fHeartBeat;
bigtime_t snoozeTill = latestRunTime < afterHeartBeatTime ?
latestRunTime : afterHeartBeatTime;
autoLock.Unlock();
if (snoozeTill > now)
snooze_until(snoozeTill, B_SYSTEM_TIMEBASE);
else
snooze(1000);
}
}
void
StandAloneTaskLoop::AddTask(DelayedTask* delayedTask)
{
_inherited::AddTask(delayedTask);
if (fScanThread < 0)
return;
thread_info info;
get_thread_info(fScanThread, &info);
if (info.state == B_THREAD_ASLEEP) {
suspend_thread(fScanThread);
snooze(1000);
resume_thread(fScanThread);
}
}
PiggybackTaskLoop::PiggybackTaskLoop(bigtime_t heartBeat)
:
TaskLoop(heartBeat),
fNextHeartBeatTime(0),
fPulseMe(false)
{
}
PiggybackTaskLoop::~PiggybackTaskLoop()
{
}
void
PiggybackTaskLoop::PulseMe()
{
if (!fPulseMe)
return;
bigtime_t time = system_time();
if (fNextHeartBeatTime < time) {
AutoLock<BLocker> autoLock(&fLock);
if (Pulse())
fPulseMe = false;
fNextHeartBeatTime = time + fHeartBeat;
}
}
bool
PiggybackTaskLoop::KeepPulsingWhenEmpty() const
{
return false;
}
void
PiggybackTaskLoop::StartPulsingIfNeeded()
{
fPulseMe = true;
}