From f5d130f832ac257e50457de9ed7bcb50ffa51398 Mon Sep 17 00:00:00 2001 From: Gleb Koval Date: Thu, 10 Oct 2024 11:28:35 +0100 Subject: [PATCH] Implement non-busy timer_sleep (task 0) --- src/devices/timer.c | 51 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/src/devices/timer.c b/src/devices/timer.c index 08ffe61..4bb602e 100644 --- a/src/devices/timer.c +++ b/src/devices/timer.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "devices/pit.h" #include "threads/interrupt.h" #include "threads/synch.h" @@ -17,6 +18,15 @@ #error TIMER_FREQ <= 1000 recommended #endif +struct asleep_thread +{ + int64_t end_at; /* Number of timer ticks to stop sleeping at. */ + struct semaphore semaphore; /* Semaphore used to block the thread. */ + struct list_elem elem; /* List element. */ +}; +/* List of threads that are sleeping. */ +static struct list sleeping_threads; + /* Number of timer ticks since OS booted. */ static int64_t ticks; @@ -29,6 +39,9 @@ static bool too_many_loops (unsigned loops); static void busy_wait (int64_t loops); static void real_time_sleep (int64_t num, int32_t denom); static void real_time_delay (int64_t num, int32_t denom); +static bool sleeping_threads_less (const struct list_elem *a, + const struct list_elem *b, + void *aux UNUSED); /* Sets up the timer to interrupt TIMER_FREQ times per second, and registers the corresponding interrupt. */ @@ -36,6 +49,7 @@ void timer_init (void) { pit_configure_channel (0, 2, TIMER_FREQ); + list_init (&sleeping_threads); intr_register_ext (0x20, timer_interrupt, "8254 Timer"); } @@ -89,11 +103,25 @@ timer_elapsed (int64_t then) void timer_sleep (int64_t ticks) { + enum intr_level old_level; int64_t start = timer_ticks (); ASSERT (intr_get_level () == INTR_ON); - while (timer_elapsed (start) < ticks) - thread_yield (); + if (ticks < 0) + return; + + struct asleep_thread st; + st.end_at = start + ticks; + sema_init (&st.semaphore, 0); + + old_level = intr_disable (); + list_insert_ordered (&sleeping_threads, &st.elem, &sleeping_threads_less, + NULL); + intr_set_level (old_level); + sema_down (&st.semaphore); + old_level = intr_disable (); + list_remove (&st.elem); + intr_set_level (old_level); } /* Sleeps for approximately MS milliseconds. Interrupts must be @@ -171,6 +199,15 @@ static void timer_interrupt (struct intr_frame *args UNUSED) { ticks++; + for (struct list_elem *e = list_begin (&sleeping_threads); + e != list_end (&sleeping_threads); e = list_next (e)) + { + struct asleep_thread *st = list_entry (e, struct asleep_thread, elem); + if (ticks >= st->end_at) + sema_up (&st->semaphore); + else + break; + } thread_tick (); } @@ -244,3 +281,13 @@ real_time_delay (int64_t num, int32_t denom) ASSERT (denom % 1000 == 0); busy_wait (loops_per_tick * num / 1000 * TIMER_FREQ / (denom / 1000)); } + +/* list_less_func for sleeping_threads list */ +bool +sleeping_threads_less (const struct list_elem *a, const struct list_elem *b, + void *aux UNUSED) +{ + struct asleep_thread *sta = list_entry (a, struct asleep_thread, elem); + struct asleep_thread *stb = list_entry (b, struct asleep_thread, elem); + return sta->end_at < stb->end_at; +}