#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(); }