Files
bsb2/kernel/sync/bellringer.cc
Niklas Gollenstede 174fe17e89 Handout
2025-10-31 22:37:36 +01:00

68 lines
1.9 KiB
C++

#include "./bellringer.h"
#include "../arch/lapic.h"
#include "../debug/assert.h"
#include "../interrupt/guard.h"
#include "arch/core_interrupt.h"
// check: Checks whether bells are running out of time and rings them if
// necessary
void Bellringer::check(Vault &vault) {
if (Bell *bell = bells.first()) {
bell->counter--;
while ((bell = bells.first()) && bell->counter == 0) {
bells.dequeue();
vault.scheduler.ready(bell->thread);
}
}
}
// job: Give a bell to the bellringer & ring it when the specified time ran out.
void Bellringer::sleep(Vault &vault, unsigned int ms) {
Bell bell_tmp;
Bell *bell = &bell_tmp;
bell->thread = vault.scheduler.active();
assert(bell != nullptr);
unsigned int ticks = ms * 1000 / LAPIC::Timer::interval();
assert(ticks != 0);
if (bells.first() == nullptr) {
// queue is empty; enqueue new bell as first element
bells.enqueue(*bell);
} else {
// To reduce the amount of work done in Bellringer::check (which
// is called frequently), we sort the queue by relative waiting
// times: Each bell is assigned the delta between its waiting
// time and its predecessor's waiting time.
// This allows us to only "tick" the first bell.
Bell *next = nullptr;
Bell *pred = nullptr;
for (Bell *p = bells.first(); p != nullptr; p = bells.next(*p)) {
if (ticks < p->counter) {
next = p;
break;
}
ticks -= p->counter;
pred = p;
}
// If there is a predecessor, enqueue the bell after pred;
// otherwise insert at the beginning.
if (pred != nullptr) {
bells.insertAfter(*pred, *bell);
} else {
bells.insertFirst(*bell);
}
// If we have a predecessor, reduce its waiting time by our waiting
// time, as _next_ will have to wait until we finished waiting.
if (next != nullptr) {
next->counter -= ticks;
}
}
bell->counter = ticks;
vault.scheduler.resume(false);
}
// Are there bells in the queue?
bool Bellringer::bellPending() const { return !bells.is_empty(); }