Files
haiku-beta6/src/system/kernel/scheduler/scheduler_thread.h
T

360 lines
7.3 KiB
C++

/*
* Copyright 2013, Paweł Dziepak, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef KERNEL_SCHEDULER_THREAD_H
#define KERNEL_SCHEDULER_THREAD_H
#include <thread.h>
#include <util/AutoLock.h>
#include "scheduler_common.h"
#include "scheduler_cpu.h"
namespace Scheduler {
struct ThreadData : public DoublyLinkedListLinkImpl<ThreadData>,
RunQueueLinkImpl<ThreadData> {
public:
ThreadData(Thread* thread);
void Init();
void Init(CoreEntry* core);
void Dump() const;
inline bool HasCacheExpired() const;
inline bool ShouldRebalance() const;
inline int32 GetEffectivePriority() const;
inline void IncreasePenalty();
inline void CancelPenalty();
inline bool ShouldCancelPenalty() const;
bool ChooseCoreAndCPU(CoreEntry*& targetCore,
CPUEntry*& targetCPU);
inline bigtime_t LastInterruptTime() const
{ return fLastInterruptTime; }
inline void SetLastInterruptTime(bigtime_t interruptTime)
{ fLastInterruptTime = interruptTime; }
inline void IncreaseStolenTime(bigtime_t stolenTime);
inline void GoesAway();
inline bigtime_t WentSleep() const { return fWentSleep; }
inline bigtime_t WentSleepActive() const { return fWentSleepActive; }
inline bigtime_t WentSleepCount() const { return fWentSleepCount; }
inline void PutBack();
inline void Enqueue();
inline bool Dequeue();
inline void UpdateActivity(bigtime_t active);
inline void ComputeLoad();
inline bool HasQuantumEnded(bool wasPreempted, bool hasYielded);
bigtime_t ComputeQuantum();
inline void StartQuantum();
inline bool IsEnqueued() const { return fEnqueued; }
inline void SetDequeued() { fEnqueued = false; }
inline Thread* GetThread() const { return fThread; }
inline int32 GetLoad() const { return fLoad; }
inline CoreEntry* Core() const { return fCore; }
inline void UnassignCore() { fCore = NULL; }
static void ComputeQuantumLengths();
private:
inline int32 _GetPenalty() const;
inline int32 _GetMinimalPriority() const;
void _ComputeEffectivePriority() const;
inline CoreEntry* _ChooseCore() const;
inline CPUEntry* _ChooseCPU(CoreEntry* core,
bool& rescheduleNeeded) const;
inline bigtime_t _GetBaseQuantum() const;
static bigtime_t _ScaleQuantum(bigtime_t maxQuantum,
bigtime_t minQuantum, int32 maxPriority,
int32 minPriority, int32 priority);
bigtime_t fStolenTime;
bigtime_t fQuantumStart;
bigtime_t fLastInterruptTime;
bigtime_t fWentSleep;
bigtime_t fWentSleepActive;
int32 fWentSleepCount;
bool fEnqueued;
Thread* fThread;
int32 fPriorityPenalty;
int32 fAdditionalPenalty;
mutable int32 fEffectivePriority;
bigtime_t fTimeLeft;
bigtime_t fMeasureActiveTime;
bigtime_t fMeasureTime;
int32 fLoad;
CoreEntry* fCore;
};
class ThreadProcessing {
public:
virtual ~ThreadProcessing();
virtual void operator()(ThreadData* thread) = 0;
};
inline bool
ThreadData::HasCacheExpired() const
{
return gCurrentMode->has_cache_expired(this);
}
inline bool
ThreadData::ShouldRebalance() const
{
ASSERT(!gSingleCore);
return gCurrentMode->should_rebalance(this);
}
inline int32
ThreadData::GetEffectivePriority() const
{
if (fEffectivePriority == -1)
_ComputeEffectivePriority();
return fEffectivePriority;
}
inline void
ThreadData::IncreasePenalty()
{
if (fThread->priority < B_LOWEST_ACTIVE_PRIORITY)
return;
if (fThread->priority >= B_FIRST_REAL_TIME_PRIORITY)
return;
TRACE("increasing thread %ld penalty\n", fThread->id);
fEffectivePriority = -1;
int32 oldPenalty = fPriorityPenalty++;
ASSERT(fThread->priority - oldPenalty >= B_LOWEST_ACTIVE_PRIORITY);
const int kMinimalPriority = _GetMinimalPriority();
if (fThread->priority - oldPenalty <= kMinimalPriority) {
fPriorityPenalty = oldPenalty;
fAdditionalPenalty++;
}
}
inline void
ThreadData::CancelPenalty()
{
if (fPriorityPenalty != 0) {
TRACE("cancelling thread %ld penalty\n", fThread->id);
fEffectivePriority = -1;
}
fAdditionalPenalty = 0;
fPriorityPenalty = 0;
}
inline bool
ThreadData::ShouldCancelPenalty() const
{
if (fCore == NULL)
return false;
return fCore->StarvationCounter() != fWentSleepCount
&& system_time() - fWentSleep > gCurrentMode->base_quantum;
}
inline void
ThreadData::IncreaseStolenTime(bigtime_t stolenTime)
{
fStolenTime += stolenTime;
}
inline void
ThreadData::GoesAway()
{
fLastInterruptTime = 0;
fWentSleep = system_time();
fWentSleepCount = fCore->StarvationCounter();
fWentSleepActive = fCore->GetActiveTime();
}
inline void
ThreadData::PutBack()
{
ComputeLoad();
fWentSleepCount = -1;
int32 priority = GetEffectivePriority();
CoreRunQueueLocker _(fCore);
ASSERT(!fEnqueued);
fEnqueued = true;
if (fThread->pinned_to_cpu > 0) {
ASSERT(fThread->cpu != NULL);
CPUEntry* cpu = CPUEntry::GetCPU(fThread->cpu->cpu_num);
cpu->PushFront(this, priority);
} else
fCore->PushFront(this, priority);
fCore->UnlockRunQueue();
}
inline void
ThreadData::Enqueue()
{
fThread->state = B_THREAD_READY;
ComputeLoad();
fWentSleepCount = 0;
int32 priority = GetEffectivePriority();
CoreRunQueueLocker _(fCore);
ASSERT(!fEnqueued);
fEnqueued = true;
if (fThread->pinned_to_cpu > 0) {
ASSERT(fThread->previous_cpu != NULL);
CPUEntry* cpu = CPUEntry::GetCPU(fThread->previous_cpu->cpu_num);
cpu->PushBack(this, priority);
} else
fCore->PushBack(this, priority);
}
inline bool
ThreadData::Dequeue()
{
CoreRunQueueLocker _(fCore);
if (!fEnqueued)
return false;
if (fThread->pinned_to_cpu > 0) {
ASSERT(fThread->previous_cpu != NULL);
CPUEntry* cpu = CPUEntry::GetCPU(fThread->previous_cpu->cpu_num);
cpu->Remove(this);
} else {
ASSERT(fWentSleepCount < 1);
fCore->Remove(this);
}
ASSERT(!fEnqueued);
return true;
}
inline void
ThreadData::UpdateActivity(bigtime_t active)
{
fMeasureActiveTime += active;
CPUEntry::GetCPU(smp_get_current_cpu())->IncreaseActiveTime(active);
fCore->IncreaseActiveTime(active);
}
inline void
ThreadData::ComputeLoad()
{
if (fLastInterruptTime > 0) {
bigtime_t interruptTime = gCPU[smp_get_current_cpu()].interrupt_time;
interruptTime -= fLastInterruptTime;
fMeasureActiveTime -= interruptTime;
}
compute_load(fMeasureTime, fMeasureActiveTime, fLoad);
}
inline bool
ThreadData::HasQuantumEnded(bool wasPreempted, bool hasYielded)
{
if (hasYielded) {
fTimeLeft = 0;
return true;
}
bigtime_t timeUsed = system_time() - fQuantumStart;
fTimeLeft -= timeUsed;
fTimeLeft = std::max(fTimeLeft, bigtime_t(0));
// too little time left, it's better make the next quantum a bit longer
if (wasPreempted || fTimeLeft <= gCurrentMode->minimal_quantum) {
fStolenTime += fTimeLeft;
fTimeLeft = 0;
}
return fTimeLeft == 0;
}
inline void
ThreadData::StartQuantum()
{
fQuantumStart = system_time();
}
inline int32
ThreadData::_GetPenalty() const
{
int32 penalty = fPriorityPenalty;
const int kMinimalPriority = _GetMinimalPriority();
if (kMinimalPriority > 0)
penalty += fAdditionalPenalty % kMinimalPriority;
return penalty;
}
inline int32
ThreadData::_GetMinimalPriority() const
{
const int32 kDivisor = 5;
const int32 kMaximalPriority = 25;
const int32 kMinimalPriority = B_LOWEST_ACTIVE_PRIORITY;
int32 priority = fThread->priority / kDivisor;
return std::max(std::min(priority, kMaximalPriority), kMinimalPriority);
}
} // namespace Scheduler
#endif // KERNEL_SCHEDULER_THREAD_H