Skip to content

Commit

Permalink
Tickless scheduler step 3: Correct for tick lengths.
Browse files Browse the repository at this point in the history
Collect the error in the tick rate and correct the current tick to be a
close approximation of the value that we'd have with a ticky scheduler.
  • Loading branch information
davidchisnall committed Apr 15, 2024
1 parent b93f513 commit 47360a9
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 6 deletions.
1 change: 1 addition & 0 deletions sdk/core/scheduler/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ namespace sched
std::tie(schedNeeded, std::ignore, std::ignore) =
futex_wake(Capability{&word}.address());
});
tick = schedNeeded;
break;
case MCAUSE_THREAD_EXIT:
// Make the current thread non-runnable.
Expand Down
19 changes: 19 additions & 0 deletions sdk/core/scheduler/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,12 @@ namespace
schedule = true;
}
}
// If this is the same priority as the current thread, we may need
// to update the timer.
if (priority >= highestPriority)
{
schedule = true;
}
if (reason == WakeReason::Timer || reason == WakeReason::Delete)
{
multiWaiter = nullptr;
Expand Down Expand Up @@ -516,6 +522,19 @@ namespace
return state == ThreadState::Ready;
}

/**
* Returns true if there are other runnable threads with the same
* priority as this thread.
*/
bool has_priority_peers()
{
Debug::Assert(state == ThreadState::Ready,
"Checking for peers on thread that is in state {}, "
"not ready",
state);
return next != this;
}

~ThreadImpl()
{
// We have static definition of threads. We only create threads in
Expand Down
37 changes: 31 additions & 6 deletions sdk/core/scheduler/timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ namespace

class Timer final : private TimerCore
{
inline static uint64_t lastTickTime = 0;
inline static uint64_t lastTickTime = 0;
inline static uint32_t accumulatedTickError = 0;

public:
static void interrupt_setup()
{
Expand All @@ -41,25 +43,48 @@ namespace

static void update()
{

if (Thread::waitingList == nullptr)
auto *thread = Thread::current_get();
bool waitingListIsEmpty = ((Thread::waitingList == nullptr) ||
(Thread::waitingList->expiryTime == -1));
bool threadHasNoPeers =
(thread == nullptr) || (!thread->has_priority_peers());
if (waitingListIsEmpty && threadHasNoPeers)
{
Debug::log("No threads waiting on timer");
clear();
}
else
{
setnext(TIMERCYCLES_PER_TICK * (Thread::waitingList->expiryTime - Thread::ticksSinceBoot));
uint64_t ticksToWait = waitingListIsEmpty
? 1
: (Thread::waitingList->expiryTime -
Thread::ticksSinceBoot);
setnext(TIMERCYCLES_PER_TICK * ticksToWait);
}
}

static void expiretimers()
{
// TODO: Should be reading the timer's time, not the core's time.
// They are currently the same value, but that's not guaranteed.
uint64_t now = rdcycle64();
uint64_t now = rdcycle64();
uint32_t elapsed = now - lastTickTime;
int32_t error = elapsed % TIMERCYCLES_PER_TICK;
if (elapsed < TIMERCYCLES_PER_TICK)
{
error = TIMERCYCLES_PER_TICK - error;
}
accumulatedTickError += error;
int32_t errorDirection = accumulatedTickError < 0 ? -1 : 1;
int32_t absoluteError = accumulatedTickError * errorDirection;
if (absoluteError >= TIMERCYCLES_PER_TICK)
{
Thread::ticksSinceBoot += errorDirection;
accumulatedTickError += TIMERCYCLES_PER_TICK * -errorDirection;
}
lastTickTime = now;
Thread::ticksSinceBoot += std::max(1U, elapsed / TIMERCYCLES_PER_TICK);
Thread::ticksSinceBoot +=
std::max(1U, elapsed / TIMERCYCLES_PER_TICK);
if (Thread::waitingList == nullptr)
{
return;
Expand Down

0 comments on commit 47360a9

Please sign in to comment.