Return tasks from spawn and spawn_stream

Also, eliminate the background spawning methods. We can spawn futures
on the executor and then spawn those on the app if we need to wait for
the result of running one.
This commit is contained in:
Nathan Sobo 2021-03-18 20:10:32 -06:00
parent f849857309
commit e809d6119a
7 changed files with 317 additions and 267 deletions

21
Cargo.lock generated
View file

@ -782,6 +782,7 @@ dependencies = [
"parking_lot", "parking_lot",
"pathfinder_color", "pathfinder_color",
"pathfinder_geometry", "pathfinder_geometry",
"pin-project",
"rand 0.8.3", "rand 0.8.3",
"smallvec", "smallvec",
"smol", "smol",
@ -1076,6 +1077,26 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "pin-project"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.4" version = "0.2.4"

View file

@ -12,6 +12,7 @@ ordered-float = "2.1.1"
parking_lot = "0.11.1" parking_lot = "0.11.1"
pathfinder_color = "0.5" pathfinder_color = "0.5"
pathfinder_geometry = "0.5" pathfinder_geometry = "0.5"
pin-project = "1.0.5"
rand = "0.8.3" rand = "0.8.3"
smallvec = "1.6.1" smallvec = "1.6.1"
smol = "1.2" smol = "1.2"

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
elements::Element, elements::Element,
executor::{self}, executor::{self, ForegroundTask},
keymap::{self, Keystroke}, keymap::{self, Keystroke},
platform::{self, App as _}, platform::{self, App as _},
util::post_inc, util::post_inc,
@ -292,7 +292,8 @@ pub struct MutableAppContext {
HashMap<usize, Box<dyn FnMut(WindowInvalidation, &mut MutableAppContext)>>, HashMap<usize, Box<dyn FnMut(WindowInvalidation, &mut MutableAppContext)>>,
foreground: Rc<executor::Foreground>, foreground: Rc<executor::Foreground>,
background: Arc<executor::Background>, background: Arc<executor::Background>,
task_callbacks: HashMap<usize, TaskCallback>, future_handlers: HashMap<usize, FutureHandler>,
stream_handlers: HashMap<usize, StreamHandler>,
task_done: (channel::Sender<usize>, channel::Receiver<usize>), task_done: (channel::Sender<usize>, channel::Receiver<usize>),
pending_effects: VecDeque<Effect>, pending_effects: VecDeque<Effect>,
pending_flushes: usize, pending_flushes: usize,
@ -321,7 +322,8 @@ impl MutableAppContext {
invalidation_callbacks: HashMap::new(), invalidation_callbacks: HashMap::new(),
foreground, foreground,
background: Arc::new(executor::Background::new()), background: Arc::new(executor::Background::new()),
task_callbacks: HashMap::new(), future_handlers: HashMap::new(),
stream_handlers: HashMap::new(),
task_done: channel::unbounded(), task_done: channel::unbounded(),
pending_effects: VecDeque::new(), pending_effects: VecDeque::new(),
pending_flushes: 0, pending_flushes: 0,
@ -869,97 +871,71 @@ impl MutableAppContext {
self.flush_effects(); self.flush_effects();
} }
fn spawn_local<F>(&mut self, future: F) -> usize fn spawn<F, T>(&mut self, future: F) -> (usize, ForegroundTask<Option<T>>)
where where
F: 'static + Future, F: 'static + Future,
T: 'static,
{ {
let task_id = post_inc(&mut self.next_task_id); let task_id = post_inc(&mut self.next_task_id);
let app = self.weak_self.as_ref().unwrap().clone(); let app = self.weak_self.as_ref().unwrap().upgrade().unwrap();
self.foreground let task = self.foreground.spawn(async move {
.spawn(async move { let output = future.await;
let output = future.await; app.borrow_mut()
if let Some(app) = app.upgrade() { .handle_future_output(task_id, Box::new(output))
app.borrow_mut() .map(|result| *result.downcast::<T>().unwrap())
.relay_task_output(task_id, Box::new(output)); });
} (task_id, task)
})
.detach();
task_id
} }
fn spawn_stream_local<F>(&mut self, mut stream: F, done_tx: channel::Sender<()>) -> usize fn spawn_stream<F, T>(&mut self, mut stream: F) -> (usize, ForegroundTask<Option<T>>)
where where
F: 'static + Stream + Unpin, F: 'static + Stream + Unpin,
T: 'static,
{ {
let task_id = post_inc(&mut self.next_task_id); let task_id = post_inc(&mut self.next_task_id);
let app = self.weak_self.as_ref().unwrap().clone(); let app = self.weak_self.as_ref().unwrap().upgrade().unwrap();
self.foreground let task = self.foreground.spawn(async move {
.spawn(async move { loop {
loop { match stream.next().await {
match stream.next().await { Some(item) => {
item @ Some(_) => { let mut app = app.borrow_mut();
if let Some(app) = app.upgrade() { if app.handle_stream_item(task_id, Box::new(item)) {
let mut app = app.borrow_mut();
if app.relay_task_output(task_id, Box::new(item)) {
app.stream_completed(task_id);
break;
}
} else {
break;
}
}
item @ None => {
if let Some(app) = app.upgrade() {
let mut app = app.borrow_mut();
app.relay_task_output(task_id, Box::new(item));
app.stream_completed(task_id);
}
let _ = done_tx.send(()).await;
break; break;
} }
} }
None => {
break;
}
} }
}) }
.detach();
task_id app.borrow_mut()
.stream_completed(task_id)
.map(|result| *result.downcast::<T>().unwrap())
});
(task_id, task)
} }
fn relay_task_output(&mut self, task_id: usize, output: Box<dyn Any>) -> bool { fn handle_future_output(
&mut self,
task_id: usize,
output: Box<dyn Any>,
) -> Option<Box<dyn Any>> {
self.pending_flushes += 1; self.pending_flushes += 1;
let task_callback = self.task_callbacks.remove(&task_id).unwrap(); let future_callback = self.future_handlers.remove(&task_id).unwrap();
let halt = match task_callback { let mut result = None;
TaskCallback::OnModelFromFuture { model_id, callback } => {
match future_callback {
FutureHandler::Model { model_id, callback } => {
if let Some(mut model) = self.ctx.models.remove(&model_id) { if let Some(mut model) = self.ctx.models.remove(&model_id) {
callback( result = Some(callback(model.as_any_mut(), output, self, model_id));
model.as_any_mut(),
output,
self,
model_id,
self.foreground.clone(),
);
self.ctx.models.insert(model_id, model); self.ctx.models.insert(model_id, model);
} }
self.task_done(task_id); self.task_done(task_id);
true
} }
TaskCallback::OnModelFromStream { FutureHandler::View {
model_id,
mut callback,
} => {
if let Some(mut model) = self.ctx.models.remove(&model_id) {
let halt = callback(model.as_any_mut(), output, self, model_id);
self.ctx.models.insert(model_id, model);
self.task_callbacks.insert(
task_id,
TaskCallback::OnModelFromStream { model_id, callback },
);
halt
} else {
true
}
}
TaskCallback::OnViewFromFuture {
window_id, window_id,
view_id, view_id,
callback, callback,
@ -970,14 +946,7 @@ impl MutableAppContext {
.get_mut(&window_id) .get_mut(&window_id)
.and_then(|w| w.views.remove(&view_id)) .and_then(|w| w.views.remove(&view_id))
{ {
callback( result = Some(callback(view.as_mut(), output, self, window_id, view_id));
view.as_mut(),
output,
self,
window_id,
view_id,
self.foreground.clone(),
);
self.ctx self.ctx
.windows .windows
.get_mut(&window_id) .get_mut(&window_id)
@ -986,12 +955,37 @@ impl MutableAppContext {
.insert(view_id, view); .insert(view_id, view);
} }
self.task_done(task_id); self.task_done(task_id);
true
} }
TaskCallback::OnViewFromStream { };
self.flush_effects();
result
}
fn handle_stream_item(&mut self, task_id: usize, output: Box<dyn Any>) -> bool {
self.pending_flushes += 1;
let mut handler = self.stream_handlers.remove(&task_id).unwrap();
let halt = match &mut handler {
StreamHandler::Model {
model_id,
item_callback,
..
} => {
if let Some(mut model) = self.ctx.models.remove(&model_id) {
let halt = item_callback(model.as_any_mut(), output, self, *model_id);
self.ctx.models.insert(*model_id, model);
self.stream_handlers.insert(task_id, handler);
halt
} else {
true
}
}
StreamHandler::View {
window_id, window_id,
view_id, view_id,
mut callback, item_callback,
..
} => { } => {
if let Some(mut view) = self if let Some(mut view) = self
.ctx .ctx
@ -999,34 +993,67 @@ impl MutableAppContext {
.get_mut(&window_id) .get_mut(&window_id)
.and_then(|w| w.views.remove(&view_id)) .and_then(|w| w.views.remove(&view_id))
{ {
let halt = callback(view.as_mut(), output, self, window_id, view_id); let halt = item_callback(view.as_mut(), output, self, *window_id, *view_id);
self.ctx self.ctx
.windows .windows
.get_mut(&window_id) .get_mut(&window_id)
.unwrap() .unwrap()
.views .views
.insert(view_id, view); .insert(*view_id, view);
self.task_callbacks.insert( self.stream_handlers.insert(task_id, handler);
task_id,
TaskCallback::OnViewFromStream {
window_id,
view_id,
callback,
},
);
halt halt
} else { } else {
true true
} }
} }
}; };
self.flush_effects(); self.flush_effects();
halt halt
} }
fn stream_completed(&mut self, task_id: usize) { fn stream_completed(&mut self, task_id: usize) -> Option<Box<dyn Any>> {
self.task_callbacks.remove(&task_id); let result = match self.stream_handlers.remove(&task_id).unwrap() {
StreamHandler::Model {
model_id,
done_callback,
..
} => {
if let Some(mut model) = self.ctx.models.remove(&model_id) {
let result = done_callback(model.as_any_mut(), self, model_id);
self.ctx.models.insert(model_id, model);
Some(result)
} else {
None
}
}
StreamHandler::View {
window_id,
view_id,
done_callback,
..
} => {
if let Some(mut view) = self
.ctx
.windows
.get_mut(&window_id)
.and_then(|w| w.views.remove(&view_id))
{
let result = done_callback(view.as_mut(), self, window_id, view_id);
self.ctx
.windows
.get_mut(&window_id)
.unwrap()
.views
.insert(view_id, view);
Some(result)
} else {
None
}
}
};
self.task_done(task_id); self.task_done(task_id);
result
} }
fn task_done(&self, task_id: usize) { fn task_done(&self, task_id: usize) {
@ -1039,7 +1066,7 @@ impl MutableAppContext {
} }
pub fn finish_pending_tasks(&self) -> impl Future<Output = ()> { pub fn finish_pending_tasks(&self) -> impl Future<Output = ()> {
let mut pending_tasks = self.task_callbacks.keys().cloned().collect::<HashSet<_>>(); let mut pending_tasks = self.future_handlers.keys().cloned().collect::<HashSet<_>>();
let task_done = self.task_done.1.clone(); let task_done = self.task_done.1.clone();
async move { async move {
@ -1404,82 +1431,68 @@ impl<'a, T: Entity> ModelContext<'a, T> {
}); });
} }
pub fn spawn_local<S, F, U>(&mut self, future: S, callback: F) -> impl Future<Output = U> pub fn spawn<S, F, U>(&mut self, future: S, callback: F) -> ForegroundTask<Option<U>>
where where
S: 'static + Future, S: 'static + Future,
F: 'static + FnOnce(&mut T, S::Output, &mut ModelContext<T>) -> U, F: 'static + FnOnce(&mut T, S::Output, &mut ModelContext<T>) -> U,
U: 'static, U: 'static,
{ {
let (tx, rx) = channel::bounded(1); let (task_id, task) = self.app.spawn::<S, U>(future);
let task_id = self.app.spawn_local(future); self.app.future_handlers.insert(
self.app.task_callbacks.insert(
task_id, task_id,
TaskCallback::OnModelFromFuture { FutureHandler::Model {
model_id: self.model_id,
callback: Box::new(move |model, output, app, model_id, executor| {
let model = model.downcast_mut().unwrap();
let output = *output.downcast().unwrap();
let result = callback(model, output, &mut ModelContext::new(app, model_id));
executor
.spawn(async move { tx.send(result).await })
.detach();
}),
},
);
async move { rx.recv().await.unwrap() }
}
pub fn spawn<S, F, U>(&mut self, future: S, callback: F) -> impl Future<Output = U>
where
S: 'static + Future + Send,
S::Output: Send,
F: 'static + FnOnce(&mut T, S::Output, &mut ModelContext<T>) -> U,
U: 'static,
{
let (tx, rx) = channel::bounded(1);
self.app
.background
.spawn(async move {
if let Err(e) = tx.send(future.await).await {
log::error!("error sending background task result to main thread: {}", e);
}
})
.detach();
self.spawn_local(async move { rx.recv().await.unwrap() }, callback)
}
pub fn spawn_stream_local<S, F>(
&mut self,
stream: S,
mut callback: F,
) -> impl Future<Output = ()>
where
S: 'static + Stream + Unpin,
F: 'static + FnMut(&mut T, Option<S::Item>, &mut ModelContext<T>),
{
let (tx, rx) = channel::bounded(1);
let task_id = self.app.spawn_stream_local(stream, tx);
self.app.task_callbacks.insert(
task_id,
TaskCallback::OnModelFromStream {
model_id: self.model_id, model_id: self.model_id,
callback: Box::new(move |model, output, app, model_id| { callback: Box::new(move |model, output, app, model_id| {
let model = model.downcast_mut().unwrap(); let model = model.downcast_mut().unwrap();
let output = *output.downcast().unwrap(); let output = *output.downcast().unwrap();
let mut ctx = ModelContext::new(app, model_id); Box::new(callback(
callback(model, output, &mut ctx); model,
ctx.halt_stream output,
&mut ModelContext::new(app, model_id),
))
}), }),
}, },
); );
async move { rx.recv().await.unwrap() } task
}
pub fn spawn_stream<S, F, G, U>(
&mut self,
stream: S,
mut item_callback: F,
done_callback: G,
) -> ForegroundTask<Option<U>>
where
S: 'static + Stream + Unpin,
F: 'static + FnMut(&mut T, S::Item, &mut ModelContext<T>),
G: 'static + FnOnce(&mut T, &mut ModelContext<T>) -> U,
U: 'static + Any,
{
let (task_id, task) = self.app.spawn_stream(stream);
self.app.stream_handlers.insert(
task_id,
StreamHandler::Model {
model_id: self.model_id,
item_callback: Box::new(move |model, output, app, model_id| {
let model = model.downcast_mut().unwrap();
let output = *output.downcast().unwrap();
let mut ctx = ModelContext::new(app, model_id);
item_callback(model, output, &mut ctx);
ctx.halt_stream
}),
done_callback: Box::new(
move |model: &mut dyn Any, app: &mut MutableAppContext, model_id| {
let model = model.downcast_mut().unwrap();
let mut ctx = ModelContext::new(app, model_id);
Box::new(done_callback(model, &mut ctx))
},
),
},
);
task
} }
} }
@ -1674,85 +1687,67 @@ impl<'a, T: View> ViewContext<'a, T> {
self.halt_stream = true; self.halt_stream = true;
} }
pub fn spawn_local<S, F, U>(&mut self, future: S, callback: F) -> impl Future<Output = U> pub fn spawn<S, F, U>(&mut self, future: S, callback: F) -> ForegroundTask<Option<U>>
where where
S: 'static + Future, S: 'static + Future,
F: 'static + FnOnce(&mut T, S::Output, &mut ViewContext<T>) -> U, F: 'static + FnOnce(&mut T, S::Output, &mut ViewContext<T>) -> U,
U: 'static, U: 'static,
{ {
let (tx, rx) = channel::bounded(1); let (task_id, task) = self.app.spawn(future);
let task_id = self.app.spawn_local(future); self.app.future_handlers.insert(
self.app.task_callbacks.insert(
task_id, task_id,
TaskCallback::OnViewFromFuture { FutureHandler::View {
window_id: self.window_id,
view_id: self.view_id,
callback: Box::new(move |view, output, app, window_id, view_id, executor| {
let view = view.as_any_mut().downcast_mut().unwrap();
let output = *output.downcast().unwrap();
let result =
callback(view, output, &mut ViewContext::new(app, window_id, view_id));
executor
.spawn(async move { tx.send(result).await })
.detach();
}),
},
);
async move { rx.recv().await.unwrap() }
}
pub fn spawn<S, F, U>(&mut self, future: S, callback: F) -> impl Future<Output = U>
where
S: 'static + Future + Send,
S::Output: Send,
F: 'static + FnOnce(&mut T, S::Output, &mut ViewContext<T>) -> U,
U: 'static,
{
let (tx, rx) = channel::bounded(1);
self.app
.background
.spawn(async move {
if let Err(_) = tx.send(future.await).await {
log::error!("Error sending background task result to main thread",);
}
})
.detach();
self.spawn_local(async move { rx.recv().await.unwrap() }, callback)
}
pub fn spawn_stream_local<S, F>(
&mut self,
stream: S,
mut callback: F,
) -> impl Future<Output = ()>
where
S: 'static + Stream + Unpin,
F: 'static + FnMut(&mut T, Option<S::Item>, &mut ViewContext<T>),
{
let (tx, rx) = channel::bounded(1);
let task_id = self.app.spawn_stream_local(stream, tx);
self.app.task_callbacks.insert(
task_id,
TaskCallback::OnViewFromStream {
window_id: self.window_id, window_id: self.window_id,
view_id: self.view_id, view_id: self.view_id,
callback: Box::new(move |view, output, app, window_id, view_id| { callback: Box::new(move |view, output, app, window_id, view_id| {
let view = view.as_any_mut().downcast_mut().unwrap(); let view = view.as_any_mut().downcast_mut().unwrap();
let output = *output.downcast().unwrap(); let output = *output.downcast().unwrap();
let mut ctx = ViewContext::new(app, window_id, view_id); Box::new(callback(
callback(view, output, &mut ctx); view,
ctx.halt_stream output,
&mut ViewContext::new(app, window_id, view_id),
))
}), }),
}, },
); );
async move { rx.recv().await.unwrap() } task
}
pub fn spawn_stream<S, F, G, U>(
&mut self,
stream: S,
mut item_callback: F,
done_callback: G,
) -> ForegroundTask<Option<U>>
where
S: 'static + Stream + Unpin,
F: 'static + FnMut(&mut T, S::Item, &mut ViewContext<T>),
G: 'static + FnOnce(&mut T, &mut ViewContext<T>) -> U,
U: 'static + Any,
{
let (task_id, task) = self.app.spawn_stream(stream);
self.app.stream_handlers.insert(
task_id,
StreamHandler::View {
window_id: self.window_id,
view_id: self.view_id,
item_callback: Box::new(move |view, output, app, window_id, view_id| {
let view = view.as_any_mut().downcast_mut().unwrap();
let output = *output.downcast().unwrap();
let mut ctx = ViewContext::new(app, window_id, view_id);
item_callback(view, output, &mut ctx);
ctx.halt_stream
}),
done_callback: Box::new(move |view, app, window_id, view_id| {
let view = view.as_any_mut().downcast_mut().unwrap();
let mut ctx = ViewContext::new(app, window_id, view_id);
Box::new(done_callback(view, &mut ctx))
}),
},
);
task
} }
} }
@ -2192,24 +2187,14 @@ enum Observation {
}, },
} }
enum TaskCallback { enum FutureHandler {
OnModelFromFuture { Model {
model_id: usize, model_id: usize,
callback: Box< callback: Box<
dyn FnOnce( dyn FnOnce(&mut dyn Any, Box<dyn Any>, &mut MutableAppContext, usize) -> Box<dyn Any>,
&mut dyn Any,
Box<dyn Any>,
&mut MutableAppContext,
usize,
Rc<executor::Foreground>,
),
>, >,
}, },
OnModelFromStream { View {
model_id: usize,
callback: Box<dyn FnMut(&mut dyn Any, Box<dyn Any>, &mut MutableAppContext, usize) -> bool>,
},
OnViewFromFuture {
window_id: usize, window_id: usize,
view_id: usize, view_id: usize,
callback: Box< callback: Box<
@ -2219,16 +2204,26 @@ enum TaskCallback {
&mut MutableAppContext, &mut MutableAppContext,
usize, usize,
usize, usize,
Rc<executor::Foreground>, ) -> Box<dyn Any>,
),
>, >,
}, },
OnViewFromStream { }
enum StreamHandler {
Model {
model_id: usize,
item_callback:
Box<dyn FnMut(&mut dyn Any, Box<dyn Any>, &mut MutableAppContext, usize) -> bool>,
done_callback: Box<dyn FnOnce(&mut dyn Any, &mut MutableAppContext, usize) -> Box<dyn Any>>,
},
View {
window_id: usize, window_id: usize,
view_id: usize, view_id: usize,
callback: Box< item_callback: Box<
dyn FnMut(&mut dyn AnyView, Box<dyn Any>, &mut MutableAppContext, usize, usize) -> bool, dyn FnMut(&mut dyn AnyView, Box<dyn Any>, &mut MutableAppContext, usize, usize) -> bool,
>, >,
done_callback:
Box<dyn FnOnce(&mut dyn AnyView, &mut MutableAppContext, usize, usize) -> Box<dyn Any>>,
}, },
} }
@ -2395,7 +2390,7 @@ mod tests {
let handle = app.add_model(|_| Model::default()); let handle = app.add_model(|_| Model::default());
handle handle
.update(&mut app, |_, c| { .update(&mut app, |_, c| {
c.spawn_local(async { 7 }, |model, output, _| { c.spawn(async { 7 }, |model, output, _| {
model.count = output; model.count = output;
}) })
}) })
@ -2428,9 +2423,15 @@ mod tests {
let handle = app.add_model(|_| Model::default()); let handle = app.add_model(|_| Model::default());
handle handle
.update(&mut app, |_, c| { .update(&mut app, |_, c| {
c.spawn_stream_local(smol::stream::iter(vec![1, 2, 3]), |model, output, _| { c.spawn_stream(
model.events.push(output); smol::stream::iter(vec![1, 2, 3]),
}) |model, output, _| {
model.events.push(Some(output));
},
|model, _| {
model.events.push(None);
},
)
}) })
.await; .await;
@ -2802,7 +2803,7 @@ mod tests {
let (_, handle) = app.add_window(|_| View::default()); let (_, handle) = app.add_window(|_| View::default());
handle handle
.update(&mut app, |_, c| { .update(&mut app, |_, c| {
c.spawn_local(async { 7 }, |me, output, _| { c.spawn(async { 7 }, |me, output, _| {
me.count = output; me.count = output;
}) })
}) })
@ -2844,9 +2845,15 @@ mod tests {
let (_, handle) = app.add_window(|_| View::default()); let (_, handle) = app.add_window(|_| View::default());
handle handle
.update(&mut app, |_, c| { .update(&mut app, |_, c| {
c.spawn_stream_local(smol::stream::iter(vec![1, 2, 3]), |me, output, _| { c.spawn_stream(
me.events.push(output); smol::stream::iter(vec![1_usize, 2, 3]),
}) |me, output, _| {
me.events.push(Some(output));
},
|me, _| {
me.events.push(None);
},
)
}) })
.await; .await;
@ -3159,19 +3166,21 @@ mod tests {
model.update(&mut app, |_, ctx| { model.update(&mut app, |_, ctx| {
let _ = ctx.spawn(async {}, |_, _, _| {}); let _ = ctx.spawn(async {}, |_, _, _| {});
let _ = ctx.spawn_local(async {}, |_, _, _| {}); let _ = ctx.spawn(async {}, |_, _, _| {});
let _ = ctx.spawn_stream_local(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}); let _ =
ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {});
}); });
view.update(&mut app, |_, ctx| { view.update(&mut app, |_, ctx| {
let _ = ctx.spawn(async {}, |_, _, _| {}); let _ = ctx.spawn(async {}, |_, _, _| {});
let _ = ctx.spawn_local(async {}, |_, _, _| {}); let _ = ctx.spawn(async {}, |_, _, _| {});
let _ = ctx.spawn_stream_local(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}); let _ =
ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {});
}); });
assert!(!app.0.borrow().task_callbacks.is_empty()); assert!(!app.0.borrow().future_handlers.is_empty());
app.finish_pending_tasks().await; app.finish_pending_tasks().await;
assert!(app.0.borrow().task_callbacks.is_empty()); assert!(app.0.borrow().future_handlers.is_empty());
app.finish_pending_tasks().await; // Don't block if there are no tasks app.finish_pending_tasks().await; // Don't block if there are no tasks
}); });
} }

View file

@ -1,6 +1,6 @@
// #[cfg(not(test))]
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use async_task::Runnable; use async_task::Runnable;
use pin_project::pin_project;
use smol::prelude::*; use smol::prelude::*;
use smol::{channel, Executor}; use smol::{channel, Executor};
use std::rc::Rc; use std::rc::Rc;
@ -17,9 +17,24 @@ pub enum Foreground {
Test(smol::LocalExecutor<'static>), Test(smol::LocalExecutor<'static>),
} }
#[pin_project(project = ForegroundTaskProject)]
pub enum ForegroundTask<T> { pub enum ForegroundTask<T> {
Platform(async_task::Task<T>), Platform(#[pin] async_task::Task<T>),
Test(smol::Task<T>), Test(#[pin] smol::Task<T>),
}
impl<T> Future for ForegroundTask<T> {
type Output = T;
fn poll(
self: std::pin::Pin<&mut Self>,
ctx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
match self.project() {
ForegroundTaskProject::Platform(task) => task.poll(ctx),
ForegroundTaskProject::Test(task) => task.poll(ctx),
}
}
} }
pub struct Background { pub struct Background {
@ -27,6 +42,7 @@ pub struct Background {
_stop: channel::Sender<()>, _stop: channel::Sender<()>,
} }
#[must_use]
pub type BackgroundTask<T> = smol::Task<T>; pub type BackgroundTask<T> = smol::Task<T>;
impl Foreground { impl Foreground {
@ -69,6 +85,7 @@ impl Foreground {
} }
} }
#[must_use]
impl<T> ForegroundTask<T> { impl<T> ForegroundTask<T> {
pub fn detach(self) { pub fn detach(self) {
match self { match self {

View file

@ -1066,7 +1066,7 @@ impl BufferView {
ctx.notify(); ctx.notify();
let epoch = self.next_blink_epoch(); let epoch = self.next_blink_epoch();
let _ = ctx.spawn_local( let _ = ctx.spawn(
async move { async move {
Timer::after(CURSOR_BLINK_INTERVAL).await; Timer::after(CURSOR_BLINK_INTERVAL).await;
epoch epoch
@ -1088,7 +1088,7 @@ impl BufferView {
ctx.notify(); ctx.notify();
let epoch = self.next_blink_epoch(); let epoch = self.next_blink_epoch();
let _ = ctx.spawn_local( let _ = ctx.spawn(
async move { async move {
Timer::after(CURSOR_BLINK_INTERVAL).await; Timer::after(CURSOR_BLINK_INTERVAL).await;
epoch epoch

View file

@ -38,14 +38,14 @@ impl<T> Receiver<T> {
impl<T: 'static + Clone> Receiver<T> { impl<T: 'static + Clone> Receiver<T> {
pub fn notify_model_on_change<M: 'static + Entity>(&self, ctx: &mut ModelContext<M>) { pub fn notify_model_on_change<M: 'static + Entity>(&self, ctx: &mut ModelContext<M>) {
let watch = self.clone(); let watch = self.clone();
let _ = ctx.spawn_local(async move { watch.updated().await }, |_, _, ctx| { let _ = ctx.spawn(async move { watch.updated().await }, |_, _, ctx| {
ctx.notify() ctx.notify()
}); });
} }
pub fn notify_view_on_change<V: 'static + View>(&self, ctx: &mut ViewContext<V>) { pub fn notify_view_on_change<V: 'static + View>(&self, ctx: &mut ViewContext<V>) {
let watch = self.clone(); let watch = self.clone();
let _ = ctx.spawn_local(async move { watch.updated().await }, |_, _, ctx| { let _ = ctx.spawn(async move { watch.updated().await }, |_, _, ctx| {
ctx.notify() ctx.notify()
}); });
} }

View file

@ -62,18 +62,19 @@ impl Worktree {
let tree = tree.clone(); let tree = tree.clone();
let (tx, rx) = smol::channel::bounded(1); let (tx, rx) = smol::channel::bounded(1);
ctx.background_executor() let task = ctx.background_executor().spawn(async move {
.spawn(async move { let _ = tx.send(tree.scan_dirs()?).await;
tx.send(tree.scan_dirs()).await.unwrap(); Ok(())
}) });
.detach();
let _ = ctx.spawn_local(async move { rx.recv().await.unwrap() }, Self::done_scanning); ctx.spawn(task, Self::done_scanning).detach();
let _ = ctx.spawn_stream_local( ctx.spawn_stream(
timer::repeat(Duration::from_millis(100)).map(|_| ()), timer::repeat(Duration::from_millis(100)).map(|_| ()),
Self::scanning, Self::scanning,
); |_, _| {},
)
.detach();
} }
tree tree
@ -347,7 +348,7 @@ impl Worktree {
} }
} }
fn scanning(&mut self, _: Option<()>, ctx: &mut ModelContext<Self>) { fn scanning(&mut self, _: (), ctx: &mut ModelContext<Self>) {
if self.0.read().scanning { if self.0.read().scanning {
ctx.notify(); ctx.notify();
} else { } else {
@ -356,6 +357,7 @@ impl Worktree {
} }
fn done_scanning(&mut self, result: io::Result<()>, ctx: &mut ModelContext<Self>) { fn done_scanning(&mut self, result: io::Result<()>, ctx: &mut ModelContext<Self>) {
log::info!("done scanning");
self.0.write().scanning = false; self.0.write().scanning = false;
if let Err(error) = result { if let Err(error) = result {
log::error!("error populating worktree: {}", error); log::error!("error populating worktree: {}", error);