Skip to content

Commit 3c70f7a

Browse files
committed
Implement an actual scheduler.
As disk operations are still blocking, it doesn't quite work as well as one would want, but it's a start.
1 parent bf05565 commit 3c70f7a

11 files changed

Lines changed: 220 additions & 82 deletions

File tree

pixie-uefi/src/flash.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use core::cell::{Cell, RefCell};
55
use core::fmt::Write;
66
use core::mem;
77
use core::net::SocketAddrV4;
8+
use core::time::Duration;
89

910
use futures::future::{select, Either};
1011
use log::info;
@@ -120,7 +121,7 @@ pub async fn flash(server_addr: SocketAddrV4) -> Result<()> {
120121
let draw_task = async {
121122
let mut last_stats = stats.borrow().clone();
122123
while !done.get() {
123-
Executor::sleep_us(100_000).await;
124+
Executor::sleep(Duration::from_millis(100)).await;
124125
let stats = stats.borrow().clone();
125126
if stats == last_stats {
126127
continue;
@@ -176,7 +177,7 @@ pub async fn flash(server_addr: SocketAddrV4) -> Result<()> {
176177
);
177178
while !chunks_info.is_empty() {
178179
let recv = Box::pin(socket.recv_from(&mut buf));
179-
let sleep = Box::pin(Executor::sleep_us(100_000));
180+
let sleep = Box::pin(Executor::sleep(Duration::from_millis(100)));
180181
match select(recv, sleep).await {
181182
Either::Left(((buf, _addr), _)) => {
182183
stats.borrow_mut().pack_recv += 1;

pixie-uefi/src/main.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ extern crate alloc;
77

88
use alloc::boxed::Box;
99
use core::net::{Ipv4Addr, SocketAddrV4};
10+
use core::time::Duration;
1011

1112
use futures::future::{self, Either};
1213
use pixie_shared::{Action, TcpRequest, UdpRequest, ACTION_PORT, PING_PORT};
@@ -43,7 +44,7 @@ async fn server_discover() -> Result<SocketAddrV4> {
4344
socket
4445
.send_to(SocketAddrV4::new(Ipv4Addr::BROADCAST, ACTION_PORT), &msg)
4546
.await?;
46-
Executor::sleep_us(1_000_000).await;
47+
Executor::sleep(Duration::from_secs(1)).await;
4748
})
4849
};
4950

@@ -70,7 +71,7 @@ async fn shutdown() -> ! {
7071
export_cov::export().await;
7172

7273
log::info!("Shutting down...");
73-
Executor::sleep_us(1_000_000).await;
74+
Executor::sleep(Duration::from_secs(1)).await;
7475
power_control::shutdown()
7576
}
7677

@@ -108,7 +109,7 @@ async fn run() -> Result<()> {
108109
.send_to(SocketAddrV4::new(*server.ip(), PING_PORT), b"pixie")
109110
.await
110111
.unwrap();
111-
Executor::sleep_us(10_000_000).await;
112+
Executor::sleep(Duration::from_secs(10)).await;
112113
}
113114
});
114115

@@ -132,11 +133,7 @@ async fn run() -> Result<()> {
132133
log::warn!("Started waiting for another command...");
133134
}
134135
last_was_wait = true;
135-
const WAIT_10MSECS: u64 = 50;
136-
for _ in 0..WAIT_10MSECS {
137-
Executor::deep_sleep_us(10_000);
138-
Executor::sched_yield().await;
139-
}
136+
Executor::sleep(Duration::from_millis(500)).await;
140137
} else {
141138
last_was_wait = false;
142139
log::info!("Command: {command:?}");

pixie-uefi/src/os/executor.rs

Lines changed: 136 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use alloc::boxed::Box;
2+
use alloc::collections::binary_heap::BinaryHeap;
23
use alloc::collections::VecDeque;
34
use alloc::sync::Arc;
45
use alloc::task::Wake;
@@ -8,10 +9,11 @@ use core::future::{poll_fn, Future};
89
use core::pin::Pin;
910
use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
1011
use 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;
1316
use spin::Mutex;
14-
use uefi::boot::{EventType, TimerTrigger, Tpl};
1517
use uefi::proto::console::text::Color;
1618

1719
use 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

6186
pub 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+
69101
pub struct Executor {
70-
// TODO(veluca): scheduling.
102+
wake_on_interrupt: Vec<WrappedWaker>,
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+
let task = EXECUTOR.lock().ready_tasks.pop_front();
239+
if let Some(task) = task {
240+
{
241+
task.in_queue.store(false, Ordering::Relaxed);
242+
let waker = Waker::from(task.clone());
243+
let mut context = Context::from_waker(&waker);
244+
let mut fut = task.future.try_lock().unwrap();
245+
let begin = Timer::micros();
246+
let done = fut.0.as_mut().poll(&mut context);
247+
let end = Timer::micros();
248+
task.micros
249+
.fetch_add((end - begin) as u64, Ordering::Relaxed);
250+
if done.is_ready() {
251+
task.done.swap(true, Ordering::Relaxed);
252+
}
253+
}
254+
do_wake(false);
255+
} else {
256+
do_wake(false);
257+
if !EXECUTOR.lock().ready_tasks.is_empty() {
258+
continue;
259+
}
260+
// If we still 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,46 @@ 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, previous_waker: &mut Option<WrappedWaker>) {
304+
if !previous_waker.is_some() {
305+
let w = Arc::new(AtomicWaker::new());
306+
EXECUTOR.lock().wake_on_interrupt.push(w.clone());
307+
*previous_waker = Some(w);
308+
}
309+
previous_waker.as_ref().unwrap().register(waker);
310+
}
311+
312+
pub(super) fn wake_at_micros(
313+
micros: i64,
314+
waker: &Waker,
315+
previous_waker: &mut Option<WrappedWaker>,
316+
) {
317+
if !previous_waker.is_some() {
318+
let w = Arc::new(AtomicWaker::new());
319+
EXECUTOR.lock().timed_wait.push(TimedWait {
320+
wake_at: micros,
321+
waker: w.clone(),
322+
});
323+
*previous_waker = Some(w);
324+
}
325+
previous_waker.as_ref().unwrap().register(waker);
228326
}
229327

230328
/// Spawn a new task.

pixie-uefi/src/os/input.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use uefi::boot::ScopedProtocol;
77
use uefi::proto::console::text::{Input, Key};
88

99
use crate::os::error::Result;
10+
use crate::os::executor::Executor;
1011
use crate::os::send_wrapper::SendWrapper;
1112

1213
static INPUT: Lazy<Mutex<SendWrapper<ScopedProtocol<Input>>>> = Lazy::new(|| {
@@ -16,13 +17,14 @@ static INPUT: Lazy<Mutex<SendWrapper<ScopedProtocol<Input>>>> = Lazy::new(|| {
1617
});
1718

1819
pub fn read_key() -> impl Future<Output = Result<Key>> {
20+
let mut ww = None;
1921
poll_fn(move |cx| {
2022
let key = INPUT.lock().read_key();
2123
match key {
2224
Err(e) => Poll::Ready(Err(e.into())),
2325
Ok(Some(key)) => Poll::Ready(Ok(key)),
2426
Ok(None) => {
25-
cx.waker().wake_by_ref();
27+
Executor::wake_on_interrupt(cx.waker(), &mut ww);
2628
Poll::Pending
2729
}
2830
}

pixie-uefi/src/os/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use core::ffi::c_void;
22
use core::future::Future;
33
use core::ptr::NonNull;
44
use core::sync::atomic::{AtomicBool, Ordering};
5+
use core::time::Duration;
56

67
use uefi::boot::{EventType, Tpl};
78
use uefi::{Event, Status};
@@ -72,7 +73,7 @@ where
7273
break;
7374
}
7475

75-
Executor::sleep_us(30_000_000).await;
76+
Executor::sleep(Duration::from_secs(30)).await;
7677
}
7778
});
7879

pixie-uefi/src/os/net/interface.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use uefi::proto::network::snp::{ReceiveFlags, SimpleNetwork};
55
use uefi::Status;
66

77
use super::ETH_PACKET_SIZE;
8+
use crate::os::send_wrapper::SendWrapper;
89

9-
type Snp = ScopedProtocol<SimpleNetwork>;
10+
type Snp = SendWrapper<ScopedProtocol<SimpleNetwork>>;
1011

1112
pub struct SnpDevice {
1213
snp: Snp,
@@ -15,9 +16,6 @@ pub struct SnpDevice {
1516
rx_buf: [u8; ETH_PACKET_SIZE + 4],
1617
}
1718

18-
// SAFETY: we never create threads anyway.
19-
unsafe impl Send for SnpDevice {}
20-
2119
impl SnpDevice {
2220
pub fn new(snp: Snp) -> SnpDevice {
2321
// Shut down the SNP protocol if needed.

0 commit comments

Comments
 (0)