Proof of concept: move the window and app context checks into the executor
This commit is contained in:
358
Cargo.lock
generated
358
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -32,12 +32,12 @@ use util::ResultExt;
|
||||
use crate::{
|
||||
current_platform, hash, init_app_menus, Action, ActionBuildError, ActionRegistry, Any, AnyView,
|
||||
AnyWindowHandle, AppContext, Asset, AssetSource, BackgroundExecutor, Bounds, ClipboardItem,
|
||||
DispatchPhase, DisplayId, EventEmitter, FocusHandle, FocusMap, ForegroundExecutor, Global,
|
||||
KeyBinding, Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels,
|
||||
Platform, PlatformDisplay, Point, PromptBuilder, PromptHandle, PromptLevel, Render,
|
||||
RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString, SubscriberSet,
|
||||
Subscription, SvgRenderer, Task, TextSystem, Window, WindowAppearance, WindowHandle, WindowId,
|
||||
WindowInvalidator,
|
||||
DispatchPhase, DisplayId, EventEmitter, FocusHandle, FocusMap, ForegroundContext,
|
||||
ForegroundExecutor, Global, KeyBinding, Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu,
|
||||
PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, PromptBuilder, PromptHandle,
|
||||
PromptLevel, Render, RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString,
|
||||
SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, Window, WindowAppearance,
|
||||
WindowHandle, WindowId, WindowInvalidator,
|
||||
};
|
||||
|
||||
mod async_context;
|
||||
@@ -1054,7 +1054,9 @@ impl App {
|
||||
Fut: Future<Output = R> + 'static,
|
||||
R: 'static,
|
||||
{
|
||||
self.foreground_executor.spawn(f(self.to_async()))
|
||||
let async_app = self.to_async();
|
||||
self.foreground_executor
|
||||
.spawn_with_context(ForegroundContext::app(&async_app.app), f(async_app))
|
||||
}
|
||||
|
||||
/// Schedules the given function to be run at the end of the current effect cycle, allowing entities
|
||||
@@ -1596,8 +1598,6 @@ impl App {
|
||||
}
|
||||
|
||||
impl AppContext for App {
|
||||
type Result<T> = T;
|
||||
|
||||
/// Build an entity that is owned by the application. The given function will be invoked with
|
||||
/// a `Context` and must return an object representing the entity. A `Entity` handle will be returned,
|
||||
/// which can be used to access the entity in a context.
|
||||
@@ -1621,7 +1621,7 @@ impl AppContext for App {
|
||||
})
|
||||
}
|
||||
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<Reservation<T>> {
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Reservation<T> {
|
||||
Reservation(self.entities.reserve())
|
||||
}
|
||||
|
||||
@@ -1629,7 +1629,7 @@ impl AppContext for App {
|
||||
&mut self,
|
||||
reservation: Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
self.update(|cx| {
|
||||
let slot = reservation.0;
|
||||
let entity = build_entity(&mut Context::new_context(cx, slot.downgrade()));
|
||||
@@ -1655,11 +1655,7 @@ impl AppContext for App {
|
||||
})
|
||||
}
|
||||
|
||||
fn read_entity<T, R>(
|
||||
&self,
|
||||
handle: &Entity<T>,
|
||||
read: impl FnOnce(&T, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
@@ -1704,7 +1700,7 @@ impl AppContext for App {
|
||||
self.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
AnyView, AnyWindowHandle, App, AppCell, AppContext, BackgroundExecutor, BorrowAppContext,
|
||||
Entity, Focusable, ForegroundExecutor, Global, PromptLevel, Render, Reservation, Result, Task,
|
||||
VisualContext, Window, WindowHandle,
|
||||
Entity, Focusable, ForegroundContext, ForegroundExecutor, Global, PromptLevel, Render,
|
||||
Reservation, Result, Task, VisualContext, Window, WindowHandle,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _};
|
||||
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use futures::channel::oneshot;
|
||||
use std::{future::Future, rc::Weak};
|
||||
@@ -21,73 +21,55 @@ pub struct AsyncApp {
|
||||
}
|
||||
|
||||
impl AppContext for AsyncApp {
|
||||
type Result<T> = Result<T>;
|
||||
|
||||
fn new<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
) -> Entity<T> {
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.new(build_entity))
|
||||
app.new(build_entity)
|
||||
}
|
||||
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Result<Reservation<T>> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Reservation<T> {
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.reserve_entity())
|
||||
app.reserve_entity()
|
||||
}
|
||||
|
||||
fn insert_entity<T: 'static>(
|
||||
&mut self,
|
||||
reservation: Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Result<Entity<T>> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
) -> Entity<T> {
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.insert_entity(reservation, build_entity))
|
||||
app.insert_entity(reservation, build_entity)
|
||||
}
|
||||
|
||||
fn update_entity<T: 'static, R>(
|
||||
&mut self,
|
||||
handle: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> Self::Result<R> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
) -> R {
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.update_entity(handle, update))
|
||||
app.update_entity(handle, update)
|
||||
}
|
||||
|
||||
fn read_entity<T, R>(
|
||||
&self,
|
||||
handle: &Entity<T>,
|
||||
callback: impl FnOnce(&T, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<T, R>(&self, handle: &Entity<T>, callback: impl FnOnce(&T, &App) -> R) -> R
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let app = self.app.upgrade().context("app was released")?;
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let lock = app.borrow();
|
||||
Ok(lock.read_entity(handle, callback))
|
||||
lock.read_entity(handle, callback)
|
||||
}
|
||||
|
||||
fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(AnyView, &mut Window, &mut App) -> T,
|
||||
{
|
||||
let app = self.app.upgrade().context("app was released")?;
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let mut lock = app.borrow_mut();
|
||||
lock.update_window(window, f)
|
||||
}
|
||||
@@ -100,7 +82,7 @@ impl AppContext for AsyncApp {
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let app = self.app.upgrade().context("app was released")?;
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let lock = app.borrow();
|
||||
lock.read_window(window, read)
|
||||
}
|
||||
@@ -112,23 +94,20 @@ impl AppContext for AsyncApp {
|
||||
self.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
let app = self.app.upgrade().context("app was released")?;
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let mut lock = app.borrow_mut();
|
||||
Ok(lock.update(|this| this.read_global(callback)))
|
||||
lock.update(|this| this.read_global(callback))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncApp {
|
||||
/// Schedules all windows in the application to be redrawn.
|
||||
pub fn refresh(&self) -> Result<()> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let mut lock = app.borrow_mut();
|
||||
lock.refresh_windows();
|
||||
Ok(())
|
||||
@@ -146,10 +125,7 @@ impl AsyncApp {
|
||||
|
||||
/// Invoke the given function in the context of the app, then flush any effects produced during its invocation.
|
||||
pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> Result<R> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let mut lock = app.borrow_mut();
|
||||
Ok(lock.update(f))
|
||||
}
|
||||
@@ -163,10 +139,7 @@ impl AsyncApp {
|
||||
where
|
||||
V: 'static + Render,
|
||||
{
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let mut lock = app.borrow_mut();
|
||||
lock.open_window(options, build_root_view)
|
||||
}
|
||||
@@ -178,31 +151,26 @@ impl AsyncApp {
|
||||
Fut: Future<Output = R> + 'static,
|
||||
R: 'static,
|
||||
{
|
||||
self.foreground_executor.spawn(f(self.clone()))
|
||||
self.foreground_executor
|
||||
.spawn_with_context(ForegroundContext::app(&self.app), f(self.clone()))
|
||||
}
|
||||
|
||||
/// Determine whether global state of the specified type has been assigned.
|
||||
/// Returns an error if the `App` has been dropped.
|
||||
pub fn has_global<G: Global>(&self) -> Result<bool> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
pub fn has_global<G: Global>(&self) -> bool {
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let app = app.borrow_mut();
|
||||
Ok(app.has_global::<G>())
|
||||
app.has_global::<G>()
|
||||
}
|
||||
|
||||
/// Reads the global state of the specified type, passing it to the given callback.
|
||||
///
|
||||
/// Panics if no global state of the specified type has been assigned.
|
||||
/// Returns an error if the `App` has been dropped.
|
||||
pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> Result<R> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> R {
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let app = app.borrow_mut();
|
||||
Ok(read(app.global(), &app))
|
||||
read(app.global(), &app)
|
||||
}
|
||||
|
||||
/// Reads the global state of the specified type, passing it to the given callback.
|
||||
@@ -223,10 +191,7 @@ impl AsyncApp {
|
||||
&self,
|
||||
update: impl FnOnce(&mut G, &mut App) -> R,
|
||||
) -> Result<R> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let app = self.app.upgrade().unwrap();
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.update(|cx| cx.update_global(update)))
|
||||
}
|
||||
@@ -304,7 +269,10 @@ impl AsyncWindowContext {
|
||||
Fut: Future<Output = R> + 'static,
|
||||
R: 'static,
|
||||
{
|
||||
self.foreground_executor.spawn(f(self.clone()))
|
||||
self.foreground_executor.spawn_with_context(
|
||||
ForegroundContext::window(&self.app.app, self.window.id),
|
||||
f(self.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Present a platform dialog.
|
||||
@@ -326,42 +294,42 @@ impl AsyncWindowContext {
|
||||
}
|
||||
|
||||
impl AppContext for AsyncWindowContext {
|
||||
type Result<T> = Result<T>;
|
||||
|
||||
fn new<T>(&mut self, build_entity: impl FnOnce(&mut Context<'_, T>) -> T) -> Result<Entity<T>>
|
||||
fn new<T>(&mut self, build_entity: impl FnOnce(&mut Context<'_, T>) -> T) -> Entity<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.window.update(self, |_, _, cx| cx.new(build_entity))
|
||||
self.window
|
||||
.update(self, |_, _, cx| cx.new(build_entity))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Result<Reservation<T>> {
|
||||
self.window.update(self, |_, _, cx| cx.reserve_entity())
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Reservation<T> {
|
||||
self.window
|
||||
.update(self, |_, _, cx| cx.reserve_entity())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn insert_entity<T: 'static>(
|
||||
&mut self,
|
||||
reservation: Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
self.window
|
||||
.update(self, |_, _, cx| cx.insert_entity(reservation, build_entity))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn update_entity<T: 'static, R>(
|
||||
&mut self,
|
||||
handle: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> Result<R> {
|
||||
) -> R {
|
||||
self.window
|
||||
.update(self, |_, _, cx| cx.update_entity(handle, update))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn read_entity<T, R>(
|
||||
&self,
|
||||
handle: &Entity<T>,
|
||||
read: impl FnOnce(&T, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
@@ -393,7 +361,7 @@ impl AppContext for AsyncWindowContext {
|
||||
self.app.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
@@ -409,38 +377,44 @@ impl VisualContext for AsyncWindowContext {
|
||||
fn new_window_entity<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Window, &mut Context<T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
self.window
|
||||
.update(self, |_, window, cx| cx.new(|cx| build_entity(window, cx)))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn update_window_entity<T: 'static, R>(
|
||||
&mut self,
|
||||
view: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
|
||||
) -> Self::Result<R> {
|
||||
self.window.update(self, |_, window, cx| {
|
||||
view.update(cx, |entity, cx| update(entity, window, cx))
|
||||
})
|
||||
) -> R {
|
||||
self.window
|
||||
.update(self, |_, window, cx| {
|
||||
view.update(cx, |entity, cx| update(entity, window, cx))
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn replace_root_view<V>(
|
||||
&mut self,
|
||||
build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
|
||||
) -> Self::Result<Entity<V>>
|
||||
) -> Entity<V>
|
||||
where
|
||||
V: 'static + Render,
|
||||
{
|
||||
self.window
|
||||
.update(self, |_, window, cx| window.replace_root(cx, build_view))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn focus<V>(&mut self, view: &Entity<V>) -> Self::Result<()>
|
||||
fn focus<V>(&mut self, view: &Entity<V>)
|
||||
where
|
||||
V: Focusable,
|
||||
{
|
||||
self.window.update(self, |_, window, cx| {
|
||||
view.read(cx).focus_handle(cx).clone().focus(window);
|
||||
})
|
||||
self.window
|
||||
.update(self, |_, window, cx| {
|
||||
view.read(cx).focus_handle(cx).clone().focus(window);
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,9 +679,7 @@ impl<T> Context<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AppContext for Context<'_, T> {
|
||||
type Result<U> = U;
|
||||
|
||||
impl<T> AppContext for Context<T> {
|
||||
fn new<U: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Context<'_, U>) -> U,
|
||||
@@ -697,7 +695,7 @@ impl<T> AppContext for Context<'_, T> {
|
||||
&mut self,
|
||||
reservation: Reservation<U>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, U>) -> U,
|
||||
) -> Self::Result<Entity<U>> {
|
||||
) -> Entity<U> {
|
||||
self.app.insert_entity(reservation, build_entity)
|
||||
}
|
||||
|
||||
@@ -709,11 +707,7 @@ impl<T> AppContext for Context<'_, T> {
|
||||
self.app.update_entity(handle, update)
|
||||
}
|
||||
|
||||
fn read_entity<U, R>(
|
||||
&self,
|
||||
handle: &Entity<U>,
|
||||
read: impl FnOnce(&U, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<U, R>(&self, handle: &Entity<U>, read: impl FnOnce(&U, &App) -> R) -> R
|
||||
where
|
||||
U: 'static,
|
||||
{
|
||||
@@ -745,7 +739,7 @@ impl<T> AppContext for Context<'_, T> {
|
||||
self.app.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
|
||||
@@ -418,11 +418,7 @@ impl<T: 'static> Entity<T> {
|
||||
}
|
||||
|
||||
/// Read the entity referenced by this handle with the given function.
|
||||
pub fn read_with<R, C: AppContext>(
|
||||
&self,
|
||||
cx: &C,
|
||||
f: impl FnOnce(&T, &App) -> R,
|
||||
) -> C::Result<R> {
|
||||
pub fn read_with<R, C: AppContext>(&self, cx: &C, f: impl FnOnce(&T, &App) -> R) -> R {
|
||||
cx.read_entity(self, f)
|
||||
}
|
||||
|
||||
@@ -435,7 +431,7 @@ impl<T: 'static> Entity<T> {
|
||||
&self,
|
||||
cx: &mut C,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> C::Result<R>
|
||||
) -> R
|
||||
where
|
||||
C: AppContext,
|
||||
{
|
||||
@@ -449,7 +445,7 @@ impl<T: 'static> Entity<T> {
|
||||
&self,
|
||||
cx: &mut C,
|
||||
update: impl FnOnce(&mut T, &mut Window, &mut Context<'_, T>) -> R,
|
||||
) -> C::Result<R>
|
||||
) -> R
|
||||
where
|
||||
C: VisualContext,
|
||||
{
|
||||
@@ -646,13 +642,10 @@ impl<T: 'static> WeakEntity<T> {
|
||||
) -> Result<R>
|
||||
where
|
||||
C: AppContext,
|
||||
Result<C::Result<R>>: crate::Flatten<R>,
|
||||
{
|
||||
crate::Flatten::flatten(
|
||||
self.upgrade()
|
||||
.ok_or_else(|| anyhow!("entity released"))
|
||||
.map(|this| cx.update_entity(&this, update)),
|
||||
)
|
||||
self.upgrade()
|
||||
.ok_or_else(|| anyhow!("entity released"))
|
||||
.map(|this| cx.update_entity(&this, update))
|
||||
}
|
||||
|
||||
/// Updates the entity referenced by this handle with the given function if
|
||||
@@ -665,14 +658,13 @@ impl<T: 'static> WeakEntity<T> {
|
||||
) -> Result<R>
|
||||
where
|
||||
C: VisualContext,
|
||||
Result<C::Result<R>>: crate::Flatten<R>,
|
||||
{
|
||||
let window = cx.window_handle();
|
||||
let this = self.upgrade().ok_or_else(|| anyhow!("entity released"))?;
|
||||
|
||||
crate::Flatten::flatten(window.update(cx, |_, window, cx| {
|
||||
window.update(cx, |_, window, cx| {
|
||||
this.update(cx, |entity, cx| update(entity, window, cx))
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads the entity referenced by this handle with the given function if
|
||||
@@ -681,13 +673,10 @@ impl<T: 'static> WeakEntity<T> {
|
||||
pub fn read_with<C, R>(&self, cx: &C, read: impl FnOnce(&T, &App) -> R) -> Result<R>
|
||||
where
|
||||
C: AppContext,
|
||||
Result<C::Result<R>>: crate::Flatten<R>,
|
||||
{
|
||||
crate::Flatten::flatten(
|
||||
self.upgrade()
|
||||
.ok_or_else(|| anyhow!("entity release"))
|
||||
.map(|this| cx.read_entity(&this, read)),
|
||||
)
|
||||
self.upgrade()
|
||||
.ok_or_else(|| anyhow!("entity release"))
|
||||
.map(|this| cx.read_entity(&this, read))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
Action, AnyView, AnyWindowHandle, App, AppCell, AppContext, AsyncApp, AvailableSpace,
|
||||
BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, DrawPhase, Drawable, Element,
|
||||
Empty, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Modifiers,
|
||||
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
|
||||
Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform,
|
||||
Empty, EventEmitter, ForegroundContext, ForegroundExecutor, Global, InputEvent, Keystroke,
|
||||
Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
|
||||
Pixels, Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform,
|
||||
TestScreenCaptureSource, TestWindow, TextSystem, VisualContext, Window, WindowBounds,
|
||||
WindowHandle, WindowOptions,
|
||||
};
|
||||
@@ -30,17 +30,15 @@ pub struct TestAppContext {
|
||||
}
|
||||
|
||||
impl AppContext for TestAppContext {
|
||||
type Result<T> = T;
|
||||
|
||||
fn new<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
let mut app = self.app.borrow_mut();
|
||||
app.new(build_entity)
|
||||
}
|
||||
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
|
||||
fn reserve_entity<T: 'static>(&mut self) -> crate::Reservation<T> {
|
||||
let mut app = self.app.borrow_mut();
|
||||
app.reserve_entity()
|
||||
}
|
||||
@@ -49,7 +47,7 @@ impl AppContext for TestAppContext {
|
||||
&mut self,
|
||||
reservation: crate::Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
let mut app = self.app.borrow_mut();
|
||||
app.insert_entity(reservation, build_entity)
|
||||
}
|
||||
@@ -58,16 +56,12 @@ impl AppContext for TestAppContext {
|
||||
&mut self,
|
||||
handle: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> Self::Result<R> {
|
||||
) -> R {
|
||||
let mut app = self.app.borrow_mut();
|
||||
app.update_entity(handle, update)
|
||||
}
|
||||
|
||||
fn read_entity<T, R>(
|
||||
&self,
|
||||
handle: &Entity<T>,
|
||||
read: impl FnOnce(&T, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
@@ -102,7 +96,7 @@ impl AppContext for TestAppContext {
|
||||
self.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
@@ -329,7 +323,10 @@ impl TestAppContext {
|
||||
Fut: Future<Output = R> + 'static,
|
||||
R: 'static,
|
||||
{
|
||||
self.foreground_executor.spawn(f(self.to_async()))
|
||||
self.foreground_executor.spawn_with_context(
|
||||
ForegroundContext::app(&Rc::downgrade(&self.app)),
|
||||
f(self.to_async()),
|
||||
)
|
||||
}
|
||||
|
||||
/// true if the given global is defined
|
||||
@@ -868,16 +865,14 @@ impl VisualTestContext {
|
||||
}
|
||||
|
||||
impl AppContext for VisualTestContext {
|
||||
type Result<T> = <TestAppContext as AppContext>::Result<T>;
|
||||
|
||||
fn new<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
self.cx.new(build_entity)
|
||||
}
|
||||
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
|
||||
fn reserve_entity<T: 'static>(&mut self) -> crate::Reservation<T> {
|
||||
self.cx.reserve_entity()
|
||||
}
|
||||
|
||||
@@ -885,7 +880,7 @@ impl AppContext for VisualTestContext {
|
||||
&mut self,
|
||||
reservation: crate::Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
self.cx.insert_entity(reservation, build_entity)
|
||||
}
|
||||
|
||||
@@ -893,18 +888,14 @@ impl AppContext for VisualTestContext {
|
||||
&mut self,
|
||||
handle: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> Self::Result<R>
|
||||
) -> R
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.cx.update_entity(handle, update)
|
||||
}
|
||||
|
||||
fn read_entity<T, R>(
|
||||
&self,
|
||||
handle: &Entity<T>,
|
||||
read: impl FnOnce(&T, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
@@ -936,7 +927,7 @@ impl AppContext for VisualTestContext {
|
||||
self.cx.background_spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
@@ -953,7 +944,7 @@ impl VisualContext for VisualTestContext {
|
||||
fn new_window_entity<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Window, &mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
self.window
|
||||
.update(&mut self.cx, |_, window, cx| {
|
||||
cx.new(|cx| build_entity(window, cx))
|
||||
@@ -965,7 +956,7 @@ impl VisualContext for VisualTestContext {
|
||||
&mut self,
|
||||
view: &Entity<V>,
|
||||
update: impl FnOnce(&mut V, &mut Window, &mut Context<V>) -> R,
|
||||
) -> Self::Result<R> {
|
||||
) -> R {
|
||||
self.window
|
||||
.update(&mut self.cx, |_, window, cx| {
|
||||
view.update(cx, |v, cx| update(v, window, cx))
|
||||
@@ -976,7 +967,7 @@ impl VisualContext for VisualTestContext {
|
||||
fn replace_root_view<V>(
|
||||
&mut self,
|
||||
build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
|
||||
) -> Self::Result<Entity<V>>
|
||||
) -> Entity<V>
|
||||
where
|
||||
V: 'static + Render,
|
||||
{
|
||||
@@ -987,7 +978,7 @@ impl VisualContext for VisualTestContext {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn focus<V: crate::Focusable>(&mut self, view: &Entity<V>) -> Self::Result<()> {
|
||||
fn focus<V: crate::Focusable>(&mut self, view: &Entity<V>) {
|
||||
self.window
|
||||
.update(&mut self.cx, |_, window, cx| {
|
||||
view.read(cx).focus_handle(cx).clone().focus(window)
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use crate::{App, PlatformDispatcher};
|
||||
use async_task::Runnable;
|
||||
use crate::{App, ForegroundContext, PlatformDispatcher};
|
||||
use async_task::Builder;
|
||||
use futures::channel::mpsc;
|
||||
use smol::prelude::*;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::panic::Location;
|
||||
use std::thread::{self, ThreadId};
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
marker::PhantomData,
|
||||
@@ -17,6 +14,7 @@ use std::{
|
||||
Arc,
|
||||
},
|
||||
task::{Context, Poll},
|
||||
thread::ThreadId,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use util::TryFutureExt;
|
||||
@@ -44,6 +42,7 @@ pub struct BackgroundExecutor {
|
||||
pub struct ForegroundExecutor {
|
||||
#[doc(hidden)]
|
||||
pub dispatcher: Arc<dyn PlatformDispatcher>,
|
||||
|
||||
not_send: PhantomData<Rc<()>>,
|
||||
}
|
||||
|
||||
@@ -64,6 +63,9 @@ enum TaskState<T> {
|
||||
|
||||
/// A task that is currently running.
|
||||
Spawned(async_task::Task<T>),
|
||||
|
||||
/// A task that is currently running on the foreground
|
||||
SpawnedOnForeground(async_task::Task<T, ForegroundContext>),
|
||||
}
|
||||
|
||||
impl<T> Task<T> {
|
||||
@@ -77,6 +79,7 @@ impl<T> Task<T> {
|
||||
match self {
|
||||
Task(TaskState::Ready(_)) => {}
|
||||
Task(TaskState::Spawned(task)) => task.detach(),
|
||||
Task(TaskState::SpawnedOnForeground(task)) => task.detach(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +95,7 @@ where
|
||||
pub fn detach_and_log_err(self, cx: &App) {
|
||||
let location = core::panic::Location::caller();
|
||||
cx.foreground_executor()
|
||||
.spawn(self.log_tracked_err(*location))
|
||||
.spawn_with_context(ForegroundContext::none(), self.log_tracked_err(*location))
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
@@ -104,6 +107,7 @@ impl<T> Future for Task<T> {
|
||||
match unsafe { self.get_unchecked_mut() } {
|
||||
Task(TaskState::Ready(val)) => Poll::Ready(val.take().unwrap()),
|
||||
Task(TaskState::Spawned(task)) => task.poll(cx),
|
||||
Task(TaskState::SpawnedOnForeground(task)) => task.poll(cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,8 +131,6 @@ impl TaskLabel {
|
||||
}
|
||||
}
|
||||
|
||||
type AnyLocalFuture<R> = Pin<Box<dyn 'static + Future<Output = R>>>;
|
||||
|
||||
type AnyFuture<R> = Pin<Box<dyn 'static + Send + Future<Output = R>>>;
|
||||
|
||||
/// BackgroundExecutor lets you run things on background threads.
|
||||
@@ -441,6 +443,17 @@ impl BackgroundExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
/// std::thread::current() doesn't always reliably return the same ID.
|
||||
/// This does.
|
||||
#[inline]
|
||||
pub(crate) fn thread_id() -> ThreadId {
|
||||
std::thread_local! {
|
||||
static ID: ThreadId = std::thread::current().id();
|
||||
}
|
||||
ID.try_with(|id| *id)
|
||||
.unwrap_or_else(|_| std::thread::current().id())
|
||||
}
|
||||
|
||||
/// ForegroundExecutor runs things on the main thread.
|
||||
impl ForegroundExecutor {
|
||||
/// Creates a new ForegroundExecutor from the given PlatformDispatcher.
|
||||
@@ -457,88 +470,31 @@ impl ForegroundExecutor {
|
||||
where
|
||||
R: 'static,
|
||||
{
|
||||
self.spawn_with_context(ForegroundContext::none(), future)
|
||||
}
|
||||
|
||||
/// Enqueues the given Task to run on the main thread at some point in the future,
|
||||
/// with a context parameter that will be checked before each turn
|
||||
pub fn spawn_with_context<R>(
|
||||
&self,
|
||||
mut context: ForegroundContext,
|
||||
future: impl Future<Output = R> + 'static,
|
||||
) -> Task<R>
|
||||
where
|
||||
R: 'static,
|
||||
{
|
||||
context.spawning_thread = Some(thread_id());
|
||||
let dispatcher = self.dispatcher.clone();
|
||||
|
||||
#[track_caller]
|
||||
fn inner<R: 'static>(
|
||||
dispatcher: Arc<dyn PlatformDispatcher>,
|
||||
future: AnyLocalFuture<R>,
|
||||
) -> Task<R> {
|
||||
let (runnable, task) = spawn_local_with_source_location(future, move |runnable| {
|
||||
dispatcher.dispatch_on_main_thread(runnable)
|
||||
});
|
||||
runnable.schedule();
|
||||
Task(TaskState::Spawned(task))
|
||||
}
|
||||
inner::<R>(dispatcher, Box::pin(future))
|
||||
let (runnable, task) = Builder::new().metadata(context).spawn_local(
|
||||
|_| future,
|
||||
move |runnable| dispatcher.dispatch_on_main_thread(runnable),
|
||||
);
|
||||
runnable.schedule();
|
||||
Task(TaskState::SpawnedOnForeground(task))
|
||||
}
|
||||
}
|
||||
|
||||
/// Variant of `async_task::spawn_local` that includes the source location of the spawn in panics.
|
||||
///
|
||||
/// Copy-modified from:
|
||||
/// https://github.com/smol-rs/async-task/blob/ca9dbe1db9c422fd765847fa91306e30a6bb58a9/src/runnable.rs#L405
|
||||
#[track_caller]
|
||||
fn spawn_local_with_source_location<Fut, S>(
|
||||
future: Fut,
|
||||
schedule: S,
|
||||
) -> (Runnable<()>, async_task::Task<Fut::Output, ()>)
|
||||
where
|
||||
Fut: Future + 'static,
|
||||
Fut::Output: 'static,
|
||||
S: async_task::Schedule<()> + Send + Sync + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn thread_id() -> ThreadId {
|
||||
std::thread_local! {
|
||||
static ID: ThreadId = thread::current().id();
|
||||
}
|
||||
ID.try_with(|id| *id)
|
||||
.unwrap_or_else(|_| thread::current().id())
|
||||
}
|
||||
|
||||
struct Checked<F> {
|
||||
id: ThreadId,
|
||||
inner: ManuallyDrop<F>,
|
||||
location: &'static Location<'static>,
|
||||
}
|
||||
|
||||
impl<F> Drop for Checked<F> {
|
||||
fn drop(&mut self) {
|
||||
assert!(
|
||||
self.id == thread_id(),
|
||||
"local task dropped by a thread that didn't spawn it. Task spawned at {}",
|
||||
self.location
|
||||
);
|
||||
unsafe {
|
||||
ManuallyDrop::drop(&mut self.inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Future> Future for Checked<F> {
|
||||
type Output = F::Output;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
assert!(
|
||||
self.id == thread_id(),
|
||||
"local task polled by a thread that didn't spawn it. Task spawned at {}",
|
||||
self.location
|
||||
);
|
||||
unsafe { self.map_unchecked_mut(|c| &mut *c.inner).poll(cx) }
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap the future into one that checks which thread it's on.
|
||||
let future = Checked {
|
||||
id: thread_id(),
|
||||
inner: ManuallyDrop::new(future),
|
||||
location: Location::caller(),
|
||||
};
|
||||
|
||||
unsafe { async_task::spawn_unchecked(future, schedule) }
|
||||
}
|
||||
|
||||
/// Scope manages a set of tasks that are enqueued and waited on together. See [`BackgroundExecutor::scoped`].
|
||||
pub struct Scope<'a> {
|
||||
executor: BackgroundExecutor,
|
||||
|
||||
@@ -161,19 +161,13 @@ use taffy::TaffyLayoutEngine;
|
||||
/// The context trait, allows the different contexts in GPUI to be used
|
||||
/// interchangeably for certain operations.
|
||||
pub trait AppContext {
|
||||
/// The result type for this context, used for async contexts that
|
||||
/// can't hold a direct reference to the application context.
|
||||
type Result<T>;
|
||||
|
||||
/// Create a new entity in the app context.
|
||||
fn new<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>>;
|
||||
fn new<T: 'static>(&mut self, build_entity: impl FnOnce(&mut Context<'_, T>) -> T)
|
||||
-> Entity<T>;
|
||||
|
||||
/// Reserve a slot for a entity to be inserted later.
|
||||
/// The returned [Reservation] allows you to obtain the [EntityId] for the future entity.
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<Reservation<T>>;
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Reservation<T>;
|
||||
|
||||
/// Insert a new entity in the app context based on a [Reservation] previously obtained from [`reserve_entity`].
|
||||
///
|
||||
@@ -182,23 +176,19 @@ pub trait AppContext {
|
||||
&mut self,
|
||||
reservation: Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>>;
|
||||
) -> Entity<T>;
|
||||
|
||||
/// Update a entity in the app context.
|
||||
fn update_entity<T, R>(
|
||||
&mut self,
|
||||
handle: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> Self::Result<R>
|
||||
) -> R
|
||||
where
|
||||
T: 'static;
|
||||
|
||||
/// Read a entity from the app context.
|
||||
fn read_entity<T, R>(
|
||||
&self,
|
||||
handle: &Entity<T>,
|
||||
read: impl FnOnce(&T, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
|
||||
where
|
||||
T: 'static;
|
||||
|
||||
@@ -222,7 +212,7 @@ pub trait AppContext {
|
||||
R: Send + 'static;
|
||||
|
||||
/// Read a global from this app context
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global;
|
||||
}
|
||||
@@ -249,24 +239,24 @@ pub trait VisualContext: AppContext {
|
||||
&mut self,
|
||||
entity: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
|
||||
) -> Self::Result<R>;
|
||||
) -> R;
|
||||
|
||||
/// Update a view with the given callback
|
||||
fn new_window_entity<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Window, &mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>>;
|
||||
) -> Entity<T>;
|
||||
|
||||
/// Replace the root view of a window with a new view.
|
||||
fn replace_root_view<V>(
|
||||
&mut self,
|
||||
build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
|
||||
) -> Self::Result<Entity<V>>
|
||||
) -> Entity<V>
|
||||
where
|
||||
V: 'static + Render;
|
||||
|
||||
/// Focus a entity in the window, if it implements the [`Focusable`] trait.
|
||||
fn focus<V>(&mut self, entity: &Entity<V>) -> Self::Result<()>
|
||||
fn focus<V>(&mut self, entity: &Entity<V>)
|
||||
where
|
||||
V: Focusable;
|
||||
}
|
||||
|
||||
@@ -27,11 +27,12 @@ mod test;
|
||||
mod windows;
|
||||
|
||||
use crate::{
|
||||
point, Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
|
||||
DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun, ForegroundExecutor,
|
||||
GlyphId, GpuSpecs, ImageSource, Keymap, LineLayout, Pixels, PlatformInput, Point,
|
||||
RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, ScaledPixels, Scene,
|
||||
SharedString, Size, SvgRenderer, SvgSize, Task, TaskLabel, Window, DEFAULT_WINDOW_SIZE,
|
||||
point, thread_id, Action, AnyWindowHandle, App, AppCell, AsyncWindowContext,
|
||||
BackgroundExecutor, Bounds, DevicePixels, DispatchEventResult, Font, FontId, FontMetrics,
|
||||
FontRun, ForegroundExecutor, GlyphId, GpuSpecs, ImageSource, Keymap, LineLayout, Pixels,
|
||||
PlatformInput, Point, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams,
|
||||
ScaledPixels, Scene, SharedString, Size, SvgRenderer, SvgSize, Task, TaskLabel, Window,
|
||||
WindowId, DEFAULT_WINDOW_SIZE,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_task::Runnable;
|
||||
@@ -47,6 +48,9 @@ use std::borrow::Cow;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::Cursor;
|
||||
use std::ops;
|
||||
use std::panic::Location;
|
||||
use std::rc::Weak;
|
||||
use std::thread::ThreadId;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{
|
||||
fmt::{self, Debug},
|
||||
@@ -450,13 +454,104 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Contexts {
|
||||
App {
|
||||
app: Weak<AppCell>,
|
||||
},
|
||||
Window {
|
||||
app: Weak<AppCell>,
|
||||
window: WindowId,
|
||||
},
|
||||
}
|
||||
|
||||
/// TODO
|
||||
#[derive(Debug)]
|
||||
pub struct ForegroundContext {
|
||||
pub(crate) spawning_thread: Option<ThreadId>,
|
||||
location: &'static Location<'static>,
|
||||
inner: Option<Contexts>,
|
||||
}
|
||||
|
||||
// SAFETY: Foreground context will safely panic before accessing non-thread safe data
|
||||
unsafe impl Send for ForegroundContext {}
|
||||
unsafe impl Sync for ForegroundContext {}
|
||||
|
||||
impl ForegroundContext {
|
||||
/// TODO
|
||||
#[track_caller]
|
||||
pub fn app(app: &Weak<AppCell>) -> Self {
|
||||
Self::contexts(Some(Contexts::App { app: app.clone() }))
|
||||
}
|
||||
|
||||
/// TODO
|
||||
#[track_caller]
|
||||
pub fn window(app: &Weak<AppCell>, window: WindowId) -> Self {
|
||||
Self::contexts(Some(Contexts::Window {
|
||||
app: app.clone(),
|
||||
window,
|
||||
}))
|
||||
}
|
||||
|
||||
/// TODO
|
||||
#[track_caller]
|
||||
pub fn none() -> Self {
|
||||
Self::contexts(None)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn contexts(contexts: Option<Contexts>) -> Self {
|
||||
Self {
|
||||
spawning_thread: None,
|
||||
location: core::panic::Location::caller(),
|
||||
inner: contexts,
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_thread_is_valid(&self) {
|
||||
if let Some(spawning_thread) = self.spawning_thread {
|
||||
assert!(
|
||||
spawning_thread == thread_id(),
|
||||
"local task polled by a thread that didn't spawn it. Task spawned at {}",
|
||||
self.location
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO
|
||||
pub fn context_is_valid(&self) -> bool {
|
||||
self.assert_thread_is_valid();
|
||||
|
||||
match &self.inner {
|
||||
Some(Contexts::App { app }) => app.upgrade().is_some(),
|
||||
Some(Contexts::Window { app, window }) => {
|
||||
if let Some(app) = app.upgrade() {
|
||||
let lock = app.borrow();
|
||||
let result = lock.windows.contains_key(*window);
|
||||
drop(lock);
|
||||
result
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ForegroundContext {
|
||||
fn drop(&mut self) {
|
||||
self.assert_thread_is_valid();
|
||||
}
|
||||
}
|
||||
|
||||
/// This type is public so that our test macro can generate and use it, but it should not
|
||||
/// be considered part of our public API.
|
||||
#[doc(hidden)]
|
||||
pub trait PlatformDispatcher: Send + Sync {
|
||||
fn is_main_thread(&self) -> bool;
|
||||
fn dispatch(&self, runnable: Runnable, label: Option<TaskLabel>);
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable);
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable<ForegroundContext>);
|
||||
fn dispatch_after(&self, duration: Duration, runnable: Runnable);
|
||||
fn park(&self, timeout: Option<Duration>) -> bool;
|
||||
fn unparker(&self) -> Unparker;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::{PlatformDispatcher, TaskLabel};
|
||||
use crate::{ForegroundContext, PlatformDispatcher, TaskLabel};
|
||||
use async_task::Runnable;
|
||||
use objc::{
|
||||
class, msg_send,
|
||||
@@ -63,12 +63,12 @@ impl PlatformDispatcher for MacDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable) {
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable<ForegroundContext>) {
|
||||
unsafe {
|
||||
dispatch_async_f(
|
||||
dispatch_get_main_queue(),
|
||||
runnable.into_raw().as_ptr() as *mut c_void,
|
||||
Some(trampoline),
|
||||
Some(context_trampoline),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -105,3 +105,14 @@ extern "C" fn trampoline(runnable: *mut c_void) {
|
||||
let task = unsafe { Runnable::<()>::from_raw(NonNull::new_unchecked(runnable as *mut ())) };
|
||||
task.run();
|
||||
}
|
||||
|
||||
extern "C" fn context_trampoline(runnable: *mut c_void) {
|
||||
let task = unsafe {
|
||||
Runnable::<ForegroundContext>::from_raw(NonNull::new_unchecked(runnable as *mut ()))
|
||||
};
|
||||
if task.metadata().context_is_valid() {
|
||||
task.run();
|
||||
} else {
|
||||
drop(task);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{PlatformDispatcher, TaskLabel};
|
||||
use crate::{platform::ForegroundContext, PlatformDispatcher, TaskLabel};
|
||||
use async_task::Runnable;
|
||||
use backtrace::Backtrace;
|
||||
use collections::{HashMap, HashSet, VecDeque};
|
||||
@@ -26,9 +26,13 @@ pub struct TestDispatcher {
|
||||
unparker: Unparker,
|
||||
}
|
||||
|
||||
// SAFETY: The test dispatcher is only ever run single threaded
|
||||
unsafe impl Send for TestDispatcher {}
|
||||
unsafe impl Sync for TestDispatcher {}
|
||||
|
||||
struct TestDispatcherState {
|
||||
random: StdRng,
|
||||
foreground: HashMap<TestDispatcherId, VecDeque<Runnable>>,
|
||||
foreground: HashMap<TestDispatcherId, VecDeque<Runnable<ForegroundContext>>>,
|
||||
background: Vec<Runnable>,
|
||||
deprioritized_background: Vec<Runnable>,
|
||||
delayed: Vec<(Duration, Runnable)>,
|
||||
@@ -135,7 +139,8 @@ impl TestDispatcher {
|
||||
};
|
||||
let background_len = state.background.len();
|
||||
|
||||
let runnable;
|
||||
let mut background_runnable = None;
|
||||
let mut foreground_runnable = None;
|
||||
let main_thread;
|
||||
if foreground_len == 0 && background_len == 0 {
|
||||
let deprioritized_background_len = state.deprioritized_background.len();
|
||||
@@ -144,7 +149,7 @@ impl TestDispatcher {
|
||||
}
|
||||
let ix = state.random.gen_range(0..deprioritized_background_len);
|
||||
main_thread = false;
|
||||
runnable = state.deprioritized_background.swap_remove(ix);
|
||||
background_runnable = Some(state.deprioritized_background.swap_remove(ix));
|
||||
} else {
|
||||
main_thread = state.random.gen_ratio(
|
||||
foreground_len as u32,
|
||||
@@ -152,24 +157,36 @@ impl TestDispatcher {
|
||||
);
|
||||
if main_thread {
|
||||
let state = &mut *state;
|
||||
runnable = state
|
||||
.foreground
|
||||
.values_mut()
|
||||
.filter(|runnables| !runnables.is_empty())
|
||||
.choose(&mut state.random)
|
||||
.unwrap()
|
||||
.pop_front()
|
||||
.unwrap();
|
||||
foreground_runnable = Some(
|
||||
state
|
||||
.foreground
|
||||
.values_mut()
|
||||
.filter(|runnables| !runnables.is_empty())
|
||||
.choose(&mut state.random)
|
||||
.unwrap()
|
||||
.pop_front()
|
||||
.unwrap(),
|
||||
);
|
||||
} else {
|
||||
let ix = state.random.gen_range(0..background_len);
|
||||
runnable = state.background.swap_remove(ix);
|
||||
background_runnable = Some(state.background.swap_remove(ix));
|
||||
};
|
||||
};
|
||||
|
||||
let was_main_thread = state.is_main_thread;
|
||||
state.is_main_thread = main_thread;
|
||||
drop(state);
|
||||
runnable.run();
|
||||
|
||||
if let Some(background_runnable) = background_runnable {
|
||||
background_runnable.run();
|
||||
} else if let Some(foreground_runnable) = foreground_runnable {
|
||||
if foreground_runnable.metadata().context_is_valid() {
|
||||
foreground_runnable.run();
|
||||
} else {
|
||||
drop(foreground_runnable);
|
||||
}
|
||||
}
|
||||
|
||||
self.state.lock().is_main_thread = was_main_thread;
|
||||
|
||||
true
|
||||
@@ -272,7 +289,7 @@ impl PlatformDispatcher for TestDispatcher {
|
||||
self.unparker.unpark();
|
||||
}
|
||||
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable) {
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable<ForegroundContext>) {
|
||||
self.state
|
||||
.lock()
|
||||
.foreground
|
||||
|
||||
Reference in New Issue
Block a user