11use alloc:: boxed:: Box ;
2+ use alloc:: collections:: binary_heap:: BinaryHeap ;
23use alloc:: collections:: VecDeque ;
34use alloc:: sync:: Arc ;
45use alloc:: task:: Wake ;
@@ -11,7 +12,6 @@ use core::task::{Context, Poll, Waker};
1112use futures:: channel:: oneshot;
1213
1314use spin:: Mutex ;
14- use uefi:: boot:: { EventType , TimerTrigger , Tpl } ;
1515use uefi:: proto:: console:: text:: Color ;
1616
1717use crate :: os:: send_wrapper:: SendWrapper ;
@@ -53,10 +53,31 @@ impl Wake for Task {
5353 }
5454}
5555
56- static EXECUTOR : Mutex < Executor > = Mutex :: new ( Executor {
57- ready_tasks : VecDeque :: new ( ) ,
58- tasks : vec ! [ ] ,
59- } ) ;
56+ struct TimedWait {
57+ wake_at : i64 ,
58+ waker : Waker ,
59+ }
60+
61+ impl PartialEq for TimedWait {
62+ fn eq ( & self , other : & Self ) -> bool {
63+ self . wake_at == other. wake_at
64+ }
65+ }
66+
67+ impl Eq for TimedWait { }
68+
69+ impl PartialOrd for TimedWait {
70+ fn partial_cmp ( & self , other : & Self ) -> Option < core:: cmp:: Ordering > {
71+ Some ( self . cmp ( other) )
72+ }
73+ }
74+
75+ impl Ord for TimedWait {
76+ fn cmp ( & self , other : & Self ) -> core:: cmp:: Ordering {
77+ // Reversed order: min-heap.
78+ other. wake_at . cmp ( & self . wake_at )
79+ }
80+ }
6081
6182pub struct JoinHandle < T > ( oneshot:: Receiver < T > ) ;
6283
@@ -66,8 +87,16 @@ impl<T> JoinHandle<T> {
6687 }
6788}
6889
90+ static EXECUTOR : Mutex < Executor > = Mutex :: new ( Executor {
91+ wake_on_interrupt : vec ! [ ] ,
92+ timed_wait : BinaryHeap :: new ( ) ,
93+ ready_tasks : VecDeque :: new ( ) ,
94+ tasks : vec ! [ ] ,
95+ } ) ;
96+
6997pub struct Executor {
70- // TODO(veluca): scheduling.
98+ wake_on_interrupt : Vec < Waker > ,
99+ timed_wait : BinaryHeap < TimedWait > ,
71100 ready_tasks : VecDeque < Arc < Task > > ,
72101 tasks : Vec < Arc < Task > > ,
73102}
@@ -167,24 +196,70 @@ impl Executor {
167196
168197 pub fn run ( ) -> ! {
169198 Self :: spawn ( "[show_tasks]" , Self :: draw_tasks ( ) ) ;
199+
200+ // Maximum amount of microseconds between wakeups of interrupt-based wakers.
201+ const INTERRUPT_MICROS : i64 = 500 ;
202+
203+ let mut last_interrupt_wakeup = Timer :: micros ( ) ;
204+
205+ let mut do_wake = |force_interrupt_wake| {
206+ // Wake timed-waiting tasks.
207+ loop {
208+ let waker = {
209+ let mut ex = EXECUTOR . lock ( ) ;
210+ let Some ( w) = ex. timed_wait . peek ( ) else {
211+ break ;
212+ } ;
213+ if w. wake_at > Timer :: micros ( ) {
214+ break ;
215+ }
216+ let w = ex. timed_wait . pop ( ) . unwrap ( ) ;
217+ w. waker
218+ } ;
219+ waker. wake ( ) ;
220+ }
221+ // Since we don't notice interrupts that happened while we are not hlt-ing,
222+ // make sure that we wake up all the interrupt-based waiting tasks every at
223+ // most INTERRUPT_MICROS micros to make it unlikely to miss interrupts.
224+ if last_interrupt_wakeup + INTERRUPT_MICROS <= Timer :: micros ( ) || force_interrupt_wake {
225+ last_interrupt_wakeup = Timer :: micros ( ) ;
226+ let to_wake = core:: mem:: take ( & mut EXECUTOR . lock ( ) . wake_on_interrupt ) ;
227+ for w in to_wake {
228+ w. wake ( ) ;
229+ }
230+ }
231+ } ;
232+
170233 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 ) ;
234+ let task = EXECUTOR . lock ( ) . ready_tasks . pop_front ( ) ;
235+ if let Some ( task) = task {
236+ {
237+ task. in_queue . store ( false , Ordering :: Relaxed ) ;
238+ let waker = Waker :: from ( task. clone ( ) ) ;
239+ let mut context = Context :: from_waker ( & waker) ;
240+ let mut fut = task. future . try_lock ( ) . unwrap ( ) ;
241+ let begin = Timer :: micros ( ) ;
242+ let done = fut. 0 . as_mut ( ) . poll ( & mut context) ;
243+ let end = Timer :: micros ( ) ;
244+ task. micros
245+ . fetch_add ( ( end - begin) as u64 , Ordering :: Relaxed ) ;
246+ if done. is_ready ( ) {
247+ task. done . swap ( true , Ordering :: Relaxed ) ;
248+ }
249+ }
250+ do_wake ( false ) ;
251+ } else {
252+ do_wake ( false ) ;
253+ if !EXECUTOR . lock ( ) . ready_tasks . is_empty ( ) {
254+ continue ;
255+ }
256+ // If we still don't have anything ready, sleep until the next interrupt.
257+ // SAFETY: hlt is available on all reasonable x86 processors and has no safety
258+ // requirements.
259+ unsafe {
260+ core:: arch:: asm!( "hlt" ) ;
261+ }
262+ do_wake ( true ) ;
188263 }
189264 }
190265 }
@@ -204,27 +279,31 @@ impl Executor {
204279 } )
205280 }
206281
282+ // Note: there are no guarantees on whether the amount of time we will sleep for
283+ // will be exceeded.
207284 pub fn sleep_us ( us : u64 ) -> impl Future < Output = ( ) > {
208- let tgt = Timer :: micros ( ) as u64 + us;
285+ let tgt = Timer :: micros ( ) + us as i64 ;
209286 poll_fn ( move |cx| {
210- let now = Timer :: micros ( ) as u64 ;
287+ let now = Timer :: micros ( ) ;
211288 if now >= tgt {
212289 Poll :: Ready ( ( ) )
213290 } else {
214- // TODO(veluca): actually suspend the task.
215- cx. waker ( ) . wake_by_ref ( ) ;
291+ Self :: wake_at_micros ( tgt, cx. waker ( ) . clone ( ) ) ;
216292 Poll :: Pending
217293 }
218294 } )
219295 }
220296
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 ( ) ;
297+ // Wakes a task as soon as *any* interrupt is received.
298+ pub ( super ) fn wake_on_interrupt ( waker : Waker ) {
299+ EXECUTOR . lock ( ) . wake_on_interrupt . push ( waker) ;
300+ }
301+
302+ pub ( super ) fn wake_at_micros ( micros : i64 , waker : Waker ) {
303+ EXECUTOR . lock ( ) . timed_wait . push ( TimedWait {
304+ wake_at : micros,
305+ waker,
306+ } ) ;
228307 }
229308
230309 /// Spawn a new task.
0 commit comments