Skip to content

Commit

Permalink
Switch to smol
Browse files Browse the repository at this point in the history
We were running into tokio's cooperative scheduling, see this bug:

tokio-rs/tokio#2418

I'm sure there's some way to make that work, but in the meantime just switching to smol lets us make progress.

This is the demo I showed at the SF Rust meetup 2022-05-24.
  • Loading branch information
raphlinus committed May 25, 2022
1 parent 9e878a8 commit ab7bdec
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 20 deletions.
6 changes: 5 additions & 1 deletion xilem/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ edition = "2021"
"druid-shell" = { path = "../druid-shell" }
bitflags = "1.3.2"
futures-task = "0.3"
tokio = { version = "1", features = ["full"] }
smol = "1.2.5"

[dev-dependencies]
sha2 = "0.10"
hex = "0.4.3"
21 changes: 17 additions & 4 deletions xilem/examples/scroll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use xilem::{list, scroll_view, App, AppLauncher, View};
use sha2::{Sha256, Digest};
use xilem::{async_list, scroll_view, App, AppLauncher, View};

fn compute_hash(i: usize) -> String {
let mut s = format!("{}", i);
for _ in 0..i {
let mut hasher = Sha256::new();
hasher.update(s.as_bytes());
let result = hasher.finalize();
s = hex::encode(result);
}
s
}

fn app_logic(_: &mut ()) -> impl View<()> {
scroll_view(list(1000, 20.0, |i| format!("{}", i)))
scroll_view(async_list(10_000, 16.0, |i| async move {
format!("{}: {}", i, compute_hash(i))
}))
}

#[tokio::main]
async fn main() {
fn main() {
let app = App::new((), app_logic);
AppLauncher::new(app).run();
}
2 changes: 1 addition & 1 deletion xilem/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ struct MyWaker {

impl ArcWake for MyWaker {
fn wake_by_ref(arc_self: &Arc<Self>) {
println!("path = {:?}", arc_self.id_path);
//println!("path = {:?}", arc_self.id_path);
if arc_self.wake_queue.push_wake(arc_self.id_path.clone()) {
// The clone shouldn't be needed; schedule_idle should be &self I think
arc_self
Expand Down
31 changes: 17 additions & 14 deletions xilem/src/view/async_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use std::{
};

use futures_task::{Context, Poll};
use tokio::task::JoinHandle;
use smol::Task;

use crate::{event::EventResult, id::Id, widget::Pod};

Expand All @@ -42,8 +42,8 @@ pub struct AsyncListState<T, A, V: View<T, A>> {
add_req: Vec<usize>,
remove_req: Vec<usize>,
items: BTreeMap<usize, ItemState<T, A, V>>,
pending: HashMap<Id, (usize, JoinHandle<V>)>,
waking: Vec<(Id, usize, JoinHandle<V>)>,
pending: HashMap<Id, (usize, Task<V>)>,
waking: Vec<(Id, usize, Task<V>)>,
}

struct ItemState<T, A, V: View<T, A>> {
Expand Down Expand Up @@ -109,35 +109,37 @@ where
for i in state.add_req.drain(..) {
// spawn a task to run the callback
let future = (self.callback)(i);
let join_handle = tokio::spawn(Box::pin(future));
let join_handle = smol::spawn(Box::pin(future));
let id = Id::next();
state.pending.insert(id, (i, join_handle));
state.waking.push((id, i, join_handle));
}
state.waking.retain_mut(|(id, i, join_handle)| {
let poll_result = cx.with_id(*id, |cx| {
for (id, i, mut join_handle) in state.waking.drain(..) {
let poll_result = cx.with_id(id, |cx| {
let waker = cx.waker();
let mut future_cx = Context::from_waker(&waker);
Pin::new(join_handle).poll(&mut future_cx)
Pin::new(&mut join_handle).poll(&mut future_cx)
});
match poll_result {
Poll::Ready(v) => {
let child_view = v.unwrap();
let child_view = v;
let (child_id, child_state, child_element) = child_view.build(cx);
element.set_child(*i, Pod::new(child_element));
element.set_child(i, Pod::new(child_element));
state.items.insert(
*i,
i,
ItemState {
id: child_id,
view: child_view,
state: child_state,
},
);
changed = true;
false
}
Poll::Pending => true,
Poll::Pending => {
//println!("poll result id={:?} i={} pending, re-inserting", id, i);
state.pending.insert(id, (i, join_handle));
}
}
});
}
for i in state.remove_req.drain(..) {
element.remove_child(i);
state.items.remove(&i);
Expand All @@ -156,6 +158,7 @@ where
) -> EventResult<A> {
if let Some((id, tl)) = id_path.split_first() {
if let Some((i, join_handle)) = state.pending.remove(id) {
//println!("got async wake of id {:?}, i={}", id, i);
state.waking.push((*id, i, join_handle));
EventResult::RequestRebuild
} else if let Some((_, s)) = state.items.iter_mut().find(|(_, s)| s.id == *id) {
Expand Down

0 comments on commit ab7bdec

Please sign in to comment.