Detailed changes
@@ -7,6 +7,7 @@ use std::{
path::{Path, PathBuf},
rc::{Rc, Weak},
sync::{Arc, atomic::Ordering::SeqCst},
+ task::{Poll, Waker},
time::Duration,
};
@@ -91,6 +92,36 @@ impl AppCell {
}
Ok(AppRefMut(self.app.try_borrow_mut()?))
}
+
+ pub fn shutdown(self: &Rc<AppCell>) {
+ let mut futures = Vec::new();
+
+ let mut cx = self.borrow_mut();
+
+ for observer in cx.quit_observers.remove(&()) {
+ futures.push(observer(&mut cx));
+ }
+
+ cx.windows.clear();
+ cx.window_handles.clear();
+ cx.flush_effects();
+ let executor = cx.background_executor.clone();
+ drop(cx);
+
+ let waker = Waker::noop();
+ let mut future_cx = std::task::Context::from_waker(waker);
+ let futures = futures::future::join_all(futures);
+ futures::pin_mut!(futures);
+ let mut start = std::time::Instant::now();
+ while dbg!(start.elapsed() < SHUTDOWN_TIMEOUT) {
+ match futures.as_mut().poll(&mut future_cx) {
+ Poll::Pending => {
+ executor.tick();
+ }
+ Poll::Ready(_) => break,
+ }
+ }
+ }
}
#[doc(hidden)]
@@ -382,7 +413,7 @@ impl App {
platform.on_quit(Box::new({
let cx = app.clone();
move || {
- cx.borrow_mut().shutdown();
+ cx.shutdown();
}
}));
@@ -391,7 +422,7 @@ impl App {
/// Quit the application gracefully. Handlers registered with [`Context::on_app_quit`]
/// will be given 100ms to complete before exiting.
- pub fn shutdown(&mut self) {
+ pub fn shutdown_old(&mut self) {
let mut futures = Vec::new();
for observer in self.quit_observers.remove(&()) {
@@ -167,7 +167,7 @@ impl TestAppContext {
/// public so the macro can call it.
pub fn quit(&self) {
self.on_quit.borrow_mut().drain(..).for_each(|f| f());
- self.app.borrow_mut().shutdown();
+ self.app.shutdown();
}
/// Register cleanup to run when the test ends.
@@ -384,10 +384,9 @@ impl BackgroundExecutor {
self.dispatcher.as_test().unwrap().advance_clock(duration)
}
- /// in tests, run one task.
- #[cfg(any(test, feature = "test-support"))]
+ /// docs
pub fn tick(&self) -> bool {
- self.dispatcher.as_test().unwrap().tick(false)
+ self.dispatcher.tick(true)
}
/// in tests, run all tasks that are ready to run. If after doing so
@@ -545,6 +545,7 @@ pub trait PlatformDispatcher: Send + Sync {
fn now(&self) -> Instant {
Instant::now()
}
+ fn tick(&self, _: bool) -> bool;
#[cfg(any(test, feature = "test-support"))]
fn as_test(&self) -> Option<&TestDispatcher> {
@@ -4,6 +4,14 @@
use crate::{PlatformDispatcher, TaskLabel};
use async_task::Runnable;
+use block::{Block, ConcreteBlock, RcBlock};
+use core_foundation::{
+ base::CFTypeRef,
+ runloop::{
+ CFRunLoopRef, CFRunLoopRunInMode, CFRunLoopWakeUp, kCFRunLoopCommonModes,
+ kCFRunLoopDefaultMode,
+ },
+};
use objc::{
class, msg_send,
runtime::{BOOL, YES},
@@ -11,7 +19,9 @@ use objc::{
};
use parking::{Parker, Unparker};
use parking_lot::Mutex;
+use smol::io::BlockOn;
use std::{
+ cell::Cell,
ffi::c_void,
ptr::{NonNull, addr_of},
sync::Arc,
@@ -64,11 +74,21 @@ impl PlatformDispatcher for MacDispatcher {
}
fn dispatch_on_main_thread(&self, runnable: Runnable) {
+ use core_foundation::runloop::CFRunLoopGetMain;
+
unsafe {
- dispatch_async_f(
- dispatch_get_main_queue(),
- runnable.into_raw().as_ptr() as *mut c_void,
- Some(trampoline),
+ let mut runnable = Cell::new(Some(runnable));
+ let main_run_loop = CFRunLoopGetMain();
+ let block = ConcreteBlock::new(move || {
+ if let Some(runnable) = runnable.take() {
+ runnable.run();
+ }
+ })
+ .copy();
+ CFRunLoopPerformBlock(
+ main_run_loop,
+ kCFRunLoopDefaultMode as _,
+ &*block as *const Block<_, _> as _,
);
}
}
@@ -87,6 +107,13 @@ impl PlatformDispatcher for MacDispatcher {
}
}
+ fn tick(&self, background_only: bool) -> bool {
+ unsafe {
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0., 0);
+ }
+ true
+ }
+
fn park(&self, timeout: Option<Duration>) -> bool {
if let Some(timeout) = timeout {
self.parker.lock().park_timeout(timeout)
@@ -105,3 +132,7 @@ extern "C" fn trampoline(runnable: *mut c_void) {
let task = unsafe { Runnable::<()>::from_raw(NonNull::new_unchecked(runnable as *mut ())) };
task.run();
}
+
+unsafe extern "C" {
+ fn CFRunLoopPerformBlock(rl: CFRunLoopRef, mode: CFTypeRef, block: *const c_void);
+}
@@ -122,68 +122,6 @@ impl TestDispatcher {
}
}
- pub fn tick(&self, background_only: bool) -> bool {
- let mut state = self.state.lock();
-
- while let Some((deadline, _)) = state.delayed.first() {
- if *deadline > state.time {
- break;
- }
- let (_, runnable) = state.delayed.remove(0);
- state.background.push(runnable);
- }
-
- let foreground_len: usize = if background_only {
- 0
- } else {
- state
- .foreground
- .values()
- .map(|runnables| runnables.len())
- .sum()
- };
- let background_len = state.background.len();
-
- let runnable;
- let main_thread;
- if foreground_len == 0 && background_len == 0 {
- let deprioritized_background_len = state.deprioritized_background.len();
- if deprioritized_background_len == 0 {
- return false;
- }
- let ix = state.random.gen_range(0..deprioritized_background_len);
- main_thread = false;
- runnable = state.deprioritized_background.swap_remove(ix);
- } else {
- main_thread = state.random.gen_ratio(
- foreground_len as u32,
- (foreground_len + background_len) as u32,
- );
- if main_thread {
- let state = &mut *state;
- runnable = state
- .foreground
- .values_mut()
- .filter(|runnables| !runnables.is_empty())
- .choose(&mut state.random)
- .unwrap()
- .pop_front()
- .unwrap();
- } else {
- let ix = state.random.gen_range(0..background_len);
- runnable = state.background.swap_remove(ix);
- };
- };
-
- let was_main_thread = state.is_main_thread;
- state.is_main_thread = main_thread;
- drop(state);
- runnable.run();
- self.state.lock().is_main_thread = was_main_thread;
-
- true
- }
-
pub fn deprioritize(&self, task_label: TaskLabel) {
self.state
.lock()
@@ -267,6 +205,68 @@ impl PlatformDispatcher for TestDispatcher {
state.start_time + state.time
}
+ fn tick(&self, background_only: bool) -> bool {
+ let mut state = self.state.lock();
+
+ while let Some((deadline, _)) = state.delayed.first() {
+ if *deadline > state.time {
+ break;
+ }
+ let (_, runnable) = state.delayed.remove(0);
+ state.background.push(runnable);
+ }
+
+ let foreground_len: usize = if background_only {
+ 0
+ } else {
+ state
+ .foreground
+ .values()
+ .map(|runnables| runnables.len())
+ .sum()
+ };
+ let background_len = state.background.len();
+
+ let runnable;
+ let main_thread;
+ if foreground_len == 0 && background_len == 0 {
+ let deprioritized_background_len = state.deprioritized_background.len();
+ if deprioritized_background_len == 0 {
+ return false;
+ }
+ let ix = state.random.gen_range(0..deprioritized_background_len);
+ main_thread = false;
+ runnable = state.deprioritized_background.swap_remove(ix);
+ } else {
+ main_thread = state.random.gen_ratio(
+ foreground_len as u32,
+ (foreground_len + background_len) as u32,
+ );
+ if main_thread {
+ let state = &mut *state;
+ runnable = state
+ .foreground
+ .values_mut()
+ .filter(|runnables| !runnables.is_empty())
+ .choose(&mut state.random)
+ .unwrap()
+ .pop_front()
+ .unwrap();
+ } else {
+ let ix = state.random.gen_range(0..background_len);
+ runnable = state.background.swap_remove(ix);
+ };
+ };
+
+ let was_main_thread = state.is_main_thread;
+ state.is_main_thread = main_thread;
+ drop(state);
+ runnable.run();
+ self.state.lock().is_main_thread = was_main_thread;
+
+ true
+ }
+
fn dispatch(&self, runnable: Runnable, label: Option<TaskLabel>) {
{
let mut state = self.state.lock();
@@ -136,6 +136,7 @@ impl DapStore {
breakpoint_store: Entity<BreakpointStore>,
cx: &mut Context<Self>,
) -> Self {
+ cx.on_app_quit(Self::shutdown_sessions).detach();
let mode = DapStoreMode::Local(LocalDapStore {
fs,
environment,
@@ -650,12 +650,13 @@ impl HeadlessProject {
cx: AsyncApp,
) -> Result<proto::Ack> {
cx.spawn(async move |cx| {
- cx.update(|cx| {
- // TODO: This is a hack, because in a headless project, shutdown isn't executed
- // when calling quit, but it should be.
- cx.shutdown();
- cx.quit();
- })
+ // todo!("come back to this")
+ // cx.update(|cx| {
+ // // TODO: This is a hack, because in a headless project, shutdown isn't executed
+ // // when calling quit, but it should be.
+ // cx.shutdown();
+ // cx.quit();
+ // })
})
.detach();
@@ -0,0 +1,5 @@
+{"Put":{"state":"hello\nˇ\nworld"}}
+{"Key":"i"}
+{"Key":"ctrl-y"}
+{"Key":"ctrl-e"}
+{"Get":{"state":"hello\nhoˇ\nworld","mode":"Insert"}}