Detailed changes
@@ -19,6 +19,8 @@ fn generate_dispatch_bindings() {
let bindings = bindgen::Builder::default()
.header("src/platform/mac/dispatch.h")
.allowlist_var("_dispatch_main_q")
+ .allowlist_var("DISPATCH_QUEUE_PRIORITY_DEFAULT")
+ .allowlist_function("dispatch_get_global_queue")
.allowlist_function("dispatch_async_f")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.layout_tests(false)
@@ -8,9 +8,9 @@ pub use model_context::*;
use refineable::Refineable;
use crate::{
- current_platform, image_cache::ImageCache, run_on_main, spawn_on_main, AssetSource, Context,
- LayoutId, MainThread, MainThreadOnly, Platform, PlatformDispatcher, RootView, SvgRenderer,
- TextStyle, TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId,
+ current_platform, image_cache::ImageCache, AssetSource, Context, Executor, LayoutId,
+ MainThread, MainThreadOnly, Platform, RootView, SvgRenderer, Task, TextStyle,
+ TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId,
};
use anyhow::{anyhow, Result};
use collections::{HashMap, VecDeque};
@@ -50,15 +50,15 @@ impl App {
asset_source: Arc<dyn AssetSource>,
http_client: Arc<dyn HttpClient>,
) -> Self {
- let dispatcher = platform.dispatcher();
+ let executor = platform.executor();
let text_system = Arc::new(TextSystem::new(platform.text_system()));
let entities = EntityMap::new();
let unit_entity = entities.redeem(entities.reserve(), ());
Self(Arc::new_cyclic(|this| {
Mutex::new(MainThread::new(AppContext {
this: this.clone(),
- platform: MainThreadOnly::new(platform, dispatcher.clone()),
- dispatcher,
+ platform: MainThreadOnly::new(platform, executor.clone()),
+ executor,
text_system,
svg_renderer: SvgRenderer::new(asset_source),
image_cache: ImageCache::new(http_client),
@@ -95,7 +95,7 @@ pub struct AppContext {
platform: MainThreadOnly<dyn Platform>,
text_system: Arc<TextSystem>,
pending_updates: usize,
- pub(crate) dispatcher: Arc<dyn PlatformDispatcher>,
+ pub(crate) executor: Executor,
pub(crate) svg_renderer: SvgRenderer,
pub(crate) image_cache: ImageCache,
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
@@ -184,6 +184,10 @@ impl AppContext {
AsyncContext(unsafe { mem::transmute(self.this.clone()) })
}
+ pub fn executor(&self) -> &Executor {
+ &self.executor
+ }
+
pub fn run_on_main<R>(
&mut self,
f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
@@ -192,20 +196,22 @@ impl AppContext {
R: Send + 'static,
{
let (tx, rx) = oneshot::channel();
- if self.dispatcher.is_main_thread() {
+ if self.executor.is_main_thread() {
let _ = tx.send(f(unsafe {
mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(self)
}));
} else {
let this = self.this.upgrade().unwrap();
- let _ = run_on_main(self.dispatcher.clone(), move || {
- let cx = &mut *this.lock();
- cx.update(|cx| {
- let _ = tx.send(f(unsafe {
- mem::transmute::<&mut Self, &mut MainThread<Self>>(cx)
- }));
+ self.executor
+ .run_on_main(move || {
+ let cx = &mut *this.lock();
+ cx.update(|cx| {
+ let _ = tx.send(f(unsafe {
+ mem::transmute::<&mut Self, &mut MainThread<Self>>(cx)
+ }));
+ })
})
- });
+ .detach();
}
async move { rx.await.unwrap() }
}
@@ -213,13 +219,13 @@ impl AppContext {
pub fn spawn_on_main<F, R>(
&self,
f: impl FnOnce(&mut MainThread<AppContext>) -> F + Send + 'static,
- ) -> impl Future<Output = R>
+ ) -> Task<R>
where
F: Future<Output = R> + 'static,
R: Send + 'static,
{
let this = self.this.upgrade().unwrap();
- spawn_on_main(self.dispatcher.clone(), move || {
+ self.executor.spawn_on_main(move || {
let cx = &mut *this.lock();
cx.update(|cx| {
f(unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) })
@@ -1,1103 +1,113 @@
-use crate::util;
use crate::PlatformDispatcher;
-use anyhow::{anyhow, Result};
-use async_task::Runnable;
-use futures::channel::{mpsc, oneshot};
-use smol::{channel, prelude::*, Executor};
+use smol::prelude::*;
use std::{
- any::Any,
- fmt::{self},
- marker::PhantomData,
- mem,
pin::Pin,
- rc::Rc,
sync::Arc,
task::{Context, Poll},
- thread,
- time::Duration,
};
-/// Enqueues the given closure to run on the application's event loop.
-/// Returns the result asynchronously.
-pub(crate) fn run_on_main<F, R>(
+#[derive(Clone)]
+pub struct Executor {
dispatcher: Arc<dyn PlatformDispatcher>,
- func: F,
-) -> impl Future<Output = R>
-where
- F: FnOnce() -> R + Send + 'static,
- R: Send + 'static,
-{
- let (tx, rx) = oneshot::channel();
- if dispatcher.is_main_thread() {
- let _ = tx.send(func());
- } else {
- let _ = spawn_on_main(dispatcher, move || async move {
- let _ = tx.send(func());
- });
- }
- async move { rx.await.unwrap() }
-}
-
-/// Enqueues the given closure to be run on the application's event loop. The
-/// closure returns a future which will be run to completion on the main thread.
-pub(crate) fn spawn_on_main<F, R>(
- dispatcher: Arc<dyn PlatformDispatcher>,
- func: impl FnOnce() -> F + Send + 'static,
-) -> impl Future<Output = R>
-where
- F: Future<Output = R> + 'static,
- R: Send + 'static,
-{
- let (tx, rx) = oneshot::channel();
- let (runnable, task) = async_task::spawn(
- {
- let dispatcher = dispatcher.clone();
- async move {
- let future = func();
- let _ = spawn_on_main_local(dispatcher, async move {
- let result = future.await;
- let _ = tx.send(result);
- });
- }
- },
- move |runnable| dispatcher.run_on_main_thread(runnable),
- );
- runnable.schedule();
- task.detach();
- async move { rx.await.unwrap() }
-}
-
-/// Enqueues the given closure to be run on the application's event loop. Must
-/// be called on the main thread.
-pub(crate) fn spawn_on_main_local<R>(
- dispatcher: Arc<dyn PlatformDispatcher>,
- future: impl Future<Output = R> + 'static,
-) -> impl Future<Output = R>
-where
- R: 'static,
-{
- assert!(dispatcher.is_main_thread(), "must be called on main thread");
-
- let (tx, rx) = oneshot::channel();
- let (runnable, task) = async_task::spawn_local(
- async move {
- let result = future.await;
- let _ = tx.send(result);
- },
- move |runnable| dispatcher.run_on_main_thread(runnable),
- );
- runnable.schedule();
- task.detach();
- async move { rx.await.unwrap() }
}
-pub enum ForegroundExecutor {
- Platform {
- dispatcher: Arc<dyn PlatformDispatcher>,
- _not_send_or_sync: PhantomData<Rc<()>>,
- },
- #[cfg(any(test, feature = "test"))]
- Deterministic {
- cx_id: usize,
- executor: Arc<Deterministic>,
- },
-}
-
-pub enum BackgroundExecutor {
- #[cfg(any(test, feature = "test"))]
- Deterministic { executor: Arc<Deterministic> },
- Production {
- executor: Arc<smol::Executor<'static>>,
- _stop: channel::Sender<()>,
- },
-}
-
-type AnyLocalFuture = Pin<Box<dyn 'static + Future<Output = Box<dyn Any + 'static>>>>;
-type AnyFuture = Pin<Box<dyn 'static + Send + Future<Output = Box<dyn Any + Send + 'static>>>>;
-type AnyTask = async_task::Task<Box<dyn Any + Send + 'static>>;
-type AnyLocalTask = async_task::Task<Box<dyn Any + 'static>>;
-
-#[must_use]
pub enum Task<T> {
Ready(Option<T>),
- Local {
- any_task: AnyLocalTask,
- result_type: PhantomData<T>,
- },
- Send {
- any_task: AnyTask,
- result_type: PhantomData<T>,
- },
-}
-
-unsafe impl<T: Send> Send for Task<T> {}
-
-#[cfg(any(test, feature = "test"))]
-struct DeterministicState {
- rng: rand::prelude::StdRng,
- seed: u64,
- scheduled_from_foreground: collections::HashMap<usize, Vec<ForegroundRunnable>>,
- scheduled_from_background: Vec<BackgroundRunnable>,
- forbid_parking: bool,
- block_on_ticks: std::ops::RangeInclusive<usize>,
- now: std::time::Instant,
- next_timer_id: usize,
- pending_timers: Vec<(usize, std::time::Instant, postage::barrier::Sender)>,
- waiting_backtrace: Option<backtrace::Backtrace>,
- next_runnable_id: usize,
- poll_history: Vec<ExecutorEvent>,
- previous_poll_history: Option<Vec<ExecutorEvent>>,
- enable_runnable_backtraces: bool,
- runnable_backtraces: collections::HashMap<usize, backtrace::Backtrace>,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum ExecutorEvent {
- PollRunnable { id: usize },
- EnqueuRunnable { id: usize },
-}
-
-#[cfg(any(test, feature = "test"))]
-struct ForegroundRunnable {
- id: usize,
- runnable: Runnable,
- main: bool,
-}
-
-#[cfg(any(test, feature = "test"))]
-struct BackgroundRunnable {
- id: usize,
- runnable: Runnable,
-}
-
-#[cfg(any(test, feature = "test"))]
-pub struct Deterministic {
- state: Arc<parking_lot::Mutex<DeterministicState>>,
- parker: parking_lot::Mutex<parking::Parker>,
-}
-
-#[must_use]
-pub enum Timer {
- Production(smol::Timer),
- #[cfg(any(test, feature = "test"))]
- Deterministic(DeterministicTimer),
-}
-
-#[cfg(any(test, feature = "test"))]
-pub struct DeterministicTimer {
- rx: postage::barrier::Receiver,
- id: usize,
- state: Arc<parking_lot::Mutex<DeterministicState>>,
-}
-
-#[cfg(any(test, feature = "test"))]
-impl Deterministic {
- pub fn new(seed: u64) -> Arc<Self> {
- use rand::prelude::*;
-
- Arc::new(Self {
- state: Arc::new(parking_lot::Mutex::new(DeterministicState {
- rng: StdRng::seed_from_u64(seed),
- seed,
- scheduled_from_foreground: Default::default(),
- scheduled_from_background: Default::default(),
- forbid_parking: false,
- block_on_ticks: 0..=1000,
- now: std::time::Instant::now(),
- next_timer_id: Default::default(),
- pending_timers: Default::default(),
- waiting_backtrace: None,
- next_runnable_id: 0,
- poll_history: Default::default(),
- previous_poll_history: Default::default(),
- enable_runnable_backtraces: false,
- runnable_backtraces: Default::default(),
- })),
- parker: Default::default(),
- })
- }
-
- pub fn execution_history(&self) -> Vec<ExecutorEvent> {
- self.state.lock().poll_history.clone()
- }
-
- pub fn set_previous_execution_history(&self, history: Option<Vec<ExecutorEvent>>) {
- self.state.lock().previous_poll_history = history;
- }
-
- pub fn enable_runnable_backtrace(&self) {
- self.state.lock().enable_runnable_backtraces = true;
- }
-
- pub fn runnable_backtrace(&self, runnable_id: usize) -> backtrace::Backtrace {
- let mut backtrace = self.state.lock().runnable_backtraces[&runnable_id].clone();
- backtrace.resolve();
- backtrace
- }
-
- pub fn build_background(self: &Arc<Self>) -> Arc<BackgroundExecutor> {
- Arc::new(BackgroundExecutor::Deterministic {
- executor: self.clone(),
- })
- }
-
- pub fn build_foreground(self: &Arc<Self>, id: usize) -> Rc<ForegroundExecutor> {
- Rc::new(ForegroundExecutor::Deterministic {
- cx_id: id,
- executor: self.clone(),
- })
- }
-
- fn spawn_from_foreground(
- &self,
- cx_id: usize,
- future: AnyLocalFuture,
- main: bool,
- ) -> AnyLocalTask {
- let state = self.state.clone();
- let id;
- {
- let mut state = state.lock();
- id = util::post_inc(&mut state.next_runnable_id);
- if state.enable_runnable_backtraces {
- state
- .runnable_backtraces
- .insert(id, backtrace::Backtrace::new_unresolved());
- }
- }
-
- let unparker = self.parker.lock().unparker();
- let (runnable, task) = async_task::spawn_local(future, move |runnable| {
- let mut state = state.lock();
- state.push_to_history(ExecutorEvent::EnqueuRunnable { id });
- state
- .scheduled_from_foreground
- .entry(cx_id)
- .or_default()
- .push(ForegroundRunnable { id, runnable, main });
- unparker.unpark();
- });
- runnable.schedule();
- task
- }
-
- fn spawn(&self, future: AnyFuture) -> AnyTask {
- let state = self.state.clone();
- let id;
- {
- let mut state = state.lock();
- id = util::post_inc(&mut state.next_runnable_id);
- if state.enable_runnable_backtraces {
- state
- .runnable_backtraces
- .insert(id, backtrace::Backtrace::new_unresolved());
- }
- }
-
- let unparker = self.parker.lock().unparker();
- let (runnable, task) = async_task::spawn(future, move |runnable| {
- let mut state = state.lock();
- state
- .poll_history
- .push(ExecutorEvent::EnqueuRunnable { id });
- state
- .scheduled_from_background
- .push(BackgroundRunnable { id, runnable });
- unparker.unpark();
- });
- runnable.schedule();
- task
- }
-
- fn run<'a>(
- &self,
- cx_id: usize,
- main_future: Pin<Box<dyn 'a + Future<Output = Box<dyn Any>>>>,
- ) -> Box<dyn Any> {
- use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
-
- let woken = Arc::new(AtomicBool::new(false));
-
- let state = self.state.clone();
- let id;
- {
- let mut state = state.lock();
- id = util::post_inc(&mut state.next_runnable_id);
- if state.enable_runnable_backtraces {
- state
- .runnable_backtraces
- .insert(id, backtrace::Backtrace::new_unresolved());
- }
- }
-
- let unparker = self.parker.lock().unparker();
- let (runnable, mut main_task) = unsafe {
- async_task::spawn_unchecked(main_future, move |runnable| {
- let state = &mut *state.lock();
- state
- .scheduled_from_foreground
- .entry(cx_id)
- .or_default()
- .push(ForegroundRunnable {
- id: util::post_inc(&mut state.next_runnable_id),
- runnable,
- main: true,
- });
- unparker.unpark();
- })
- };
- runnable.schedule();
-
- loop {
- if let Some(result) = self.run_internal(woken.clone(), Some(&mut main_task)) {
- return result;
- }
-
- if !woken.load(SeqCst) {
- self.state.lock().will_park();
- }
-
- woken.store(false, SeqCst);
- self.parker.lock().park();
- }
- }
-
- pub fn run_until_parked(&self) {
- use std::sync::atomic::AtomicBool;
- let woken = Arc::new(AtomicBool::new(false));
- self.run_internal(woken, None);
- }
-
- fn run_internal(
- &self,
- woken: Arc<std::sync::atomic::AtomicBool>,
- mut main_task: Option<&mut AnyLocalTask>,
- ) -> Option<Box<dyn Any>> {
- use rand::prelude::*;
- use std::sync::atomic::Ordering::SeqCst;
-
- let unparker = self.parker.lock().unparker();
- let waker = waker_fn::waker_fn(move || {
- woken.store(true, SeqCst);
- unparker.unpark();
- });
-
- let mut cx = Context::from_waker(&waker);
- loop {
- let mut state = self.state.lock();
-
- if state.scheduled_from_foreground.is_empty()
- && state.scheduled_from_background.is_empty()
- {
- if let Some(main_task) = main_task {
- if let Poll::Ready(result) = main_task.poll(&mut cx) {
- return Some(result);
- }
- }
-
- return None;
- }
-
- if !state.scheduled_from_background.is_empty() && state.rng.gen() {
- let background_len = state.scheduled_from_background.len();
- let ix = state.rng.gen_range(0..background_len);
- let background_runnable = state.scheduled_from_background.remove(ix);
- state.push_to_history(ExecutorEvent::PollRunnable {
- id: background_runnable.id,
- });
- drop(state);
- background_runnable.runnable.run();
- } else if !state.scheduled_from_foreground.is_empty() {
- let available_cx_ids = state
- .scheduled_from_foreground
- .keys()
- .copied()
- .collect::<Vec<_>>();
- let cx_id_to_run = *available_cx_ids.iter().choose(&mut state.rng).unwrap();
- let scheduled_from_cx = state
- .scheduled_from_foreground
- .get_mut(&cx_id_to_run)
- .unwrap();
- let foreground_runnable = scheduled_from_cx.remove(0);
- if scheduled_from_cx.is_empty() {
- state.scheduled_from_foreground.remove(&cx_id_to_run);
- }
- state.push_to_history(ExecutorEvent::PollRunnable {
- id: foreground_runnable.id,
- });
-
- drop(state);
-
- foreground_runnable.runnable.run();
- if let Some(main_task) = main_task.as_mut() {
- if foreground_runnable.main {
- if let Poll::Ready(result) = main_task.poll(&mut cx) {
- return Some(result);
- }
- }
- }
- }
- }
- }
-
- fn block<F, T>(&self, future: &mut F, max_ticks: usize) -> Option<T>
- where
- F: Unpin + Future<Output = T>,
- {
- use rand::prelude::*;
-
- let unparker = self.parker.lock().unparker();
- let waker = waker_fn::waker_fn(move || {
- unparker.unpark();
- });
-
- let mut cx = Context::from_waker(&waker);
- for _ in 0..max_ticks {
- let mut state = self.state.lock();
- let runnable_count = state.scheduled_from_background.len();
- let ix = state.rng.gen_range(0..=runnable_count);
- if ix < state.scheduled_from_background.len() {
- let background_runnable = state.scheduled_from_background.remove(ix);
- state.push_to_history(ExecutorEvent::PollRunnable {
- id: background_runnable.id,
- });
- drop(state);
- background_runnable.runnable.run();
- } else {
- drop(state);
- if let Poll::Ready(result) = future.poll(&mut cx) {
- return Some(result);
- }
- let mut state = self.state.lock();
- if state.scheduled_from_background.is_empty() {
- state.will_park();
- drop(state);
- self.parker.lock().park();
- }
-
- continue;
- }
- }
-
- None
- }
-
- pub fn timer(&self, duration: Duration) -> Timer {
- let (tx, rx) = postage::barrier::channel();
- let mut state = self.state.lock();
- let wakeup_at = state.now + duration;
- let id = util::post_inc(&mut state.next_timer_id);
- match state
- .pending_timers
- .binary_search_by_key(&wakeup_at, |e| e.1)
- {
- Ok(ix) | Err(ix) => state.pending_timers.insert(ix, (id, wakeup_at, tx)),
- }
- let state = self.state.clone();
- Timer::Deterministic(DeterministicTimer { rx, id, state })
- }
-
- pub fn now(&self) -> std::time::Instant {
- let state = self.state.lock();
- state.now
- }
-
- pub fn advance_clock(&self, duration: Duration) {
- let new_now = self.state.lock().now + duration;
- loop {
- self.run_until_parked();
- let mut state = self.state.lock();
-
- if let Some((_, wakeup_time, _)) = state.pending_timers.first() {
- let wakeup_time = *wakeup_time;
- if wakeup_time <= new_now {
- let timer_count = state
- .pending_timers
- .iter()
- .take_while(|(_, t, _)| *t == wakeup_time)
- .count();
- state.now = wakeup_time;
- let timers_to_wake = state
- .pending_timers
- .drain(0..timer_count)
- .collect::<Vec<_>>();
- drop(state);
- drop(timers_to_wake);
- continue;
- }
- }
-
- break;
- }
-
- self.state.lock().now = new_now;
- }
-
- pub fn start_waiting(&self) {
- self.state.lock().waiting_backtrace = Some(backtrace::Backtrace::new_unresolved());
- }
-
- pub fn finish_waiting(&self) {
- self.state.lock().waiting_backtrace.take();
- }
-
- pub fn forbid_parking(&self) {
- use rand::prelude::*;
-
- let mut state = self.state.lock();
- state.forbid_parking = true;
- state.rng = StdRng::seed_from_u64(state.seed);
- }
-
- pub fn allow_parking(&self) {
- use rand::prelude::*;
-
- let mut state = self.state.lock();
- state.forbid_parking = false;
- state.rng = StdRng::seed_from_u64(state.seed);
- }
-
- pub async fn simulate_random_delay(&self) {
- use rand::prelude::*;
- use smol::future::yield_now;
- if self.state.lock().rng.gen_bool(0.2) {
- let yields = self.state.lock().rng.gen_range(1..=10);
- for _ in 0..yields {
- yield_now().await;
- }
- }
- }
-
- pub fn record_backtrace(&self) {
- let mut state = self.state.lock();
- if state.enable_runnable_backtraces {
- let current_id = state
- .poll_history
- .iter()
- .rev()
- .find_map(|event| match event {
- ExecutorEvent::PollRunnable { id } => Some(*id),
- _ => None,
- });
- if let Some(id) = current_id {
- state
- .runnable_backtraces
- .insert(id, backtrace::Backtrace::new_unresolved());
- }
- }
- }
-}
-
-impl Drop for Timer {
- fn drop(&mut self) {
- #[cfg(any(test, feature = "test"))]
- if let Timer::Deterministic(DeterministicTimer { state, id, .. }) = self {
- state
- .lock()
- .pending_timers
- .retain(|(timer_id, _, _)| timer_id != id)
- }
- }
-}
-
-impl Future for Timer {
- type Output = ();
-
- fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
- match &mut *self {
- #[cfg(any(test, feature = "test"))]
- Self::Deterministic(DeterministicTimer { rx, .. }) => {
- use postage::stream::{PollRecv, Stream as _};
- smol::pin!(rx);
- match rx.poll_recv(&mut postage::Context::from_waker(cx.waker())) {
- PollRecv::Ready(()) | PollRecv::Closed => Poll::Ready(()),
- PollRecv::Pending => Poll::Pending,
- }
- }
- Self::Production(timer) => {
- smol::pin!(timer);
- match timer.poll(cx) {
- Poll::Ready(_) => Poll::Ready(()),
- Poll::Pending => Poll::Pending,
- }
- }
- }
- }
+ Spawned(async_task::Task<T>),
}
-#[cfg(any(test, feature = "test"))]
-impl DeterministicState {
- fn push_to_history(&mut self, event: ExecutorEvent) {
- use std::fmt::Write as _;
-
- self.poll_history.push(event);
- if let Some(prev_history) = &self.previous_poll_history {
- let ix = self.poll_history.len() - 1;
- let prev_event = prev_history[ix];
- if event != prev_event {
- let mut message = String::new();
- writeln!(
- &mut message,
- "current runnable backtrace:\n{:?}",
- self.runnable_backtraces.get_mut(&event.id()).map(|trace| {
- trace.resolve();
- crate::util::CwdBacktrace(trace)
- })
- )
- .unwrap();
- writeln!(
- &mut message,
- "previous runnable backtrace:\n{:?}",
- self.runnable_backtraces
- .get_mut(&prev_event.id())
- .map(|trace| {
- trace.resolve();
- util::CwdBacktrace(trace)
- })
- )
- .unwrap();
- panic!("detected non-determinism after {ix}. {message}");
- }
- }
- }
-
- fn will_park(&mut self) {
- if self.forbid_parking {
- let mut backtrace_message = String::new();
- #[cfg(any(test, feature = "test"))]
- if let Some(backtrace) = self.waiting_backtrace.as_mut() {
- backtrace.resolve();
- backtrace_message = format!(
- "\nbacktrace of waiting future:\n{:?}",
- util::CwdBacktrace(backtrace)
- );
- }
-
- panic!(
- "deterministic executor parked after a call to forbid_parking{}",
- backtrace_message
- );
- }
+impl<T> Task<T> {
+ pub fn ready(val: T) -> Self {
+ Task::Ready(Some(val))
}
-}
-#[cfg(any(test, feature = "test"))]
-impl ExecutorEvent {
- pub fn id(&self) -> usize {
+ pub fn detach(self) {
match self {
- ExecutorEvent::PollRunnable { id } => *id,
- ExecutorEvent::EnqueuRunnable { id } => *id,
+ Task::Ready(_) => {}
+ Task::Spawned(task) => task.detach(),
}
}
}
-impl ForegroundExecutor {
- pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Result<Self> {
- if dispatcher.is_main_thread() {
- Ok(Self::Platform {
- dispatcher,
- _not_send_or_sync: PhantomData,
- })
- } else {
- Err(anyhow!("must be constructed on main thread"))
- }
- }
-
- pub fn spawn<T: 'static>(&self, future: impl Future<Output = T> + 'static) -> Task<T> {
- let future = any_local_future(future);
- let any_task = match self {
- #[cfg(any(test, feature = "test"))]
- Self::Deterministic { cx_id, executor } => {
- executor.spawn_from_foreground(*cx_id, future, false)
- }
- Self::Platform { dispatcher, .. } => {
- fn spawn_inner(
- future: AnyLocalFuture,
- dispatcher: &Arc<dyn PlatformDispatcher>,
- ) -> AnyLocalTask {
- let dispatcher = dispatcher.clone();
- let schedule =
- move |runnable: Runnable| dispatcher.run_on_main_thread(runnable);
- let (runnable, task) = async_task::spawn_local(future, schedule);
- runnable.schedule();
- task
- }
- spawn_inner(future, dispatcher)
- }
- };
- Task::local(any_task)
- }
-
- #[cfg(any(test, feature = "test"))]
- pub fn run<T: 'static>(&self, future: impl Future<Output = T>) -> T {
- let future = async move { Box::new(future.await) as Box<dyn Any> }.boxed_local();
- let result = match self {
- Self::Deterministic { cx_id, executor } => executor.run(*cx_id, future),
- Self::Platform { .. } => panic!("you can't call run on a platform foreground executor"),
- };
- *result.downcast().unwrap()
- }
-
- #[cfg(any(test, feature = "test"))]
- pub fn run_until_parked(&self) {
- match self {
- Self::Deterministic { executor, .. } => executor.run_until_parked(),
- _ => panic!("this method can only be called on a deterministic executor"),
- }
- }
-
- #[cfg(any(test, feature = "test"))]
- pub fn parking_forbidden(&self) -> bool {
- match self {
- Self::Deterministic { executor, .. } => executor.state.lock().forbid_parking,
- _ => panic!("this method can only be called on a deterministic executor"),
- }
- }
-
- #[cfg(any(test, feature = "test"))]
- pub fn start_waiting(&self) {
- match self {
- Self::Deterministic { executor, .. } => executor.start_waiting(),
- _ => panic!("this method can only be called on a deterministic executor"),
- }
- }
-
- #[cfg(any(test, feature = "test"))]
- pub fn finish_waiting(&self) {
- match self {
- Self::Deterministic { executor, .. } => executor.finish_waiting(),
- _ => panic!("this method can only be called on a deterministic executor"),
- }
- }
-
- #[cfg(any(test, feature = "test"))]
- pub fn forbid_parking(&self) {
- match self {
- Self::Deterministic { executor, .. } => executor.forbid_parking(),
- _ => panic!("this method can only be called on a deterministic executor"),
- }
- }
-
- #[cfg(any(test, feature = "test"))]
- pub fn allow_parking(&self) {
- match self {
- Self::Deterministic { executor, .. } => executor.allow_parking(),
- _ => panic!("this method can only be called on a deterministic executor"),
- }
- }
-
- #[cfg(any(test, feature = "test"))]
- pub fn advance_clock(&self, duration: Duration) {
- match self {
- Self::Deterministic { executor, .. } => executor.advance_clock(duration),
- _ => panic!("this method can only be called on a deterministic executor"),
- }
- }
+impl<T> Future for Task<T> {
+ type Output = T;
- #[cfg(any(test, feature = "test"))]
- pub fn set_block_on_ticks(&self, range: std::ops::RangeInclusive<usize>) {
- match self {
- Self::Deterministic { executor, .. } => executor.state.lock().block_on_ticks = range,
- _ => panic!("this method can only be called on a deterministic executor"),
+ fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+ match unsafe { self.get_unchecked_mut() } {
+ Task::Ready(val) => Poll::Ready(val.take().unwrap()),
+ Task::Spawned(task) => task.poll(cx),
}
}
}
-impl BackgroundExecutor {
- pub fn new() -> Self {
- let executor = Arc::new(Executor::new());
- let stop = channel::unbounded::<()>();
-
- for i in 0..2 * num_cpus::get() {
- let executor = executor.clone();
- let stop = stop.1.clone();
- thread::Builder::new()
- .name(format!("background-executor-{}", i))
- .spawn(move || smol::block_on(executor.run(stop.recv())))
- .unwrap();
- }
-
- Self::Production {
- executor,
- _stop: stop.0,
- }
- }
-
- pub fn num_cpus(&self) -> usize {
- num_cpus::get()
+impl Executor {
+ pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
+ Self { dispatcher }
}
- pub fn spawn<T, F>(&self, future: F) -> Task<T>
+ /// Enqueues the given closure to be run on any thread. The closure returns
+ /// a future which will be run to completion on any available thread.
+ pub fn spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
where
- T: 'static + Send,
- F: Send + Future<Output = T> + 'static,
+ R: Send + 'static,
{
- let future = any_future(future);
- let any_task = match self {
- Self::Production { executor, .. } => executor.spawn(future),
- #[cfg(any(test, feature = "test"))]
- Self::Deterministic { executor } => executor.spawn(future),
- };
- Task::send(any_task)
- }
-
- pub fn block<F, T>(&self, future: F) -> T
- where
- F: Future<Output = T>,
- {
- smol::pin!(future);
- match self {
- Self::Production { .. } => smol::block_on(&mut future),
- #[cfg(any(test, feature = "test"))]
- Self::Deterministic { executor, .. } => {
- executor.block(&mut future, usize::MAX).unwrap()
- }
- }
+ let dispatcher = self.dispatcher.clone();
+ let (runnable, task) =
+ async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable));
+ runnable.schedule();
+ Task::Spawned(task)
}
- pub fn block_with_timeout<F, T>(
- &self,
- timeout: Duration,
- future: F,
- ) -> Result<T, impl Future<Output = T>>
+ /// Enqueues the given closure to run on the application's event loop.
+ /// Returns the result asynchronously.
+ pub fn run_on_main<F, R>(&self, func: F) -> Task<R>
where
- T: 'static,
- F: 'static + Unpin + Future<Output = T>,
+ F: FnOnce() -> R + Send + 'static,
+ R: Send + 'static,
{
- let mut future = any_local_future(future);
- if !timeout.is_zero() {
- let output = match self {
- Self::Production { .. } => smol::block_on(util::timeout(timeout, &mut future)).ok(),
- #[cfg(any(test, feature = "test"))]
- Self::Deterministic { executor, .. } => {
- use rand::prelude::*;
- let max_ticks = {
- let mut state = executor.state.lock();
- let range = state.block_on_ticks.clone();
- state.rng.gen_range(range)
- };
- executor.block(&mut future, max_ticks)
- }
- };
- if let Some(output) = output {
- return Ok(*output.downcast().unwrap());
- }
+ if self.dispatcher.is_main_thread() {
+ Task::ready(func())
+ } else {
+ self.spawn_on_main(move || async move { func() })
}
- Err(async { *future.await.downcast().unwrap() })
}
- pub async fn scoped<'scope, F>(self: &Arc<Self>, scheduler: F)
+ /// Enqueues the given closure to be run on the application's event loop. The
+ /// closure returns a future which will be run to completion on the main thread.
+ pub fn spawn_on_main<F, R>(&self, func: impl FnOnce() -> F + Send + 'static) -> Task<R>
where
- F: FnOnce(&mut Scope<'scope>),
+ F: Future<Output = R> + 'static,
+ R: Send + 'static,
{
- let mut scope = Scope::new(self.clone());
- (scheduler)(&mut scope);
- let spawned = mem::take(&mut scope.futures)
- .into_iter()
- .map(|f| self.spawn(f))
- .collect::<Vec<_>>();
- for task in spawned {
- task.await;
- }
- }
-
- pub fn timer(&self, duration: Duration) -> Timer {
- match self {
- BackgroundExecutor::Production { .. } => {
- Timer::Production(smol::Timer::after(duration))
- }
- #[cfg(any(test, feature = "test"))]
- BackgroundExecutor::Deterministic { executor } => executor.timer(duration),
- }
- }
-
- pub fn now(&self) -> std::time::Instant {
- match self {
- BackgroundExecutor::Production { .. } => std::time::Instant::now(),
- #[cfg(any(test, feature = "test"))]
- BackgroundExecutor::Deterministic { executor } => executor.now(),
- }
- }
-
- #[cfg(any(test, feature = "test"))]
- pub fn rng<'a>(&'a self) -> impl 'a + std::ops::DerefMut<Target = rand::prelude::StdRng> {
- match self {
- Self::Deterministic { executor, .. } => {
- parking_lot::lock_api::MutexGuard::map(executor.state.lock(), |s| &mut s.rng)
- }
- _ => panic!("this method can only be called on a deterministic executor"),
- }
- }
-
- #[cfg(any(test, feature = "test"))]
- pub async fn simulate_random_delay(&self) {
- match self {
- Self::Deterministic { executor, .. } => {
- executor.simulate_random_delay().await;
- }
- _ => {
- panic!("this method can only be called on a deterministic executor")
- }
- }
- }
-
- #[cfg(any(test, feature = "test"))]
- pub fn record_backtrace(&self) {
- match self {
- Self::Deterministic { executor, .. } => executor.record_backtrace(),
- _ => {
- panic!("this method can only be called on a deterministic executor")
- }
- }
- }
-
- #[cfg(any(test, feature = "test"))]
- pub fn start_waiting(&self) {
- match self {
- Self::Deterministic { executor, .. } => executor.start_waiting(),
- _ => panic!("this method can only be called on a deterministic executor"),
- }
- }
-}
-
-impl Default for BackgroundExecutor {
- fn default() -> Self {
- Self::new()
- }
-}
-
-pub struct Scope<'a> {
- executor: Arc<BackgroundExecutor>,
- futures: Vec<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
- tx: Option<mpsc::Sender<()>>,
- rx: mpsc::Receiver<()>,
- _phantom: PhantomData<&'a ()>,
-}
-
-impl<'a> Scope<'a> {
- fn new(executor: Arc<BackgroundExecutor>) -> Self {
- let (tx, rx) = mpsc::channel(1);
- Self {
- executor,
- tx: Some(tx),
- rx,
- futures: Default::default(),
- _phantom: PhantomData,
- }
+ let dispatcher = self.dispatcher.clone();
+ let (runnable, task) =
+ async_task::spawn_local(async move { func().await }, move |runnable| {
+ dispatcher.dispatch_on_main_thread(runnable)
+ });
+ runnable.schedule();
+ Task::Spawned(task)
}
- pub fn spawn<F>(&mut self, f: F)
+ /// Enqueues the given closure to be run on the application's event loop. Must
+ /// be called on the main thread.
+ pub fn spawn_on_main_local<R>(&self, future: impl Future<Output = R> + 'static) -> Task<R>
where
- F: Future<Output = ()> + Send + 'a,
+ R: 'static,
{
- let tx = self.tx.clone().unwrap();
-
- // Safety: The 'a lifetime is guaranteed to outlive any of these futures because
- // dropping this `Scope` blocks until all of the futures have resolved.
- let f = unsafe {
- mem::transmute::<
- Pin<Box<dyn Future<Output = ()> + Send + 'a>>,
- Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
- >(Box::pin(async move {
- f.await;
- drop(tx);
- }))
- };
- self.futures.push(f);
- }
-}
-
-impl<'a> Drop for Scope<'a> {
- fn drop(&mut self) {
- self.tx.take().unwrap();
-
- // Wait until the channel is closed, which means that all of the spawned
- // futures have resolved.
- self.executor.block(self.rx.next());
- }
-}
+ assert!(
+ self.dispatcher.is_main_thread(),
+ "must be called on main thread"
+ );
-impl<T> Task<T> {
- pub fn ready(value: T) -> Self {
- Self::Ready(Some(value))
- }
-
- fn local(any_task: AnyLocalTask) -> Self {
- Self::Local {
- any_task,
- result_type: PhantomData,
- }
- }
-
- pub fn detach(self) {
- match self {
- Task::Ready(_) => {}
- Task::Local { any_task, .. } => any_task.detach(),
- Task::Send { any_task, .. } => any_task.detach(),
- }
- }
-}
-
-// impl<T: 'static, E: 'static + Display> Task<Result<T, E>> {
-// #[track_caller]
-// pub fn detach_and_log_err(self, cx: &mut AppContext) {
-// let caller = Location::caller();
-// cx.spawn(|_| async move {
-// if let Err(err) = self.await {
-// log::error!("{}:{}: {:#}", caller.file(), caller.line(), err);
-// }
-// })
-// .detach();
-// }
-// }
-
-impl<T: Send> Task<T> {
- fn send(any_task: AnyTask) -> Self {
- Self::Send {
- any_task,
- result_type: PhantomData,
- }
- }
-}
-
-impl<T: fmt::Debug> fmt::Debug for Task<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Task::Ready(value) => value.fmt(f),
- Task::Local { any_task, .. } => any_task.fmt(f),
- Task::Send { any_task, .. } => any_task.fmt(f),
- }
+ let dispatcher = self.dispatcher.clone();
+ let (runnable, task) = async_task::spawn_local(future, move |runnable| {
+ dispatcher.dispatch_on_main_thread(runnable)
+ });
+ runnable.schedule();
+ Task::Spawned(task)
}
-}
-impl<T: 'static> Future for Task<T> {
- type Output = T;
-
- fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
- match unsafe { self.get_unchecked_mut() } {
- Task::Ready(value) => Poll::Ready(value.take().unwrap()),
- Task::Local { any_task, .. } => {
- any_task.poll(cx).map(|value| *value.downcast().unwrap())
- }
- Task::Send { any_task, .. } => {
- any_task.poll(cx).map(|value| *value.downcast().unwrap())
- }
- }
+ pub fn is_main_thread(&self) -> bool {
+ self.dispatcher.is_main_thread()
}
}
-
-fn any_future<T, F>(future: F) -> AnyFuture
-where
- T: 'static + Send,
- F: Future<Output = T> + Send + 'static,
-{
- async { Box::new(future.await) as Box<dyn Any + Send> }.boxed()
-}
-
-fn any_local_future<T, F>(future: F) -> AnyLocalFuture
-where
- T: 'static,
- F: Future<Output = T> + 'static,
-{
- async { Box::new(future.await) as Box<dyn Any> }.boxed_local()
-}
@@ -227,14 +227,14 @@ impl<'a, T> DerefMut for Reference<'a, T> {
}
pub(crate) struct MainThreadOnly<T: ?Sized> {
- dispatcher: Arc<dyn PlatformDispatcher>,
+ executor: Executor,
value: Arc<T>,
}
impl<T: ?Sized> Clone for MainThreadOnly<T> {
fn clone(&self) -> Self {
Self {
- dispatcher: self.dispatcher.clone(),
+ executor: self.executor.clone(),
value: self.value.clone(),
}
}
@@ -243,12 +243,12 @@ impl<T: ?Sized> Clone for MainThreadOnly<T> {
/// Allows a value to be accessed only on the main thread, allowing a non-`Send` type
/// to become `Send`.
impl<T: 'static + ?Sized> MainThreadOnly<T> {
- pub(crate) fn new(value: Arc<T>, dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
- Self { dispatcher, value }
+ pub(crate) fn new(value: Arc<T>, executor: Executor) -> Self {
+ Self { executor, value }
}
pub(crate) fn borrow_on_main_thread(&self) -> &T {
- assert!(self.dispatcher.is_main_thread());
+ assert!(self.executor.is_main_thread());
&self.value
}
}
@@ -7,8 +7,8 @@ mod test;
use crate::image_cache::RenderImageParams;
use crate::{
- AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point,
- RenderGlyphParams, RenderSvgParams, Result, Scene, ShapedLine, SharedString, Size,
+ AnyWindowHandle, Bounds, DevicePixels, Executor, Font, FontId, FontMetrics, GlyphId, Pixels,
+ Point, RenderGlyphParams, RenderSvgParams, Result, Scene, ShapedLine, SharedString, Size,
};
use anyhow::anyhow;
use async_task::Runnable;
@@ -43,7 +43,7 @@ pub(crate) fn current_platform() -> Arc<dyn Platform> {
}
pub trait Platform: 'static {
- fn dispatcher(&self) -> Arc<dyn PlatformDispatcher>;
+ fn executor(&self) -> Executor;
fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>);
@@ -154,7 +154,8 @@ pub trait PlatformWindow {
pub trait PlatformDispatcher: Send + Sync {
fn is_main_thread(&self) -> bool;
- fn run_on_main_thread(&self, task: Runnable);
+ fn dispatch(&self, task: Runnable);
+ fn dispatch_on_main_thread(&self, task: Runnable);
}
pub trait PlatformTextSystem: Send + Sync {
@@ -25,18 +25,49 @@ impl PlatformDispatcher for MacDispatcher {
is_main_thread == YES
}
- fn run_on_main_thread(&self, runnable: Runnable) {
+ fn dispatch(&self, runnable: Runnable) {
unsafe {
dispatch_async_f(
- dispatch_get_main_queue(),
+ dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0),
runnable.into_raw() as *mut c_void,
Some(trampoline),
);
}
+ }
- extern "C" fn trampoline(runnable: *mut c_void) {
- let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
- task.run();
+ fn dispatch_on_main_thread(&self, runnable: Runnable) {
+ unsafe {
+ dispatch_async_f(
+ dispatch_get_main_queue(),
+ runnable.into_raw() as *mut c_void,
+ Some(trampoline),
+ );
}
}
}
+
+extern "C" fn trampoline(runnable: *mut c_void) {
+ let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
+ task.run();
+}
+
+// #include <dispatch/dispatch.h>
+
+// int main(void) {
+
+// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+// // Do some lengthy background work here...
+// printf("Background Work\n");
+
+// dispatch_async(dispatch_get_main_queue(), ^{
+// // Once done, update your UI on the main queue here.
+// printf("UI Updated\n");
+
+// });
+// });
+
+// sleep(3); // prevent the program from terminating immediately
+
+// return 0;
+// }
+// ```
@@ -1,8 +1,8 @@
use super::BoolExt;
use crate::{
- AnyWindowHandle, ClipboardItem, CursorStyle, Event, MacDispatcher, MacScreen, MacTextSystem,
- MacWindow, PathPromptOptions, Platform, PlatformScreen, PlatformTextSystem, PlatformWindow,
- Result, ScreenId, SemanticVersion, WindowOptions,
+ AnyWindowHandle, ClipboardItem, CursorStyle, Event, Executor, MacDispatcher, MacScreen,
+ MacTextSystem, MacWindow, PathPromptOptions, Platform, PlatformScreen, PlatformTextSystem,
+ PlatformWindow, Result, ScreenId, SemanticVersion, WindowOptions,
};
use anyhow::anyhow;
use block::ConcreteBlock;
@@ -142,7 +142,7 @@ unsafe fn build_classes() {
pub struct MacPlatform(Mutex<MacPlatformState>);
pub struct MacPlatformState {
- dispatcher: Arc<MacDispatcher>,
+ executor: Executor,
text_system: Arc<MacTextSystem>,
pasteboard: id,
text_hash_pasteboard_type: id,
@@ -163,7 +163,7 @@ pub struct MacPlatformState {
impl MacPlatform {
pub fn new() -> Self {
Self(Mutex::new(MacPlatformState {
- dispatcher: Arc::new(MacDispatcher),
+ executor: Executor::new(Arc::new(MacDispatcher)),
text_system: Arc::new(MacTextSystem::new()),
pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) },
text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") },
@@ -343,8 +343,8 @@ impl MacPlatform {
}
impl Platform for MacPlatform {
- fn dispatcher(&self) -> Arc<dyn crate::PlatformDispatcher> {
- Arc::new(MacDispatcher)
+ fn executor(&self) -> Executor {
+ self.0.lock().executor.clone()
}
fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
@@ -479,7 +479,7 @@ impl Platform for MacPlatform {
handle: AnyWindowHandle,
options: WindowOptions,
) -> Box<dyn PlatformWindow> {
- Box::new(MacWindow::open(handle, options, self))
+ Box::new(MacWindow::open(handle, options, self.executor()))
}
fn open_url(&self, url: &str) {
@@ -566,17 +566,20 @@ impl Platform for MacPlatform {
fn reveal_path(&self, path: &Path) {
unsafe {
let path = path.to_path_buf();
- let dispatcher = self.0.lock().dispatcher.clone();
- let _ = crate::spawn_on_main_local(dispatcher, async move {
- let full_path = ns_string(path.to_str().unwrap_or(""));
- let root_full_path = ns_string("");
- let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
- let _: BOOL = msg_send![
- workspace,
- selectFile: full_path
- inFileViewerRootedAtPath: root_full_path
- ];
- });
+ self.0
+ .lock()
+ .executor
+ .spawn_on_main_local(async move {
+ let full_path = ns_string(path.to_str().unwrap_or(""));
+ let root_full_path = ns_string("");
+ let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
+ let _: BOOL = msg_send![
+ workspace,
+ selectFile: full_path
+ inFileViewerRootedAtPath: root_full_path
+ ];
+ })
+ .detach();
}
}
@@ -1,10 +1,10 @@
use super::{ns_string, MetalRenderer, NSRange};
use crate::{
- point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers,
- ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt,
- Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen,
- PlatformWindow, Point, Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind,
- WindowOptions, WindowPromptLevel,
+ point, px, size, AnyWindowHandle, Bounds, Event, Executor, KeyDownEvent, Keystroke, MacScreen,
+ Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent,
+ NSRectExt, Pixels, PlatformAtlas, PlatformInputHandler, PlatformScreen, PlatformWindow, Point,
+ Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
+ WindowPromptLevel,
};
use block::ConcreteBlock;
use cocoa::{
@@ -279,7 +279,7 @@ struct InsertText {
struct MacWindowState {
handle: AnyWindowHandle,
- dispatcher: Arc<dyn PlatformDispatcher>,
+ executor: Executor,
native_window: id,
renderer: MetalRenderer,
scene_to_render: Option<Scene>,
@@ -415,7 +415,7 @@ unsafe impl Send for MacWindowState {}
pub struct MacWindow(Arc<Mutex<MacWindowState>>);
impl MacWindow {
- pub fn open(handle: AnyWindowHandle, options: WindowOptions, platform: &dyn Platform) -> Self {
+ pub fn open(handle: AnyWindowHandle, options: WindowOptions, executor: Executor) -> Self {
unsafe {
let pool = NSAutoreleasePool::new(nil);
@@ -479,7 +479,7 @@ impl MacWindow {
let window = Self(Arc::new(Mutex::new(MacWindowState {
handle,
- dispatcher: platform.dispatcher(),
+ executor,
native_window,
renderer: MetalRenderer::new(true),
scene_to_render: None,
@@ -616,12 +616,12 @@ impl MacWindow {
impl Drop for MacWindow {
fn drop(&mut self) {
let this = self.0.clone();
- let dispatcher = self.0.lock().dispatcher.clone();
- let _ = crate::spawn_on_main(dispatcher, || async move {
- unsafe {
+ let executor = self.0.lock().executor.clone();
+ executor
+ .run_on_main(move || unsafe {
this.lock().native_window.close();
- }
- });
+ })
+ .detach();
}
}
@@ -739,14 +739,16 @@ impl PlatformWindow for MacWindow {
});
let block = block.copy();
let native_window = self.0.lock().native_window;
- let dispatcher = self.0.lock().dispatcher.clone();
- let _ = crate::spawn_on_main_local(dispatcher, async move {
- let _: () = msg_send![
- alert,
- beginSheetModalForWindow: native_window
- completionHandler: block
- ];
- });
+ let executor = self.0.lock().executor.clone();
+ executor
+ .spawn_on_main_local(async move {
+ let _: () = msg_send![
+ alert,
+ beginSheetModalForWindow: native_window
+ completionHandler: block
+ ];
+ })
+ .detach();
done_rx
}
@@ -754,12 +756,14 @@ impl PlatformWindow for MacWindow {
fn activate(&self) {
let window = self.0.lock().native_window;
- let dispatcher = self.0.lock().dispatcher.clone();
- let _ = crate::spawn_on_main_local(dispatcher.clone(), async move {
- unsafe {
- let _: () = msg_send![window, makeKeyAndOrderFront: nil];
- }
- });
+ let executor = self.0.lock().executor.clone();
+ executor
+ .spawn_on_main_local(async move {
+ unsafe {
+ let _: () = msg_send![window, makeKeyAndOrderFront: nil];
+ }
+ })
+ .detach();
}
fn set_title(&mut self, title: &str) {
@@ -802,23 +806,25 @@ impl PlatformWindow for MacWindow {
fn zoom(&self) {
let this = self.0.lock();
let window = this.native_window;
- let dispatcher = this.dispatcher.clone();
- let _ = crate::spawn_on_main_local(dispatcher, async move {
- unsafe {
- window.zoom_(nil);
- }
- });
+ this.executor
+ .spawn_on_main_local(async move {
+ unsafe {
+ window.zoom_(nil);
+ }
+ })
+ .detach();
}
fn toggle_full_screen(&self) {
let this = self.0.lock();
let window = this.native_window;
- let dispatcher = this.dispatcher.clone();
- let _ = crate::spawn_on_main_local(dispatcher, async move {
- unsafe {
- window.toggleFullScreen_(nil);
- }
- });
+ this.executor
+ .spawn_on_main_local(async move {
+ unsafe {
+ window.toggleFullScreen_(nil);
+ }
+ })
+ .detach();
}
fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>) {
@@ -1114,15 +1120,14 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
},
) => {
lock.synthetic_drag_counter += 1;
- let dispatcher = lock.dispatcher.clone();
- let _ = crate::spawn_on_main_local(
- dispatcher,
- synthetic_drag(
+ let executor = lock.executor.clone();
+ executor
+ .spawn_on_main_local(synthetic_drag(
weak_window_state,
lock.synthetic_drag_counter,
event.clone(),
- ),
- );
+ ))
+ .detach();
}
Event::MouseMoved(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
@@ -1241,16 +1246,18 @@ extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id)
}
}
- let dispatcher = lock.dispatcher.clone();
+ let executor = lock.executor.clone();
drop(lock);
- let _ = crate::spawn_on_main_local(dispatcher, async move {
- let mut lock = window_state.as_ref().lock();
- if let Some(mut callback) = lock.activate_callback.take() {
- drop(lock);
- callback(is_active);
- window_state.lock().activate_callback = Some(callback);
- };
- });
+ executor
+ .spawn_on_main_local(async move {
+ let mut lock = window_state.as_ref().lock();
+ if let Some(mut callback) = lock.activate_callback.take() {
+ drop(lock);
+ callback(is_active);
+ window_state.lock().activate_callback = Some(callback);
+ };
+ })
+ .detach();
}
extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
@@ -1,5 +1,5 @@
use super::Platform;
-use crate::ScreenId;
+use crate::{Executor, ScreenId};
pub struct TestPlatform;
@@ -11,7 +11,7 @@ impl TestPlatform {
// todo!("implement out what our tests needed in GPUI 1")
impl Platform for TestPlatform {
- fn dispatcher(&self) -> std::sync::Arc<dyn crate::PlatformDispatcher> {
+ fn executor(&self) -> Executor {
unimplemented!()
}
@@ -1,18 +1,16 @@
-use smol::future::FutureExt;
-use std::{future::Future, time::Duration};
pub use util::*;
-pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
-where
- F: Future<Output = T>,
-{
- let timer = async {
- smol::Timer::after(timeout).await;
- Err(())
- };
- let future = async move { Ok(f.await) };
- timer.race(future).await
-}
+// pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
+// where
+// F: Future<Output = T>,
+// {
+// let timer = async {
+// smol::Timer::after(timeout).await;
+// Err(())
+// };
+// let future = async move { Ok(f.await) };
+// timer.race(future).await
+// }
#[cfg(any(test, feature = "test"))]
pub struct CwdBacktrace<'a>(pub &'a backtrace::Backtrace);
@@ -53,8 +53,7 @@ impl Window {
}
}));
- let platform_window =
- MainThreadOnly::new(Arc::new(platform_window), cx.platform().dispatcher());
+ let platform_window = MainThreadOnly::new(Arc::new(platform_window), cx.executor.clone());
Window {
handle,
@@ -122,7 +121,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
R: Send + 'static,
{
let (tx, rx) = oneshot::channel();
- if self.dispatcher.is_main_thread() {
+ if self.executor.is_main_thread() {
let _ = tx.send(f(unsafe {
mem::transmute::<&mut Self, &mut MainThread<Self>>(self)
}));
@@ -600,7 +599,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
R: Send + 'static,
{
let (tx, rx) = oneshot::channel();
- if self.dispatcher.is_main_thread() {
+ if self.executor.is_main_thread() {
let cx = unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(self) };
let _ = tx.send(f(view, cx));
} else {