Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First implementation of kernel level timers #591

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AK/SinglyLinkedList.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class SinglyLinkedList {
m_tail->next = node;
m_tail = node;
}

bool contains_slow(const T& value) const
{
for (auto* node = m_head; node; node = node->next) {
Expand All @@ -138,6 +138,7 @@ class SinglyLinkedList {
return false;
}


using Iterator = SinglyLinkedListIterator<SinglyLinkedList, T>;
friend Iterator;
Iterator begin() { return Iterator(m_head); }
Expand Down
22 changes: 16 additions & 6 deletions Kernel/Scheduler.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <AK/SinglyLinkedList.h>
#include <AK/TemporaryChange.h>
#include <Kernel/Arch/i386/PIT.h>
#include <Kernel/Devices/PCSpeaker.h>
Expand Down Expand Up @@ -48,12 +49,13 @@ Thread* g_last_fpu_thread;
Thread* g_finalizer;
static Process* s_colonel_process;
u64 g_uptime;
static u64 s_beep_timeout;
static u64 s_next_timer_due;

struct TaskRedirectionData {
u16 selector;
TSS32 tss;
};

static TaskRedirectionData s_redirection;
static bool s_active;

Expand All @@ -65,7 +67,14 @@ bool Scheduler::is_active()
void Scheduler::beep()
{
PCSpeaker::tone_on(440);
s_beep_timeout = g_uptime + 100;

auto timer = Timer();
timer.expires = g_uptime + 100;
timer.callback = []() {
PCSpeaker::tone_off();
dbgprintf("tone off");
};
add_timer(timer);
}

Thread::FileDescriptionBlocker::FileDescriptionBlocker(const FileDescription& description)
Expand Down Expand Up @@ -523,6 +532,8 @@ void Scheduler::initialize()
// Make sure the colonel uses a smallish time slice.
s_colonel_process->set_priority(Process::IdlePriority);
load_task_register(s_redirection.selector);

g_timer_queue = new SinglyLinkedList<Timer>();
}

void Scheduler::timer_tick(RegisterDump& regs)
Expand All @@ -531,10 +542,9 @@ void Scheduler::timer_tick(RegisterDump& regs)
return;

++g_uptime;

if (s_beep_timeout && g_uptime > s_beep_timeout) {
PCSpeaker::tone_off();
s_beep_timeout = 0;

if ( TimerQueue::s_next_timer_due && g_uptime > TimerQueue::s_next_timer_due) {
TimerQueue::fire_timer();
}

if (current->tick())
Expand Down
1 change: 1 addition & 0 deletions Kernel/Scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

class Process;
class Thread;
struct Timer;
struct RegisterDump;
struct SchedulerData;

Expand Down
71 changes: 71 additions & 0 deletions Kernel/TimerQueue.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include <AK/SinglyLinkedList.h>
#include <Kernel/Scheduler.h>
#include <Kernel/TimerQueue.h>


SinglyLinkedList<Timer>* g_timer_queue;

static u64 s_next_timer_due;
static u64 s_timer_id_count;
void TimerQueue::initialize()
{

g_timer_queue = new SinglyLinkedList<Timer>();
}


u64 TimerQueue::add_timer(Timer& timer)
{
ASSERT(timer.expires > Scheduler::g_uptime);
timer.id = ++s_timer_id_count;
g_timer_queue->sorted_insert_slow(timer);
s_next_timer_due = g_timer_queue->first().expires;
return s_timer_id_count;
}

u64 add_timer(u64 duration, TimeUnit unit, void (callback)())
{
atuo timer = Timer();
timer.expires = g_uptime + duration * unit;
timer.callback = callback;
return add_timer(timer);
}

void TimerQueue::sorted_insert_slow(T&& value)
{
auto* new_node = new Node(move(value));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reaching directly into the SinglyLinkedList class here is very strange.
It would be nicer to implement this by adding an API such as SinglyLinkedList::insert(Iterator&, T&&).
Then you could iterate over the list until you find your insertion point and then call insert() with the iterator you're using.

new_node->value = value;

if (!g_timer_queue->m_head || g_timer_queue->m_head->value > new_node->value) {
new_node->next = g_timer_queue->m_head;
if (!m_head)
m_tail = new_node;
m_head = new_node;
return;
}

Node* curr = m_head;
while (curr->next && curr->next->value < new_node->value)
{
curr = curr->next;
}

new_node->next = curr->next;
curr->next = new_node;
if(g_timer_queue->m_tail == curr)
g_timer_queue->m_tail = new_node;
}

void TimerQueue::fire_timer()
{
ASSERT(s_next_timer_due == g_timer_queue->first().expires);
while (! g_timer_queue->is_empty() && Scheduler::g_uptime > g_timer_queue->first().expires)
{
auto timer = g_timer_queue->take_first();
timer.callback();
}
if (! g_timer_queue->is_empty())
s_next_timer_due = g_timer_queue->first().expires;
else
s_next_timer_due = 0;
}
43 changes: 43 additions & 0 deletions Kernel/TimerQueue.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once

#include <Kernel/Arch/i386/PIT.h>

struct Timer {
u64 id;
u64 expires;
void (*callback)();
bool operator<(const Timer& rhs) const
{
return expires < rhs.expires;
}
bool operator>( const Timer& rhs) const
{
return expires > rhs.expires;
}

bool operator==( const X& rhs) const
{
return id == rhs.id;
}
};

enum TimeUnit {
MS = TICKS_PER_SECOND / 1000,
S = TICKS_PER_SECOND,
M = TICKS_PER_SECOND * 60
};


class TimerQueue {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pretty roundabout way to basically add a lot of global functions.
Instead of having a TimerQueue class with nothing but public static functions, let's use a singleton object:

// In header file:
class TimerQueue {
public:
    static TimerQueue& the();
    u64 add_timer(Timer&);
    u64 add_timer(u64 duration, TimeUnit, void (callback)());
    ...

private:
    SinglyLinkedList<Timer> m_timers;
};

In cpp file:
TimerQueue& TimerQueue::the()
{
    if (!queue)
        queue = new TimerQueue;
    return s_the;
}

Then you can use the single global instance of TimerQueue like so:

TimerQueue::the().add_timer(...);

friend class SinglyLinkedList;
public:
static void initialize();
static u64 add_timer(Timer&);
static u64 add_timer(u64 duration, TimeUnit, void (callback)());
static Timer& get_timer(u64 id);
static bool update_timer(Timer&)
static bool cancel_timer(u64 id);
private:
static void fire_timer();
static void sorted_list_insert_slow()
};