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,11 @@ 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;
15+ use futures:: task:: AtomicWaker ;
1316use spin:: Mutex ;
14- use uefi:: boot:: { EventType , TimerTrigger , Tpl } ;
1517use uefi:: proto:: console:: text:: Color ;
1618
1719use crate :: os:: send_wrapper:: SendWrapper ;
@@ -53,10 +55,33 @@ impl Wake for Task {
5355 }
5456}
5557
56- static EXECUTOR : Mutex < Executor > = Mutex :: new ( Executor {
57- ready_tasks : VecDeque :: new ( ) ,
58- tasks : vec ! [ ] ,
59- } ) ;
58+ pub ( super ) type WrappedWaker = Arc < AtomicWaker > ;
59+
60+ struct TimedWait {
61+ wake_at : i64 ,
62+ waker : WrappedWaker ,
63+ }
64+
65+ impl PartialEq for TimedWait {
66+ fn eq ( & self , other : & Self ) -> bool {
67+ self . wake_at == other. wake_at
68+ }
69+ }
70+
71+ impl Eq for TimedWait { }
72+
73+ impl PartialOrd for TimedWait {
74+ fn partial_cmp ( & self , other : & Self ) -> Option < core:: cmp:: Ordering > {
75+ Some ( self . cmp ( other) )
76+ }
77+ }
78+
79+ impl Ord for TimedWait {
80+ fn cmp ( & self , other : & Self ) -> core:: cmp:: Ordering {
81+ // Reversed order: min-heap.
82+ other. wake_at . cmp ( & self . wake_at )
83+ }
84+ }
6085
6186pub struct JoinHandle < T > ( oneshot:: Receiver < T > ) ;
6287
@@ -66,8 +91,16 @@ impl<T> JoinHandle<T> {
6691 }
6792}
6893
94+ static EXECUTOR : Mutex < Executor > = Mutex :: new ( Executor {
95+ wake_on_interrupt : vec ! [ ] ,
96+ timed_wait : BinaryHeap :: new ( ) ,
97+ ready_tasks : VecDeque :: new ( ) ,
98+ tasks : vec ! [ ] ,
99+ } ) ;
100+
69101pub struct Executor {
70- // TODO(veluca): scheduling.
102+ wake_on_interrupt : Vec < Waker > ,
103+ timed_wait : BinaryHeap < TimedWait > ,
71104 ready_tasks : VecDeque < Arc < Task > > ,
72105 tasks : Vec < Arc < Task > > ,
73106}
@@ -81,7 +114,7 @@ impl Executor {
81114 assert ! ( ( w - 1 ) . is_multiple_of( TASK_LEN + 1 ) ) ;
82115 let num_w = ( w - 1 ) / ( TASK_LEN + 1 ) ;
83116 let mut last = Timer :: micros ( ) as u64 ;
84- Self :: sleep_us ( 100_000 ) . await ;
117+ Self :: sleep ( Duration :: from_millis ( 100 ) ) . await ;
85118 loop {
86119 draw_area. clear ( ) ;
87120 let cur = Timer :: micros ( ) as u64 ;
@@ -161,30 +194,76 @@ impl Executor {
161194 tasks. retain ( |t| !t. done . load ( Ordering :: Relaxed ) ) ;
162195 }
163196 last = Timer :: micros ( ) as u64 ;
164- Self :: sleep_us ( 1_000_000 ) . await ;
197+ Self :: sleep ( Duration :: from_secs ( 1 ) ) . await
165198 }
166199 }
167200
168201 pub fn run ( ) -> ! {
169202 Self :: spawn ( "[show_tasks]" , Self :: draw_tasks ( ) ) ;
203+
204+ // Maximum amount of microseconds between wakeups of interrupt-based wakers.
205+ const INTERRUPT_MICROS : i64 = 500 ;
206+
207+ let mut last_interrupt_wakeup = Timer :: micros ( ) ;
208+
209+ let mut do_wake = |force_interrupt_wake| {
210+ // Wake timed-waiting tasks.
211+ loop {
212+ let waker = {
213+ let mut ex = EXECUTOR . lock ( ) ;
214+ let Some ( w) = ex. timed_wait . peek ( ) else {
215+ break ;
216+ } ;
217+ if w. wake_at > Timer :: micros ( ) {
218+ break ;
219+ }
220+ let w = ex. timed_wait . pop ( ) . unwrap ( ) ;
221+ w. waker
222+ } ;
223+ waker. wake ( ) ;
224+ }
225+ // Since we don't notice interrupts that happened while we are not hlt-ing,
226+ // make sure that we wake up all the interrupt-based waiting tasks every at
227+ // most INTERRUPT_MICROS micros to make it unlikely to miss interrupts.
228+ if last_interrupt_wakeup + INTERRUPT_MICROS <= Timer :: micros ( ) || force_interrupt_wake {
229+ last_interrupt_wakeup = Timer :: micros ( ) ;
230+ let to_wake = core:: mem:: take ( & mut EXECUTOR . lock ( ) . wake_on_interrupt ) ;
231+ for w in to_wake {
232+ w. wake ( ) ;
233+ }
234+ }
235+ } ;
236+
170237 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 ) ;
238+ do_wake ( false ) ;
239+ let task = EXECUTOR . lock ( ) . ready_tasks . pop_front ( ) ;
240+ if let Some ( task) = task {
241+ // It is possible for a done task to end up in the queue (if it wakes
242+ // itself during execution). If that happens, we just remove it from
243+ // the queue here.
244+ if task. done . load ( Ordering :: Relaxed ) {
245+ continue ;
246+ }
247+ task. in_queue . store ( false , Ordering :: Relaxed ) ;
248+ let waker = Waker :: from ( task. clone ( ) ) ;
249+ let mut context = Context :: from_waker ( & waker) ;
250+ let mut fut = task. future . try_lock ( ) . unwrap ( ) ;
251+ let begin = Timer :: micros ( ) ;
252+ let done = fut. 0 . as_mut ( ) . poll ( & mut context) ;
253+ let end = Timer :: micros ( ) ;
254+ task. micros
255+ . fetch_add ( ( end - begin) as u64 , Ordering :: Relaxed ) ;
256+ if done. is_ready ( ) {
257+ task. done . swap ( true , Ordering :: Relaxed ) ;
258+ }
259+ } else {
260+ // If we don't have anything ready, sleep until the next interrupt.
261+ // SAFETY: hlt is available on all reasonable x86 processors and has no safety
262+ // requirements.
263+ unsafe {
264+ core:: arch:: asm!( "hlt" ) ;
265+ }
266+ do_wake ( true ) ;
188267 }
189268 }
190269 }
@@ -204,27 +283,41 @@ impl Executor {
204283 } )
205284 }
206285
207- pub fn sleep_us ( us : u64 ) -> impl Future < Output = ( ) > {
208- let tgt = Timer :: micros ( ) as u64 + us;
286+ // Note: there are no guarantees on whether the amount of time we will sleep for
287+ // will be exceeded.
288+ pub fn sleep ( time : Duration ) -> impl Future < Output = ( ) > {
289+ let tgt = Timer :: micros ( ) + time. as_micros ( ) as i64 ;
290+ let mut ww = None ;
209291 poll_fn ( move |cx| {
210- let now = Timer :: micros ( ) as u64 ;
292+ let now = Timer :: micros ( ) ;
211293 if now >= tgt {
212294 Poll :: Ready ( ( ) )
213295 } else {
214- // TODO(veluca): actually suspend the task.
215- cx. waker ( ) . wake_by_ref ( ) ;
296+ Self :: wake_at_micros ( tgt, cx. waker ( ) , & mut ww) ;
216297 Poll :: Pending
217298 }
218299 } )
219300 }
220301
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 ( ) ;
302+ // Wakes a task as soon as *any* interrupt is received.
303+ pub ( super ) fn wake_on_interrupt ( waker : & Waker ) {
304+ EXECUTOR . lock ( ) . wake_on_interrupt . push ( waker. clone ( ) ) ;
305+ }
306+
307+ pub ( super ) fn wake_at_micros (
308+ micros : i64 ,
309+ waker : & Waker ,
310+ previous_waker : & mut Option < WrappedWaker > ,
311+ ) {
312+ if !previous_waker. is_some ( ) {
313+ let w = Arc :: new ( AtomicWaker :: new ( ) ) ;
314+ EXECUTOR . lock ( ) . timed_wait . push ( TimedWait {
315+ wake_at : micros,
316+ waker : w. clone ( ) ,
317+ } ) ;
318+ * previous_waker = Some ( w) ;
319+ }
320+ previous_waker. as_ref ( ) . unwrap ( ) . register ( waker) ;
228321 }
229322
230323 /// Spawn a new task.
0 commit comments