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};
@@ -29,7 +29,7 @@ use util::{
};
#[derive(Clone)]
-pub struct App(Arc<Mutex<MainThread<AppContext>>>);
+pub struct App(Arc<Mutex<AppContext>>);
impl App {
pub fn production(asset_source: Arc<dyn AssetSource>) -> Self {
@@ -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(), ());
+ let unit_entity = entities.insert(entities.reserve(), ());
Self(Arc::new_cyclic(|this| {
- Mutex::new(MainThread::new(AppContext {
+ Mutex::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),
@@ -71,7 +71,7 @@ impl App {
pending_effects: Default::default(),
observers: Default::default(),
layout_id_buffer: Default::default(),
- }))
+ })
}))
}
@@ -83,6 +83,7 @@ impl App {
let platform = self.0.lock().platform.clone();
platform.borrow_on_main_thread().run(Box::new(move || {
let cx = &mut *this.0.lock();
+ let cx = unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) };
on_finish_launching(cx);
}));
}
@@ -91,11 +92,11 @@ impl App {
type Handlers = SmallVec<[Arc<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>; 2]>;
pub struct AppContext {
- this: Weak<Mutex<MainThread<AppContext>>>,
+ this: Weak<Mutex<AppContext>>,
platform: MainThreadOnly<dyn Platform>,
- dispatcher: Arc<dyn PlatformDispatcher>,
text_system: Arc<TextSystem>,
pending_updates: usize,
+ pub(crate) executor: Executor,
pub(crate) svg_renderer: SvgRenderer,
pub(crate) image_cache: ImageCache,
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
@@ -109,7 +110,7 @@ pub struct AppContext {
}
impl AppContext {
- fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
+ pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
self.pending_updates += 1;
let result = update(self);
if self.pending_updates == 1 {
@@ -144,6 +145,7 @@ impl AppContext {
}
fn flush_effects(&mut self) {
+ dbg!("flush effects");
while let Some(effect) = self.pending_effects.pop_front() {
match effect {
Effect::Notify(entity_id) => self.apply_notify_effect(entity_id),
@@ -180,36 +182,44 @@ impl AppContext {
}
}
- pub fn to_async(&self) -> AsyncContext {
- AsyncContext(unsafe { mem::transmute(self.this.clone()) })
+ pub fn to_async(&self) -> AsyncAppContext {
+ AsyncAppContext(unsafe { mem::transmute(self.this.clone()) })
+ }
+
+ pub fn executor(&self) -> &Executor {
+ &self.executor
}
pub fn run_on_main<R>(
- &self,
+ &mut self,
f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
- ) -> impl Future<Output = R>
+ ) -> Task<R>
where
R: Send + 'static,
{
- let this = self.this.upgrade().unwrap();
- run_on_main(self.dispatcher.clone(), move || {
- let cx = &mut *this.lock();
- cx.update(|cx| {
- f(unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) })
+ if self.executor.is_main_thread() {
+ Task::ready(f(unsafe {
+ mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(self)
+ }))
+ } else {
+ let this = self.this.upgrade().unwrap();
+ self.executor.run_on_main(move || {
+ let cx = &mut *this.lock();
+ cx.update(|cx| f(unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(cx) }))
})
- })
+ }
}
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,
+ F: Future<Output = R> + Send + '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) })
@@ -217,6 +227,18 @@ impl AppContext {
})
}
+ pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>
+ where
+ Fut: Future<Output = R> + Send + 'static,
+ R: Send + 'static,
+ {
+ let cx = self.to_async();
+ self.executor.spawn(async move {
+ let future = f(cx);
+ future.await
+ })
+ }
+
pub fn text_system(&self) -> &Arc<TextSystem> {
&self.text_system
}
@@ -278,9 +300,11 @@ impl Context for AppContext {
&mut self,
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
) -> Handle<T> {
- let slot = self.entities.reserve();
- let entity = build_entity(&mut ModelContext::mutable(self, slot.id));
- self.entities.redeem(slot, entity)
+ self.update(|cx| {
+ let slot = cx.entities.reserve();
+ let entity = build_entity(&mut ModelContext::mutable(cx, slot.id));
+ cx.entities.insert(slot, entity)
+ })
}
fn update_entity<T: Send + Sync + 'static, R>(
@@ -288,10 +312,12 @@ impl Context for AppContext {
handle: &Handle<T>,
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
) -> R {
- let mut entity = self.entities.lease(handle);
- let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
- self.entities.end_lease(entity);
- result
+ self.update(|cx| {
+ let mut entity = cx.entities.lease(handle);
+ let result = update(&mut entity, &mut ModelContext::mutable(cx, handle.id));
+ cx.entities.end_lease(entity);
+ result
+ })
}
}
@@ -307,7 +333,7 @@ impl MainThread<AppContext> {
pub(crate) fn update_window<R>(
&mut self,
id: WindowId,
- update: impl FnOnce(&mut WindowContext) -> R,
+ update: impl FnOnce(&mut MainThread<WindowContext>) -> R,
) -> Result<R> {
self.0.update_window(id, |cx| {
update(unsafe {
@@ -1,13 +1,15 @@
-use crate::{AnyWindowHandle, AppContext, Context, Handle, ModelContext, Result, WindowContext};
+use crate::{
+ AnyWindowHandle, AppContext, Context, Handle, ModelContext, Result, ViewContext, WindowContext,
+};
use anyhow::anyhow;
use parking_lot::Mutex;
use std::sync::Weak;
#[derive(Clone)]
-pub struct AsyncContext(pub(crate) Weak<Mutex<AppContext>>);
+pub struct AsyncAppContext(pub(crate) Weak<Mutex<AppContext>>);
-impl Context for AsyncContext {
- type EntityContext<'a, 'b, T: 'static + Send + Sync> = ModelContext<'a, T>;
+impl Context for AsyncAppContext {
+ type EntityContext<'a, 'w, T: 'static + Send + Sync> = ModelContext<'a, T>;
type Result<T> = Result<T>;
fn entity<T: Send + Sync + 'static>(
@@ -18,7 +20,7 @@ impl Context for AsyncContext {
.0
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
- let mut lock = app.lock();
+ let mut lock = app.lock(); // Does not compile without this variable.
Ok(lock.entity(build_entity))
}
@@ -31,17 +33,17 @@ impl Context for AsyncContext {
.0
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
- let mut lock = app.lock();
+ let mut lock = app.lock(); // Does not compile without this variable.
Ok(lock.update_entity(handle, update))
}
}
-impl AsyncContext {
- pub fn update_window<T>(
+impl AsyncAppContext {
+ pub fn update_window<R>(
&self,
handle: AnyWindowHandle,
- update: impl FnOnce(&mut WindowContext) -> T + Send + Sync,
- ) -> Result<T> {
+ update: impl FnOnce(&mut WindowContext) -> R,
+ ) -> Result<R> {
let app = self
.0
.upgrade()
@@ -50,3 +52,37 @@ impl AsyncContext {
app_context.update_window(handle.id, update)
}
}
+
+#[derive(Clone)]
+pub struct AsyncWindowContext {
+ app: AsyncAppContext,
+ window: AnyWindowHandle,
+}
+
+impl AsyncWindowContext {
+ pub fn new(app: AsyncAppContext, window: AnyWindowHandle) -> Self {
+ Self { app, window }
+ }
+}
+
+impl Context for AsyncWindowContext {
+ type EntityContext<'a, 'w, T: 'static + Send + Sync> = ViewContext<'a, 'w, T>;
+ type Result<T> = Result<T>;
+
+ fn entity<R: Send + Sync + 'static>(
+ &mut self,
+ build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, R>) -> R,
+ ) -> Result<Handle<R>> {
+ self.app
+ .update_window(self.window, |cx| cx.entity(build_entity))
+ }
+
+ fn update_entity<T: Send + Sync + 'static, R>(
+ &mut self,
+ handle: &Handle<T>,
+ update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
+ ) -> Result<R> {
+ self.app
+ .update_window(self.window, |cx| cx.update_entity(handle, update))
+ }
+}
@@ -14,14 +14,6 @@ use std::{
slotmap::new_key_type! { pub struct EntityId; }
-#[derive(Deref, DerefMut)]
-pub struct Lease<T> {
- #[deref]
- #[deref_mut]
- entity: Box<T>,
- pub id: EntityId,
-}
-
pub(crate) struct EntityMap {
ref_counts: Arc<RwLock<RefCounts>>,
entities: Arc<Mutex<SecondaryMap<EntityId, Box<dyn Any + Send + Sync>>>>,
@@ -35,31 +27,38 @@ impl EntityMap {
}
}
+ /// Reserve a slot for an entity, which you can subsequently use with `insert`.
pub fn reserve<T: 'static + Send + Sync>(&self) -> Slot<T> {
let id = self.ref_counts.write().insert(1.into());
Slot(Handle::new(id, Arc::downgrade(&self.ref_counts)))
}
- pub fn redeem<T: 'static + Any + Send + Sync>(&self, slot: Slot<T>, entity: T) -> Handle<T> {
+ /// Insert an entity into a slot obtained by calling `reserve`.
+ pub fn insert<T: 'static + Any + Send + Sync>(&self, slot: Slot<T>, entity: T) -> Handle<T> {
let handle = slot.0;
self.entities.lock().insert(handle.id, Box::new(entity));
handle
}
+ /// Move an entity to the stack.
pub fn lease<T: 'static + Send + Sync>(&self, handle: &Handle<T>) -> Lease<T> {
let id = handle.id;
- let entity = self
- .entities
- .lock()
- .remove(id)
- .expect("Circular entity lease. Is the entity already being updated?")
- .downcast::<T>()
- .unwrap();
+ let entity = Some(
+ self.entities
+ .lock()
+ .remove(id)
+ .expect("Circular entity lease. Is the entity already being updated?")
+ .downcast::<T>()
+ .unwrap(),
+ );
Lease { id, entity }
}
- pub fn end_lease<T: 'static + Send + Sync>(&mut self, lease: Lease<T>) {
- self.entities.lock().insert(lease.id, lease.entity);
+ /// Return an entity after moving it to the stack.
+ pub fn end_lease<T: 'static + Send + Sync>(&mut self, mut lease: Lease<T>) {
+ self.entities
+ .lock()
+ .insert(lease.id, lease.entity.take().unwrap());
}
pub fn weak_handle<T: 'static + Send + Sync>(&self, id: EntityId) -> WeakHandle<T> {
@@ -71,6 +70,34 @@ impl EntityMap {
}
}
+pub struct Lease<T> {
+ entity: Option<Box<T>>,
+ pub id: EntityId,
+}
+
+impl<T> core::ops::Deref for Lease<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ self.entity.as_ref().unwrap()
+ }
+}
+
+impl<T> core::ops::DerefMut for Lease<T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.entity.as_mut().unwrap()
+ }
+}
+
+impl<T> Drop for Lease<T> {
+ fn drop(&mut self) {
+ assert!(
+ self.entity.is_none(),
+ "Leases must be ended with EntityMap::end_lease"
+ );
+ }
+}
+
#[derive(Deref, DerefMut)]
pub struct Slot<T: Send + Sync + 'static>(Handle<T>);
@@ -1,5 +1,6 @@
use crate::{
- Element, Layout, LayoutId, Result, SharedString, Style, StyleHelpers, Styled, ViewContext,
+ BorrowWindow, Element, Layout, LayoutId, Result, SharedString, Style, StyleHelpers, Styled,
+ ViewContext,
};
use futures::FutureExt;
use refineable::RefinementCascade;
@@ -34,7 +35,7 @@ impl<S> Img<S> {
}
}
-impl<S: 'static> Element for Img<S> {
+impl<S: Send + Sync + 'static> Element for Img<S> {
type State = S;
type FrameState = ();
@@ -64,22 +65,25 @@ impl<S: 'static> Element for Img<S> {
style.paint(order, bounds, cx);
- if let Some(uri) = &self.uri {
- let image_future = cx.image_cache.get(uri.clone());
+ if let Some(uri) = self.uri.clone() {
+ let image_future = cx.image_cache.get(uri);
if let Some(data) = image_future
.clone()
.now_or_never()
.and_then(ResultExt::log_err)
{
- cx.paint_image(bounds, order, data, self.grayscale)?;
+ let corner_radii = style.corner_radii.to_pixels(bounds, cx.rem_size());
+ cx.paint_image(bounds, corner_radii, order, data, self.grayscale)?;
} else {
- log::warn!("image not loaded yet");
- // cx.spawn(|this, mut cx| async move {
- // if image_future.await.log_err().is_some() {
- // this.update(&mut cx, |_, cx| cx.notify()).ok();
- // }
- // })
- // .detach();
+ cx.spawn(|view, mut cx| async move {
+ if image_future.await.log_err().is_some() {
+ view.update(&mut cx, |_, cx| {
+ cx.notify();
+ })
+ .ok();
+ }
+ })
+ .detach()
}
}
Ok(())
@@ -1,1095 +1,112 @@
-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,
-{
- spawn_on_main(dispatcher, move || async move { func() })
}
-/// 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,
- }
+impl Executor {
+ pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
+ Self { dispatcher }
}
- pub fn num_cpus(&self) -> usize {
- num_cpus::get()
- }
-
- 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> + Send + '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(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()
-}
@@ -1,7 +1,10 @@
use core::fmt::Debug;
use derive_more::{Add, AddAssign, Div, Mul, Sub, SubAssign};
use refineable::Refineable;
-use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign};
+use std::{
+ cmp,
+ ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign},
+};
#[derive(Refineable, Default, Add, AddAssign, Sub, SubAssign, Copy, Debug, PartialEq, Eq, Hash)]
#[refineable(debug)]
@@ -84,7 +87,7 @@ impl<T: Clone + Debug + Div<S, Output = T>, S: Clone> Div<S> for Point<T> {
}
}
-impl<T: Clone + Debug + std::cmp::PartialOrd> Point<T> {
+impl<T: Clone + Debug + cmp::PartialOrd> Point<T> {
pub fn max(&self, other: &Self) -> Self {
Point {
x: if self.x >= other.x {
@@ -99,6 +102,21 @@ impl<T: Clone + Debug + std::cmp::PartialOrd> Point<T> {
},
}
}
+
+ pub fn min(&self, other: &Self) -> Self {
+ Point {
+ x: if self.x <= other.x {
+ self.x.clone()
+ } else {
+ other.x.clone()
+ },
+ y: if self.y <= other.y {
+ self.y.clone()
+ } else {
+ other.y.clone()
+ },
+ }
+ }
}
impl<T: Clone + Debug> Clone for Point<T> {
@@ -239,6 +257,14 @@ impl<T: Clone + Debug + Sub<Output = T>> Bounds<T> {
}
}
+impl<T: Clone + Debug + PartialOrd + Add<T, Output = T> + Sub<Output = T>> Bounds<T> {
+ pub fn intersect(&self, other: &Self) -> Self {
+ let upper_left = self.origin.max(&other.origin);
+ let lower_right = self.lower_right().min(&other.lower_right());
+ Self::from_corners(upper_left, lower_right)
+ }
+}
+
impl<T, Rhs> Mul<Rhs> for Bounds<T>
where
T: Mul<Rhs, Output = Rhs> + Clone + Debug,
@@ -433,12 +459,13 @@ pub struct Corners<T: Clone + Debug> {
}
impl Corners<AbsoluteLength> {
- pub fn to_pixels(&self, rem_size: Pixels) -> Corners<Pixels> {
+ pub fn to_pixels(&self, bounds: Bounds<Pixels>, rem_size: Pixels) -> Corners<Pixels> {
+ let max = bounds.size.width.max(bounds.size.height) / 2.;
Corners {
- top_left: self.top_left.to_pixels(rem_size),
- top_right: self.top_right.to_pixels(rem_size),
- bottom_right: self.bottom_right.to_pixels(rem_size),
- bottom_left: self.bottom_left.to_pixels(rem_size),
+ top_left: self.top_left.to_pixels(rem_size).min(max),
+ top_right: self.top_right.to_pixels(rem_size).min(max),
+ bottom_right: self.bottom_right.to_pixels(rem_size).min(max),
+ bottom_left: self.bottom_left.to_pixels(rem_size).min(max),
}
}
}
@@ -536,7 +563,7 @@ impl Mul<Pixels> for Pixels {
impl Eq for Pixels {}
impl Ord for Pixels {
- fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0.partial_cmp(&other.0).unwrap()
}
}
@@ -925,6 +952,15 @@ impl<T: IsZero + Debug + Clone> IsZero for Size<T> {
impl<T: IsZero + Debug + Clone> IsZero for Bounds<T> {
fn is_zero(&self) -> bool {
- self.origin.is_zero() && self.size.is_zero()
+ self.size.is_zero()
+ }
+}
+
+impl<T: IsZero + Debug + Clone> IsZero for Corners<T> {
+ fn is_zero(&self) -> bool {
+ self.top_left.is_zero()
+ && self.top_right.is_zero()
+ && self.bottom_right.is_zero()
+ && self.bottom_left.is_zero()
}
}
@@ -27,8 +27,6 @@ pub use elements::*;
pub use executor::*;
pub use geometry::*;
pub use gpui3_macros::*;
-pub use svg_renderer::*;
-
pub use platform::*;
pub use refineable::*;
pub use scene::*;
@@ -37,12 +35,14 @@ pub use serde_json;
pub use smallvec;
pub use smol::Timer;
use std::{
+ mem,
ops::{Deref, DerefMut},
sync::Arc,
};
pub use style::*;
pub use style_helpers::*;
pub use styled::*;
+pub use svg_renderer::*;
use taffy::TaffyLayoutEngine;
pub use taffy::{AvailableSpace, LayoutId};
pub use text_system::*;
@@ -69,12 +69,6 @@ pub trait Context {
#[repr(transparent)]
pub struct MainThread<T>(T);
-impl<T> MainThread<T> {
- fn new(value: T) -> Self {
- Self(value)
- }
-}
-
impl<T> Deref for MainThread<T> {
type Target = T;
@@ -89,6 +83,42 @@ impl<T> DerefMut for MainThread<T> {
}
}
+impl<C: Context> Context for MainThread<C> {
+ type EntityContext<'a, 'w, T: 'static + Send + Sync> = MainThread<C::EntityContext<'a, 'w, T>>;
+ type Result<T> = C::Result<T>;
+
+ fn entity<T: Send + Sync + 'static>(
+ &mut self,
+ build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
+ ) -> Self::Result<Handle<T>> {
+ self.0.entity(|cx| {
+ let cx = unsafe {
+ mem::transmute::<
+ &mut C::EntityContext<'_, '_, T>,
+ &mut MainThread<C::EntityContext<'_, '_, T>>,
+ >(cx)
+ };
+ build_entity(cx)
+ })
+ }
+
+ fn update_entity<T: Send + Sync + 'static, R>(
+ &mut self,
+ handle: &Handle<T>,
+ update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
+ ) -> Self::Result<R> {
+ self.0.update_entity(handle, |entity, cx| {
+ let cx = unsafe {
+ mem::transmute::<
+ &mut C::EntityContext<'_, '_, T>,
+ &mut MainThread<C::EntityContext<'_, '_, T>>,
+ >(cx)
+ };
+ update(entity, cx)
+ })
+ }
+}
+
pub trait BorrowAppContext {
fn app_mut(&mut self) -> &mut AppContext;
@@ -190,14 +220,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(),
}
}
@@ -206,12 +236,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;
+// }
+// ```
@@ -131,6 +131,8 @@ impl MetalRenderer {
}
pub fn draw(&mut self, scene: &mut Scene) {
+ dbg!("draw scene");
+
let layer = self.layer.clone();
let viewport_size = layer.drawable_size();
let viewport_size: Size<DevicePixels> = size(
@@ -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();
}
}
@@ -106,11 +106,7 @@ fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]],
color = float4(premultiplied_output_rgb, output_alpha);
}
- float clip_distance =
- quad_sdf(input.position.xy, quad.clip_bounds, quad.clip_corner_radii);
- return color *
- float4(1., 1., 1.,
- saturate(0.5 - distance) * saturate(0.5 - clip_distance));
+ return color * float4(1., 1., 1., saturate(0.5 - distance));
}
struct MonochromeSpriteVertexOutput {
@@ -131,8 +127,9 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
float2 unit_vertex = unit_vertices[unit_vertex_id];
MonochromeSprite sprite = sprites[sprite_id];
+ // Don't apply content mask at the vertex level because we don't have time to make sampling from the texture match the mask.
float4 device_position = to_device_position(
- unit_vertex, sprite.bounds, sprite.content_mask.bounds, viewport_size);
+ unit_vertex, sprite.bounds, sprite.bounds, viewport_size);
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
float4 color = hsla_to_rgba(sprite.color);
return MonochromeSpriteVertexOutput{device_position, tile_position, color,
@@ -148,8 +145,11 @@ fragment float4 monochrome_sprite_fragment(
min_filter::linear);
float4 sample =
atlas_texture.sample(atlas_texture_sampler, input.tile_position);
- float clip_distance = quad_sdf(input.position.xy, sprite.content_mask.bounds,
- sprite.content_mask.corner_radii);
+ float clip_distance = quad_sdf(
+ input.position.xy,
+ sprite.content_mask.bounds,
+ Corners_ScaledPixels { 0., 0., 0., 0. }
+ );
float4 color = input.color;
color.a *= sample.a * saturate(0.5 - clip_distance);
return color;
@@ -172,8 +172,9 @@ vertex PolychromeSpriteVertexOutput polychrome_sprite_vertex(
float2 unit_vertex = unit_vertices[unit_vertex_id];
PolychromeSprite sprite = sprites[sprite_id];
+ // Don't apply content mask at the vertex level because we don't have time to make sampling from the texture match the mask.
float4 device_position = to_device_position(
- unit_vertex, sprite.bounds, sprite.content_mask.bounds, viewport_size);
+ unit_vertex, sprite.bounds, sprite.bounds, viewport_size);
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
return PolychromeSpriteVertexOutput{device_position, tile_position,
sprite_id};
@@ -188,8 +189,10 @@ fragment float4 polychrome_sprite_fragment(
min_filter::linear);
float4 sample =
atlas_texture.sample(atlas_texture_sampler, input.tile_position);
- float clip_distance = quad_sdf(input.position.xy, sprite.content_mask.bounds,
- sprite.content_mask.corner_radii);
+ float quad_distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii);
+ float clip_distance = quad_sdf(input.position.xy, sprite.content_mask.bounds, Corners_ScaledPixels { 0., 0., 0., 0. });
+ float distance = max(quad_distance, clip_distance);
+
float4 color = sample;
if (sprite.grayscale) {
float grayscale = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
@@ -197,7 +200,7 @@ fragment float4 polychrome_sprite_fragment(
color.g = grayscale;
color.b = grayscale;
}
- color.a *= saturate(0.5 - clip_distance);
+ color.a *= saturate(0.5 - distance);
return color;
}
@@ -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!()
}
@@ -275,6 +275,7 @@ pub struct PolychromeSprite {
pub order: u32,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ScaledContentMask,
+ pub corner_radii: Corners<ScaledPixels>,
pub tile: AtlasTile,
pub grayscale: bool,
}
@@ -202,7 +202,6 @@ impl Style {
let min = current_mask.bounds.origin;
let max = current_mask.bounds.lower_right();
- let mask_corner_radii = Corners::default();
let mask_bounds = match (
self.overflow.x == Overflow::Visible,
self.overflow.y == Overflow::Visible,
@@ -224,7 +223,6 @@ impl Style {
};
let mask = ContentMask {
bounds: mask_bounds,
- corner_radii: mask_corner_radii,
};
cx.with_content_mask(mask, f)
@@ -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);
@@ -1,15 +1,14 @@
use crate::{
- image_cache::RenderImageParams, px, AnyView, AppContext, AvailableSpace, BorrowAppContext,
- Bounds, Context, Corners, DevicePixels, Effect, Element, EntityId, FontId, GlyphId, Handle,
- Hsla, ImageData, IsZero, LayerId, LayoutId, MainThread, MainThreadOnly, MonochromeSprite,
- Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Reference, RenderGlyphParams,
- RenderSvgParams, ScaledPixels, Scene, SharedString, Size, Style, TaffyLayoutEngine, WeakHandle,
- WindowOptions, SUBPIXEL_VARIANTS,
+ image_cache::RenderImageParams, px, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
+ BorrowAppContext, Bounds, Context, Corners, DevicePixels, Effect, Element, EntityId, FontId,
+ GlyphId, Handle, Hsla, ImageData, IsZero, LayerId, LayoutId, MainThread, MainThreadOnly,
+ MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Reference,
+ RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene, SharedString, Size, Style,
+ TaffyLayoutEngine, Task, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
};
use anyhow::Result;
-use futures::Future;
use smallvec::SmallVec;
-use std::{any::TypeId, borrow::Cow, marker::PhantomData, mem, sync::Arc};
+use std::{any::TypeId, borrow::Cow, future::Future, marker::PhantomData, mem, sync::Arc};
use util::ResultExt;
pub struct AnyWindow {}
@@ -53,8 +52,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,
@@ -76,23 +74,25 @@ impl Window {
#[derive(Clone, Debug)]
pub struct ContentMask {
pub bounds: Bounds<Pixels>,
- pub corner_radii: Corners<Pixels>,
}
impl ContentMask {
pub fn scale(&self, factor: f32) -> ScaledContentMask {
ScaledContentMask {
bounds: self.bounds.scale(factor),
- corner_radii: self.corner_radii.scale(factor),
}
}
+
+ pub fn intersect(&self, other: &Self) -> Self {
+ let bounds = self.bounds.intersect(&other.bounds);
+ ContentMask { bounds }
+ }
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct ScaledContentMask {
bounds: Bounds<ScaledPixels>,
- corner_radii: Corners<ScaledPixels>,
}
pub struct WindowContext<'a, 'w> {
@@ -112,6 +112,43 @@ impl<'a, 'w> WindowContext<'a, 'w> {
self.window.dirty = true;
}
+ pub fn run_on_main<R>(
+ &mut self,
+ f: impl FnOnce(&mut MainThread<WindowContext<'_, '_>>) -> R + Send + 'static,
+ ) -> Task<Result<R>>
+ where
+ R: Send + 'static,
+ {
+ if self.executor.is_main_thread() {
+ Task::ready(Ok(f(unsafe {
+ mem::transmute::<&mut Self, &mut MainThread<Self>>(self)
+ })))
+ } else {
+ let id = self.window.handle.id;
+ self.app.run_on_main(move |cx| cx.update_window(id, f))
+ }
+ }
+
+ pub fn to_async(&self) -> AsyncWindowContext {
+ AsyncWindowContext::new(self.app.to_async(), self.window.handle)
+ }
+
+ pub fn spawn<Fut, R>(
+ &mut self,
+ f: impl FnOnce(AnyWindowHandle, AsyncWindowContext) -> Fut + Send + 'static,
+ ) -> Task<R>
+ where
+ R: Send + 'static,
+ Fut: Future<Output = R> + Send + 'static,
+ {
+ let window = self.window.handle;
+ self.app.spawn(move |app| {
+ let cx = AsyncWindowContext::new(app, window);
+ let future = f(window, cx);
+ async move { future.await }
+ })
+ }
+
pub fn request_layout(
&mut self,
style: Style,
@@ -170,44 +207,10 @@ impl<'a, 'w> WindowContext<'a, 'w> {
result
}
- pub fn clip<F, R>(
- &mut self,
- bounds: Bounds<Pixels>,
- corner_radii: Corners<Pixels>,
- f: impl FnOnce(&mut Self) -> R,
- ) -> R {
- let clip_mask = ContentMask {
- bounds,
- corner_radii,
- };
-
- self.window.content_mask_stack.push(clip_mask);
- let result = f(self);
- self.window.content_mask_stack.pop();
- result
- }
-
pub fn current_layer_id(&self) -> LayerId {
self.window.current_layer_id.clone()
}
- pub fn run_on_main<R>(
- &self,
- f: impl FnOnce(&mut MainThread<WindowContext>) -> R + Send + 'static,
- ) -> impl Future<Output = Result<R>>
- where
- R: Send + 'static,
- {
- let id = self.window.handle.id;
- self.app.run_on_main(move |cx| {
- cx.update_window(id, |cx| {
- f(unsafe {
- mem::transmute::<&mut WindowContext, &mut MainThread<WindowContext>>(cx)
- })
- })
- })
- }
-
pub fn paint_glyph(
&mut self,
origin: Point<Pixels>,
@@ -303,6 +306,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
PolychromeSprite {
order,
bounds,
+ corner_radii: Default::default(),
content_mask,
tile,
grayscale: false,
@@ -356,6 +360,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
pub fn paint_image(
&mut self,
bounds: Bounds<Pixels>,
+ corner_radii: Corners<Pixels>,
order: u32,
data: Arc<ImageData>,
grayscale: bool,
@@ -372,6 +377,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
Ok((data.size(), Cow::Borrowed(data.as_bytes())))
})?;
let content_mask = self.content_mask().scale(scale_factor);
+ let corner_radii = corner_radii.scale(scale_factor);
self.window.scene.insert(
layer_id,
@@ -379,6 +385,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
order,
bounds,
content_mask,
+ corner_radii,
tile,
grayscale,
},
@@ -389,7 +396,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
pub(crate) fn draw(&mut self) -> Result<()> {
let unit_entity = self.unit_entity.clone();
- self.update_entity(&unit_entity, |_, cx| {
+ self.update_entity(&unit_entity, |view, cx| {
let mut root_view = cx.window.root_view.take().unwrap();
let (root_layout_id, mut frame_state) = root_view.layout(&mut (), cx)?;
let available_space = cx.window.content_size.map(Into::into);
@@ -403,14 +410,15 @@ impl<'a, 'w> WindowContext<'a, 'w> {
cx.window.root_view = Some(root_view);
let scene = cx.window.scene.take();
- let _ = cx.run_on_main(|cx| {
+ cx.run_on_main(view, |_, cx| {
cx.window
.platform_window
.borrow_on_main_thread()
.draw(scene);
- });
+ cx.window.dirty = false;
+ })
+ .detach();
- cx.window.dirty = false;
Ok(())
})
}
@@ -430,7 +438,7 @@ impl Context for WindowContext<'_, '_> {
&mut self.window,
slot.id,
));
- self.entities.redeem(slot, entity)
+ self.entities.insert(slot, entity)
}
fn update_entity<T: Send + Sync + 'static, R>(
@@ -473,6 +481,7 @@ pub trait BorrowWindow: BorrowAppContext {
fn window_mut(&mut self) -> &mut Window;
fn with_content_mask<R>(&mut self, mask: ContentMask, f: impl FnOnce(&mut Self) -> R) -> R {
+ let mask = mask.intersect(&self.content_mask());
self.window_mut().content_mask_stack.push(mask);
let result = f(self);
self.window_mut().content_mask_stack.pop();
@@ -489,7 +498,6 @@ pub trait BorrowWindow: BorrowAppContext {
origin: Point::default(),
size: self.window().content_size,
},
- corner_radii: Default::default(),
})
}
@@ -596,6 +604,38 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
.push_back(Effect::Notify(self.entity_id));
}
+ pub fn run_on_main<R>(
+ &mut self,
+ view: &mut S,
+ f: impl FnOnce(&mut S, &mut MainThread<ViewContext<'_, '_, S>>) -> R + Send + 'static,
+ ) -> Task<Result<R>>
+ where
+ R: Send + 'static,
+ {
+ if self.executor.is_main_thread() {
+ let cx = unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(self) };
+ Task::ready(Ok(f(view, cx)))
+ } else {
+ let handle = self.handle().upgrade(self).unwrap();
+ self.window_cx.run_on_main(move |cx| handle.update(cx, f))
+ }
+ }
+
+ pub fn spawn<Fut, R>(
+ &mut self,
+ f: impl FnOnce(WeakHandle<S>, AsyncWindowContext) -> Fut + Send + 'static,
+ ) -> Task<R>
+ where
+ R: Send + 'static,
+ Fut: Future<Output = R> + Send + 'static,
+ {
+ let handle = self.handle();
+ self.window_cx.spawn(move |_, cx| {
+ let result = f(handle, cx);
+ async move { result.await }
+ })
+ }
+
pub(crate) fn erase_state<R>(&mut self, f: impl FnOnce(&mut ViewContext<()>) -> R) -> R {
let entity_id = self.unit_entity.id;
let mut cx = ViewContext::mutable(