11use alloc:: boxed:: Box ;
2+ use alloc:: collections:: binary_heap:: BinaryHeap ;
23use alloc:: collections:: VecDeque ;
34use alloc:: sync:: Arc ;
45use alloc:: task:: Wake ;
@@ -8,10 +9,10 @@ use core::future::{poll_fn, Future};
89use core:: pin:: Pin ;
910use core:: sync:: atomic:: { AtomicBool , AtomicU64 , Ordering } ;
1011use core:: task:: { Context , Poll , Waker } ;
11- use futures :: channel :: oneshot ;
12+ use core :: time :: Duration ;
1213
14+ use futures:: channel:: oneshot;
1315use spin:: Mutex ;
14- use uefi:: boot:: { EventType , TimerTrigger , Tpl } ;
1516use uefi:: proto:: console:: text:: Color ;
1617
1718use crate :: os:: send_wrapper:: SendWrapper ;
@@ -53,10 +54,58 @@ impl Wake for Task {
5354 }
5455}
5556
56- static EXECUTOR : Mutex < Executor > = Mutex :: new ( Executor {
57- ready_tasks : VecDeque :: new ( ) ,
58- tasks : vec ! [ ] ,
59- } ) ;
57+ #[ derive( Clone ) ]
58+ pub ( super ) struct WrappedWaker ( Arc < Mutex < Option < Waker > > > ) ;
59+
60+ impl WrappedWaker {
61+ pub ( super ) fn empty ( ) -> Self {
62+ Self ( Arc :: new ( Mutex :: new ( None ) ) )
63+ }
64+
65+ pub ( super ) fn clear ( & self ) {
66+ * self . 0 . lock ( ) = None ;
67+ }
68+
69+ fn wake ( self ) {
70+ let mut w = self . 0 . lock ( ) ;
71+ if let Some ( w) = w. take ( ) {
72+ w. wake ( ) ;
73+ }
74+ }
75+
76+ fn replace ( & self , waker : Waker ) -> bool {
77+ let mut w = self . 0 . lock ( ) ;
78+ let was_present = w. is_some ( ) ;
79+ * w = Some ( waker) ;
80+ was_present
81+ }
82+ }
83+
84+ struct TimedWait {
85+ wake_at : i64 ,
86+ waker : WrappedWaker ,
87+ }
88+
89+ impl PartialEq for TimedWait {
90+ fn eq ( & self , other : & Self ) -> bool {
91+ self . wake_at == other. wake_at
92+ }
93+ }
94+
95+ impl Eq for TimedWait { }
96+
97+ impl PartialOrd for TimedWait {
98+ fn partial_cmp ( & self , other : & Self ) -> Option < core:: cmp:: Ordering > {
99+ Some ( self . cmp ( other) )
100+ }
101+ }
102+
103+ impl Ord for TimedWait {
104+ fn cmp ( & self , other : & Self ) -> core:: cmp:: Ordering {
105+ // Reversed order: min-heap.
106+ other. wake_at . cmp ( & self . wake_at )
107+ }
108+ }
60109
61110pub struct JoinHandle < T > ( oneshot:: Receiver < T > ) ;
62111
@@ -66,8 +115,16 @@ impl<T> JoinHandle<T> {
66115 }
67116}
68117
118+ static EXECUTOR : Mutex < Executor > = Mutex :: new ( Executor {
119+ wake_on_interrupt : vec ! [ ] ,
120+ timed_wait : BinaryHeap :: new ( ) ,
121+ ready_tasks : VecDeque :: new ( ) ,
122+ tasks : vec ! [ ] ,
123+ } ) ;
124+
69125pub struct Executor {
70- // TODO(veluca): scheduling.
126+ wake_on_interrupt : Vec < WrappedWaker > ,
127+ timed_wait : BinaryHeap < TimedWait > ,
71128 ready_tasks : VecDeque < Arc < Task > > ,
72129 tasks : Vec < Arc < Task > > ,
73130}
@@ -81,7 +138,7 @@ impl Executor {
81138 assert ! ( ( w - 1 ) . is_multiple_of( TASK_LEN + 1 ) ) ;
82139 let num_w = ( w - 1 ) / ( TASK_LEN + 1 ) ;
83140 let mut last = Timer :: micros ( ) as u64 ;
84- Self :: sleep_us ( 100_000 ) . await ;
141+ Self :: sleep ( Duration :: from_millis ( 100 ) ) . await ;
85142 loop {
86143 draw_area. clear ( ) ;
87144 let cur = Timer :: micros ( ) as u64 ;
@@ -161,30 +218,76 @@ impl Executor {
161218 tasks. retain ( |t| !t. done . load ( Ordering :: Relaxed ) ) ;
162219 }
163220 last = Timer :: micros ( ) as u64 ;
164- Self :: sleep_us ( 1_000_000 ) . await ;
221+ Self :: sleep ( Duration :: from_secs ( 1 ) ) . await
165222 }
166223 }
167224
168225 pub fn run ( ) -> ! {
169226 Self :: spawn ( "[show_tasks]" , Self :: draw_tasks ( ) ) ;
227+
228+ // Maximum amount of microseconds between wakeups of interrupt-based wakers.
229+ const INTERRUPT_MICROS : i64 = 500 ;
230+
231+ let mut last_interrupt_wakeup = Timer :: micros ( ) ;
232+
233+ let mut do_wake = |force_interrupt_wake| {
234+ // Wake timed-waiting tasks.
235+ loop {
236+ let waker = {
237+ let mut ex = EXECUTOR . lock ( ) ;
238+ let Some ( w) = ex. timed_wait . peek ( ) else {
239+ break ;
240+ } ;
241+ if w. wake_at > Timer :: micros ( ) {
242+ break ;
243+ }
244+ let w = ex. timed_wait . pop ( ) . unwrap ( ) ;
245+ w. waker
246+ } ;
247+ waker. wake ( ) ;
248+ }
249+ // Since we don't notice interrupts that happened while we are not hlt-ing,
250+ // make sure that we wake up all the interrupt-based waiting tasks every at
251+ // most INTERRUPT_MICROS micros to make it unlikely to miss interrupts.
252+ if last_interrupt_wakeup + INTERRUPT_MICROS <= Timer :: micros ( ) || force_interrupt_wake {
253+ last_interrupt_wakeup = Timer :: micros ( ) ;
254+ let to_wake = core:: mem:: take ( & mut EXECUTOR . lock ( ) . wake_on_interrupt ) ;
255+ for w in to_wake {
256+ w. wake ( ) ;
257+ }
258+ }
259+ } ;
260+
170261 loop {
171- let task = EXECUTOR
172- . lock ( )
173- . ready_tasks
174- . pop_front ( )
175- . expect ( "Executor should never run out of ready tasks" ) ;
176-
177- task. in_queue . store ( false , Ordering :: Relaxed ) ;
178- let waker = Waker :: from ( task. clone ( ) ) ;
179- let mut context = Context :: from_waker ( & waker) ;
180- let mut fut = task. future . try_lock ( ) . unwrap ( ) ;
181- let begin = Timer :: micros ( ) ;
182- let done = fut. 0 . as_mut ( ) . poll ( & mut context) ;
183- let end = Timer :: micros ( ) ;
184- task. micros
185- . fetch_add ( ( end - begin) as u64 , Ordering :: Relaxed ) ;
186- if done. is_ready ( ) {
187- task. done . swap ( true , Ordering :: Relaxed ) ;
262+ let task = EXECUTOR . lock ( ) . ready_tasks . pop_front ( ) ;
263+ if let Some ( task) = task {
264+ {
265+ task. in_queue . store ( false , Ordering :: Relaxed ) ;
266+ let waker = Waker :: from ( task. clone ( ) ) ;
267+ let mut context = Context :: from_waker ( & waker) ;
268+ let mut fut = task. future . try_lock ( ) . unwrap ( ) ;
269+ let begin = Timer :: micros ( ) ;
270+ let done = fut. 0 . as_mut ( ) . poll ( & mut context) ;
271+ let end = Timer :: micros ( ) ;
272+ task. micros
273+ . fetch_add ( ( end - begin) as u64 , Ordering :: Relaxed ) ;
274+ if done. is_ready ( ) {
275+ task. done . swap ( true , Ordering :: Relaxed ) ;
276+ }
277+ }
278+ do_wake ( false ) ;
279+ } else {
280+ do_wake ( false ) ;
281+ if !EXECUTOR . lock ( ) . ready_tasks . is_empty ( ) {
282+ continue ;
283+ }
284+ // If we still don't have anything ready, sleep until the next interrupt.
285+ // SAFETY: hlt is available on all reasonable x86 processors and has no safety
286+ // requirements.
287+ unsafe {
288+ core:: arch:: asm!( "hlt" ) ;
289+ }
290+ do_wake ( true ) ;
188291 }
189292 }
190293 }
@@ -204,27 +307,39 @@ impl Executor {
204307 } )
205308 }
206309
207- pub fn sleep_us ( us : u64 ) -> impl Future < Output = ( ) > {
208- let tgt = Timer :: micros ( ) as u64 + us;
310+ // Note: there are no guarantees on whether the amount of time we will sleep for
311+ // will be exceeded.
312+ pub fn sleep ( time : Duration ) -> impl Future < Output = ( ) > {
313+ let tgt = Timer :: micros ( ) + time. as_micros ( ) as i64 ;
314+ let ww = WrappedWaker :: empty ( ) ;
209315 poll_fn ( move |cx| {
210- let now = Timer :: micros ( ) as u64 ;
316+ let now = Timer :: micros ( ) ;
211317 if now >= tgt {
212318 Poll :: Ready ( ( ) )
213319 } else {
214- // TODO(veluca): actually suspend the task.
215- cx. waker ( ) . wake_by_ref ( ) ;
320+ Self :: wake_at_micros ( tgt, cx. waker ( ) . clone ( ) , & ww) ;
216321 Poll :: Pending
217322 }
218323 } )
219324 }
220325
221- /// **WARNING**: this function halts all tasks
222- pub fn deep_sleep_us ( us : u64 ) {
223- // SAFETY: we are not using a callback
224- let e =
225- unsafe { uefi:: boot:: create_event ( EventType :: TIMER , Tpl :: NOTIFY , None , None ) . unwrap ( ) } ;
226- uefi:: boot:: set_timer ( & e, TimerTrigger :: Relative ( 10 * us) ) . unwrap ( ) ;
227- uefi:: boot:: wait_for_event ( & mut [ e] ) . unwrap ( ) ;
326+ // Wakes a task as soon as *any* interrupt is received.
327+ pub ( super ) fn wake_on_interrupt ( waker : Waker , previous_waker : & WrappedWaker ) {
328+ if !previous_waker. replace ( waker) {
329+ EXECUTOR
330+ . lock ( )
331+ . wake_on_interrupt
332+ . push ( previous_waker. clone ( ) ) ;
333+ }
334+ }
335+
336+ pub ( super ) fn wake_at_micros ( micros : i64 , waker : Waker , previous_waker : & WrappedWaker ) {
337+ if !previous_waker. replace ( waker) {
338+ EXECUTOR . lock ( ) . timed_wait . push ( TimedWait {
339+ wake_at : micros,
340+ waker : previous_waker. clone ( ) ,
341+ } ) ;
342+ }
228343 }
229344
230345 /// Spawn a new task.
0 commit comments