@@ -73,7 +73,7 @@ the needed state increases with each added step.
|
|||||||
|
|
||||||
This is the model used in Rust today. It has a few notable advantages:
|
This is the model used in Rust today. It has a few notable advantages:
|
||||||
|
|
||||||
1. It's easy to convert normal Rust code to a stackless coroutine using using
|
1. It's easy to convert normal Rust code to a stackless coroutine using
|
||||||
async/await as keywords (it can even be done using a macro).
|
async/await as keywords (it can even be done using a macro).
|
||||||
2. No need for context switching and saving/restoring CPU state
|
2. No need for context switching and saving/restoring CPU state
|
||||||
3. No need to handle dynamic stack allocation
|
3. No need to handle dynamic stack allocation
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ On a more serious note, I feel obliged to mention that there are valid reasons
|
|||||||
for the names that were chosen. Naming is not easy, and I considered renaming
|
for the names that were chosen. Naming is not easy, and I considered renaming
|
||||||
`Unpin` and `!Unpin` in this book to make them easier to reason about.
|
`Unpin` and `!Unpin` in this book to make them easier to reason about.
|
||||||
|
|
||||||
However, an experienced member of the Rust community convinced me that that there
|
However, an experienced member of the Rust community convinced me that there
|
||||||
are just too many nuances and edge-cases to consider which are easily overlooked when
|
are just too many nuances and edge-cases to consider which are easily overlooked when
|
||||||
naively giving these markers different names, and I'm convinced that we'll
|
naively giving these markers different names, and I'm convinced that we'll
|
||||||
just have to get used to them and use them as is.
|
just have to get used to them and use them as is.
|
||||||
|
|||||||
@@ -28,9 +28,17 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
future::Future, sync::{ mpsc::{channel, Sender}, Arc, Mutex, Condvar},
|
collections::HashMap,
|
||||||
task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, mem, pin::Pin,
|
future::Future,
|
||||||
thread::{self, JoinHandle}, time::{Duration, Instant}, collections::HashMap
|
mem,
|
||||||
|
pin::Pin,
|
||||||
|
sync::{
|
||||||
|
mpsc::{channel, Sender},
|
||||||
|
Arc, Condvar, Mutex,
|
||||||
|
},
|
||||||
|
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
// ============================= EXECUTOR ====================================
|
// ============================= EXECUTOR ====================================
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -39,9 +47,9 @@ struct Parker(Mutex<bool>, Condvar);
|
|||||||
impl Parker {
|
impl Parker {
|
||||||
fn park(&self) {
|
fn park(&self) {
|
||||||
let mut resumable = self.0.lock().unwrap();
|
let mut resumable = self.0.lock().unwrap();
|
||||||
while !*resumable {
|
while !*resumable {
|
||||||
resumable = self.1.wait(resumable).unwrap();
|
resumable = self.1.wait(resumable).unwrap();
|
||||||
}
|
}
|
||||||
*resumable = false;
|
*resumable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +61,9 @@ impl Parker {
|
|||||||
|
|
||||||
fn block_on<F: Future>(mut future: F) -> F::Output {
|
fn block_on<F: Future>(mut future: F) -> F::Output {
|
||||||
let parker = Arc::new(Parker::default());
|
let parker = Arc::new(Parker::default());
|
||||||
let mywaker = Arc::new(MyWaker { parker: parker.clone() });
|
let mywaker = Arc::new(MyWaker {
|
||||||
|
parker: parker.clone(),
|
||||||
|
});
|
||||||
let waker = mywaker_into_waker(Arc::into_raw(mywaker));
|
let waker = mywaker_into_waker(Arc::into_raw(mywaker));
|
||||||
let mut cx = Context::from_waker(&waker);
|
let mut cx = Context::from_waker(&waker);
|
||||||
|
|
||||||
@@ -116,8 +126,8 @@ impl Future for Task {
|
|||||||
if r.is_ready(self.id) {
|
if r.is_ready(self.id) {
|
||||||
*r.tasks.get_mut(&self.id).unwrap() = TaskState::Finished;
|
*r.tasks.get_mut(&self.id).unwrap() = TaskState::Finished;
|
||||||
Poll::Ready(self.id)
|
Poll::Ready(self.id)
|
||||||
} else if r.tasks.contains_key(&self.id) {
|
} else if let std::collections::hash_map::Entry::Occupied(mut e) = r.tasks.entry(self.id) {
|
||||||
r.tasks.insert(self.id, TaskState::NotReady(cx.waker().clone()));
|
e.insert(TaskState::NotReady(cx.waker().clone()));
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
} else {
|
} else {
|
||||||
r.register(self.data, cx.waker().clone(), self.id);
|
r.register(self.data, cx.waker().clone(), self.id);
|
||||||
@@ -169,7 +179,9 @@ impl Reactor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handles.into_iter().for_each(|handle| handle.join().unwrap());
|
handles
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|handle| handle.join().unwrap());
|
||||||
});
|
});
|
||||||
reactor.lock().map(|mut r| r.handle = Some(handle)).unwrap();
|
reactor.lock().map(|mut r| r.handle = Some(handle)).unwrap();
|
||||||
reactor
|
reactor
|
||||||
@@ -180,7 +192,7 @@ impl Reactor {
|
|||||||
match mem::replace(state, TaskState::Ready) {
|
match mem::replace(state, TaskState::Ready) {
|
||||||
TaskState::NotReady(waker) => waker.wake(),
|
TaskState::NotReady(waker) => waker.wake(),
|
||||||
TaskState::Finished => panic!("Called 'wake' twice on task: {}", id),
|
TaskState::Finished => panic!("Called 'wake' twice on task: {}", id),
|
||||||
_ => unreachable!()
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,10 +204,10 @@ impl Reactor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_ready(&self, id: usize) -> bool {
|
fn is_ready(&self, id: usize) -> bool {
|
||||||
self.tasks.get(&id).map(|state| match state {
|
self.tasks
|
||||||
TaskState::Ready => true,
|
.get(&id)
|
||||||
_ => false,
|
.map(|state| matches!(state, TaskState::Ready))
|
||||||
}).unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user