Remove lock from element states

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2021-08-27 18:04:21 +02:00
parent d5b7e2d4e3
commit 53dc08dfc5
14 changed files with 143 additions and 104 deletions

View file

@ -28,7 +28,7 @@ impl gpui::View for TextView {
"View"
}
fn render(&self, _: &gpui::RenderContext<Self>) -> gpui::ElementBox {
fn render(&self, _: &mut gpui::RenderContext<Self>) -> gpui::ElementBox {
TextElement.boxed()
}
}

View file

@ -5,13 +5,12 @@ use crate::{
platform::{self, Platform, PromptLevel, WindowOptions},
presenter::Presenter,
util::{post_inc, timeout},
AssetCache, AssetSource, ClipboardItem, EventContext, FontCache, PathPromptOptions,
TextLayoutCache,
AssetCache, AssetSource, ClipboardItem, FontCache, PathPromptOptions, TextLayoutCache,
};
use anyhow::{anyhow, Result};
use async_task::Task;
use keymap::MatchResult;
use parking_lot::{Mutex, RwLock};
use parking_lot::Mutex;
use platform::Event;
use postage::{mpsc, sink::Sink as _, stream::Stream as _};
use smol::prelude::*;
@ -38,7 +37,7 @@ pub trait Entity: 'static {
pub trait View: Entity + Sized {
fn ui_name() -> &'static str;
fn render(&self, cx: &RenderContext<'_, Self>) -> ElementBox;
fn render(&self, cx: &mut RenderContext<'_, Self>) -> ElementBox;
fn on_focus(&mut self, _: &mut ViewContext<Self>) {}
fn on_blur(&mut self, _: &mut ViewContext<Self>) {}
fn keymap_context(&self, _: &AppContext) -> keymap::Context {
@ -681,7 +680,7 @@ impl MutableAppContext {
models: Default::default(),
views: Default::default(),
windows: Default::default(),
values: Default::default(),
element_states: Default::default(),
ref_counts: Arc::new(Mutex::new(RefCounts::default())),
background,
font_cache,
@ -1308,11 +1307,26 @@ impl MutableAppContext {
handle
}
pub fn element_state<Tag: 'static, T: 'static + Default>(
&mut self,
id: usize,
) -> ElementStateHandle<T> {
let key = (TypeId::of::<Tag>(), id);
self.cx
.element_states
.entry(key)
.or_insert_with(|| Box::new(T::default()));
ElementStateHandle::new(TypeId::of::<Tag>(), id, &self.cx.ref_counts)
}
fn remove_dropped_entities(&mut self) {
loop {
let (dropped_models, dropped_views, dropped_values) =
let (dropped_models, dropped_views, dropped_element_states) =
self.cx.ref_counts.lock().take_dropped();
if dropped_models.is_empty() && dropped_views.is_empty() && dropped_values.is_empty() {
if dropped_models.is_empty()
&& dropped_views.is_empty()
&& dropped_element_states.is_empty()
{
break;
}
@ -1346,9 +1360,8 @@ impl MutableAppContext {
}
}
let mut values = self.cx.values.write();
for key in dropped_values {
values.remove(&key);
for key in dropped_element_states {
self.cx.element_states.remove(&key);
}
}
}
@ -1667,7 +1680,7 @@ pub struct AppContext {
models: HashMap<usize, Box<dyn AnyModel>>,
views: HashMap<(usize, usize), Box<dyn AnyView>>,
windows: HashMap<usize, Window>,
values: RwLock<HashMap<(TypeId, usize), Box<dyn Any>>>,
element_states: HashMap<(TypeId, usize), Box<dyn Any>>,
background: Arc<executor::Background>,
ref_counts: Arc<Mutex<RefCounts>>,
font_cache: Arc<FontCache>,
@ -1698,15 +1711,6 @@ impl AppContext {
pub fn platform(&self) -> &Arc<dyn Platform> {
&self.platform
}
pub fn value<Tag: 'static, T: 'static + Default>(&self, id: usize) -> ValueHandle<T> {
let key = (TypeId::of::<Tag>(), id);
self.values
.write()
.entry(key)
.or_insert_with(|| Box::new(T::default()));
ValueHandle::new(TypeId::of::<Tag>(), id, &self.ref_counts)
}
}
impl ReadModel for AppContext {
@ -1875,7 +1879,7 @@ where
) -> ElementBox {
View::render(
self,
&RenderContext {
&mut RenderContext {
window_id,
view_id,
app: cx,
@ -2269,10 +2273,16 @@ impl AsRef<AppContext> for &AppContext {
}
impl<V: View> Deref for RenderContext<'_, V> {
type Target = AppContext;
type Target = MutableAppContext;
fn deref(&self) -> &Self::Target {
&self.app
self.app
}
}
impl<V: View> DerefMut for RenderContext<'_, V> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.app
}
}
@ -2964,16 +2974,16 @@ impl<T> Clone for WeakViewHandle<T> {
}
}
pub struct ValueHandle<T> {
pub struct ElementStateHandle<T> {
value_type: PhantomData<T>,
tag_type_id: TypeId,
id: usize,
ref_counts: Weak<Mutex<RefCounts>>,
}
impl<T: 'static> ValueHandle<T> {
impl<T: 'static> ElementStateHandle<T> {
fn new(tag_type_id: TypeId, id: usize, ref_counts: &Arc<Mutex<RefCounts>>) -> Self {
ref_counts.lock().inc_value(tag_type_id, id);
ref_counts.lock().inc_element_state(tag_type_id, id);
Self {
value_type: PhantomData,
tag_type_id,
@ -2982,41 +2992,39 @@ impl<T: 'static> ValueHandle<T> {
}
}
pub fn read<R>(&self, cx: &AppContext, f: impl FnOnce(&T) -> R) -> R {
f(cx.values
.read()
pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T {
cx.element_states
.get(&(self.tag_type_id, self.id))
.unwrap()
.downcast_ref()
.unwrap())
.unwrap()
}
pub fn update<R>(
&self,
cx: &mut EventContext,
f: impl FnOnce(&mut T, &mut EventContext) -> R,
) -> R {
let mut value = cx
.app
pub fn update<C, R>(&self, cx: &mut C, f: impl FnOnce(&mut T, &mut C) -> R) -> R
where
C: DerefMut<Target = MutableAppContext>,
{
let mut element_state = cx
.deref_mut()
.cx
.values
.write()
.element_states
.remove(&(self.tag_type_id, self.id))
.unwrap();
let result = f(value.downcast_mut().unwrap(), cx);
cx.app
let result = f(element_state.downcast_mut().unwrap(), cx);
cx.deref_mut()
.cx
.values
.write()
.insert((self.tag_type_id, self.id), value);
.element_states
.insert((self.tag_type_id, self.id), element_state);
result
}
}
impl<T> Drop for ValueHandle<T> {
impl<T> Drop for ElementStateHandle<T> {
fn drop(&mut self) {
if let Some(ref_counts) = self.ref_counts.upgrade() {
ref_counts.lock().dec_value(self.tag_type_id, self.id);
ref_counts
.lock()
.dec_element_state(self.tag_type_id, self.id);
}
}
}
@ -3080,10 +3088,10 @@ impl Drop for Subscription {
#[derive(Default)]
struct RefCounts {
entity_counts: HashMap<usize, usize>,
value_counts: HashMap<(TypeId, usize), usize>,
element_state_counts: HashMap<(TypeId, usize), usize>,
dropped_models: HashSet<usize>,
dropped_views: HashSet<(usize, usize)>,
dropped_values: HashSet<(TypeId, usize)>,
dropped_element_states: HashSet<(TypeId, usize)>,
}
impl RefCounts {
@ -3107,8 +3115,11 @@ impl RefCounts {
}
}
fn inc_value(&mut self, tag_type_id: TypeId, id: usize) {
*self.value_counts.entry((tag_type_id, id)).or_insert(0) += 1;
fn inc_element_state(&mut self, tag_type_id: TypeId, id: usize) {
*self
.element_state_counts
.entry((tag_type_id, id))
.or_insert(0) += 1;
}
fn dec_model(&mut self, model_id: usize) {
@ -3129,13 +3140,13 @@ impl RefCounts {
}
}
fn dec_value(&mut self, tag_type_id: TypeId, id: usize) {
fn dec_element_state(&mut self, tag_type_id: TypeId, id: usize) {
let key = (tag_type_id, id);
let count = self.value_counts.get_mut(&key).unwrap();
let count = self.element_state_counts.get_mut(&key).unwrap();
*count -= 1;
if *count == 0 {
self.value_counts.remove(&key);
self.dropped_values.insert(key);
self.element_state_counts.remove(&key);
self.dropped_element_states.insert(key);
}
}
@ -3152,11 +3163,14 @@ impl RefCounts {
) {
let mut dropped_models = HashSet::new();
let mut dropped_views = HashSet::new();
let mut dropped_values = HashSet::new();
let mut dropped_element_states = HashSet::new();
std::mem::swap(&mut self.dropped_models, &mut dropped_models);
std::mem::swap(&mut self.dropped_views, &mut dropped_views);
std::mem::swap(&mut self.dropped_values, &mut dropped_values);
(dropped_models, dropped_views, dropped_values)
std::mem::swap(
&mut self.dropped_element_states,
&mut dropped_element_states,
);
(dropped_models, dropped_views, dropped_element_states)
}
}
@ -3314,7 +3328,7 @@ mod tests {
}
impl super::View for View {
fn render<'a>(&self, _: &RenderContext<Self>) -> ElementBox {
fn render<'a>(&self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().boxed()
}
@ -3378,7 +3392,7 @@ mod tests {
}
impl super::View for View {
fn render<'a>(&self, _: &RenderContext<Self>) -> ElementBox {
fn render<'a>(&self, _: &mut RenderContext<Self>) -> ElementBox {
let mouse_down_count = self.mouse_down_count.clone();
EventHandler::new(Empty::new().boxed())
.on_mouse_down(move |_| {
@ -3440,7 +3454,7 @@ mod tests {
"View"
}
fn render<'a>(&self, _: &RenderContext<Self>) -> ElementBox {
fn render<'a>(&self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().boxed()
}
}
@ -3480,7 +3494,7 @@ mod tests {
}
impl super::View for View {
fn render<'a>(&self, _: &RenderContext<Self>) -> ElementBox {
fn render<'a>(&self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().boxed()
}
@ -3536,7 +3550,7 @@ mod tests {
}
impl super::View for View {
fn render<'a>(&self, _: &RenderContext<Self>) -> ElementBox {
fn render<'a>(&self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().boxed()
}
@ -3586,7 +3600,7 @@ mod tests {
}
impl super::View for View {
fn render<'a>(&self, _: &RenderContext<Self>) -> ElementBox {
fn render<'a>(&self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().boxed()
}
@ -3630,7 +3644,7 @@ mod tests {
}
impl super::View for View {
fn render<'a>(&self, _: &RenderContext<Self>) -> ElementBox {
fn render<'a>(&self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().boxed()
}
@ -3677,7 +3691,7 @@ mod tests {
}
impl super::View for View {
fn render<'a>(&self, _: &RenderContext<Self>) -> ElementBox {
fn render<'a>(&self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().boxed()
}
@ -3735,7 +3749,7 @@ mod tests {
}
impl View for ViewA {
fn render<'a>(&self, _: &RenderContext<Self>) -> ElementBox {
fn render<'a>(&self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().boxed()
}
@ -3753,7 +3767,7 @@ mod tests {
}
impl View for ViewB {
fn render<'a>(&self, _: &RenderContext<Self>) -> ElementBox {
fn render<'a>(&self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().boxed()
}
@ -3849,7 +3863,7 @@ mod tests {
}
impl super::View for View {
fn render<'a>(&self, _: &RenderContext<Self>) -> ElementBox {
fn render<'a>(&self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().boxed()
}
@ -3984,7 +3998,7 @@ mod tests {
"test view"
}
fn render(&self, _: &RenderContext<Self>) -> ElementBox {
fn render(&self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().boxed()
}
}
@ -4029,7 +4043,7 @@ mod tests {
"test view"
}
fn render(&self, _: &RenderContext<Self>) -> ElementBox {
fn render(&self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().boxed()
}
}
@ -4052,7 +4066,7 @@ mod tests {
"test view"
}
fn render(&self, _: &RenderContext<Self>) -> ElementBox {
fn render(&self, _: &mut RenderContext<Self>) -> ElementBox {
Empty::new().boxed()
}
}

View file

@ -496,7 +496,7 @@ mod tests {
"TestView"
}
fn render(&self, _: &RenderContext<'_, Self>) -> ElementBox {
fn render(&self, _: &mut RenderContext<'_, Self>) -> ElementBox {
unimplemented!()
}
}

View file

@ -1,12 +1,14 @@
use std::ops::DerefMut;
use crate::{
geometry::{rect::RectF, vector::Vector2F},
AppContext, DebugContext, Element, ElementBox, Event, EventContext, LayoutContext,
PaintContext, SizeConstraint, ValueHandle,
DebugContext, Element, ElementBox, ElementStateHandle, Event, EventContext, LayoutContext,
MutableAppContext, PaintContext, SizeConstraint,
};
use serde_json::json;
pub struct MouseEventHandler {
state: ValueHandle<MouseState>,
state: ElementStateHandle<MouseState>,
child: ElementBox,
click_handler: Option<Box<dyn FnMut(&mut EventContext)>>,
drag_handler: Option<Box<dyn FnMut(Vector2F, &mut EventContext)>>,
@ -20,14 +22,14 @@ pub struct MouseState {
}
impl MouseEventHandler {
pub fn new<Tag, F>(id: usize, cx: &AppContext, render_child: F) -> Self
pub fn new<Tag, F, C>(id: usize, cx: &mut C, render_child: F) -> Self
where
Tag: 'static,
F: FnOnce(MouseState) -> ElementBox,
F: FnOnce(&MouseState, &mut C) -> ElementBox,
C: DerefMut<Target = MutableAppContext>,
{
let state_handle = cx.value::<Tag, _>(id);
let state = state_handle.read(cx.as_ref(), |state| *state);
let child = render_child(state);
let state_handle = cx.element_state::<Tag, _>(id);
let child = state_handle.update(cx, |state, cx| render_child(state, cx));
Self {
state: state_handle,
child,

View file

@ -11,6 +11,7 @@ use pathfinder_geometry::vector::{vec2f, Vector2F};
use serde_json::json;
use std::{
collections::{HashMap, HashSet},
ops::{Deref, DerefMut},
sync::Arc,
};
@ -269,6 +270,20 @@ impl<'a> EventContext<'a> {
}
}
impl<'a> Deref for EventContext<'a> {
type Target = MutableAppContext;
fn deref(&self) -> &Self::Target {
self.app
}
}
impl<'a> DerefMut for EventContext<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.app
}
}
pub struct DebugContext<'a> {
rendered_views: &'a HashMap<usize, ElementBox>,
pub font_cache: &'a FontCache,

View file

@ -1620,7 +1620,7 @@ mod tests {
"empty view"
}
fn render<'a>(&self, _: &gpui::RenderContext<Self>) -> gpui::ElementBox {
fn render(&self, _: &mut gpui::RenderContext<Self>) -> gpui::ElementBox {
gpui::Element::boxed(gpui::elements::Empty)
}
}

View file

@ -99,7 +99,7 @@ impl ChatPanel {
cx.notify();
}
fn render_channel_name(&self, cx: &RenderContext<Self>) -> ElementBox {
fn render_channel_name(&self, cx: &mut RenderContext<Self>) -> ElementBox {
let settings = self.settings.borrow();
let theme = &settings.theme.chat_panel;
if let Some((channel, _)) = self.active_channel.as_ref() {
@ -121,7 +121,7 @@ impl ChatPanel {
}
}
fn render_active_channel_messages(&self, cx: &RenderContext<Self>) -> ElementBox {
fn render_active_channel_messages(&self, cx: &mut RenderContext<Self>) -> ElementBox {
let messages = if let Some((channel, _)) = self.active_channel.as_ref() {
let channel = channel.read(cx);
let now = OffsetDateTime::now_utc();
@ -202,7 +202,7 @@ impl View for ChatPanel {
"ChatPanel"
}
fn render(&self, cx: &RenderContext<Self>) -> ElementBox {
fn render(&self, cx: &mut RenderContext<Self>) -> ElementBox {
let theme = &self.settings.borrow().theme;
Container::new(
Flex::column()

View file

@ -2522,7 +2522,7 @@ impl Entity for Editor {
}
impl View for Editor {
fn render<'a>(&self, _: &RenderContext<Self>) -> ElementBox {
fn render<'a>(&self, _: &mut RenderContext<Self>) -> ElementBox {
EditorElement::new(self.handle.clone()).boxed()
}

View file

@ -79,7 +79,7 @@ impl View for FileFinder {
"FileFinder"
}
fn render(&self, _: &RenderContext<Self>) -> ElementBox {
fn render(&self, _: &mut RenderContext<Self>) -> ElementBox {
let settings = self.settings.borrow();
Align::new(

View file

@ -13,7 +13,7 @@ impl View for ProjectBrowser {
"ProjectBrowser"
}
fn render(&self, _: &gpui::RenderContext<'_, Self>) -> gpui::ElementBox {
fn render(&self, _: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox {
Empty::new().boxed()
}
}

View file

@ -200,7 +200,7 @@ impl ThemeSelector {
}
}
fn render_matches(&self, cx: &RenderContext<Self>) -> ElementBox {
fn render_matches(&self, cx: &mut RenderContext<Self>) -> ElementBox {
if self.matches.is_empty() {
let settings = self.settings.borrow();
return Container::new(
@ -269,7 +269,7 @@ impl View for ThemeSelector {
"ThemeSelector"
}
fn render(&self, cx: &RenderContext<Self>) -> ElementBox {
fn render(&self, cx: &mut RenderContext<Self>) -> ElementBox {
let settings = self.settings.borrow();
Align::new(

View file

@ -944,7 +944,7 @@ impl View for Workspace {
"Workspace"
}
fn render(&self, cx: &RenderContext<Self>) -> ElementBox {
fn render(&self, cx: &mut RenderContext<Self>) -> ElementBox {
let settings = self.settings.borrow();
Container::new(
Flex::column()

View file

@ -6,8 +6,7 @@ use gpui::{
elements::*,
geometry::{rect::RectF, vector::vec2f},
keymap::Binding,
AppContext, Border, Entity, MutableAppContext, Quad, RenderContext, View, ViewContext,
ViewHandle,
Border, Entity, MutableAppContext, Quad, RenderContext, View, ViewContext, ViewHandle,
};
use postage::watch;
use std::{cmp, path::Path, sync::Arc};
@ -175,7 +174,7 @@ impl Pane {
cx.emit(Event::Split(direction));
}
fn render_tabs(&self, cx: &AppContext) -> ElementBox {
fn render_tabs(&self, cx: &mut RenderContext<Self>) -> ElementBox {
let settings = self.settings.borrow();
let theme = &settings.theme;
let line_height = cx.font_cache().line_height(
@ -194,7 +193,7 @@ impl Pane {
row.add_child(
Expanded::new(
1.0,
MouseEventHandler::new::<Tab, _>(item.id(), cx, |mouse_state| {
MouseEventHandler::new::<Tab, _, _>(item.id(), cx, |mouse_state, cx| {
let title = item.title(cx);
let mut border = border.clone();
@ -299,7 +298,7 @@ impl Pane {
is_dirty: bool,
has_conflict: bool,
theme: &theme::Theme,
cx: &AppContext,
cx: &mut RenderContext<Self>,
) -> ElementBox {
enum TabCloseButton {}
@ -318,7 +317,7 @@ impl Pane {
let close_color = current_color.unwrap_or(theme.workspace.tab.icon_close);
let icon = Svg::new("icons/x.svg").with_color(close_color);
MouseEventHandler::new::<TabCloseButton, _>(item_id, cx, |mouse_state| {
MouseEventHandler::new::<TabCloseButton, _, _>(item_id, cx, |mouse_state, _| {
if mouse_state.hovered {
Container::new(icon.with_color(Color::white()).boxed())
.with_background_color(if mouse_state.clicked {
@ -370,7 +369,7 @@ impl View for Pane {
"Pane"
}
fn render<'a>(&self, cx: &RenderContext<Self>) -> ElementBox {
fn render(&self, cx: &mut RenderContext<Self>) -> ElementBox {
if let Some(active_item) = self.active_item() {
Flex::column()
.with_child(self.render_tabs(cx))

View file

@ -1,5 +1,6 @@
use super::Workspace;
use crate::Settings;
use gpui::{action, elements::*, AnyViewHandle, AppContext};
use gpui::{action, elements::*, AnyViewHandle, MutableAppContext, RenderContext};
use std::{cell::RefCell, rc::Rc};
pub struct Sidebar {
@ -56,7 +57,7 @@ impl Sidebar {
.map(|item| &item.view)
}
pub fn render(&self, settings: &Settings, cx: &AppContext) -> ElementBox {
pub fn render(&self, settings: &Settings, cx: &mut RenderContext<Workspace>) -> ElementBox {
let side = self.side;
let theme = &settings.theme;
let line_height = cx.font_cache().line_height(
@ -73,7 +74,7 @@ impl Sidebar {
&settings.theme.workspace.sidebar_icon
};
enum SidebarButton {}
MouseEventHandler::new::<SidebarButton, _>(item.view.id(), cx, |_| {
MouseEventHandler::new::<SidebarButton, _, _>(item.view.id(), cx, |_, _| {
ConstrainedBox::new(
Align::new(
ConstrainedBox::new(
@ -98,7 +99,11 @@ impl Sidebar {
.boxed()
}
pub fn render_active_item(&self, settings: &Settings, cx: &AppContext) -> Option<ElementBox> {
pub fn render_active_item(
&self,
settings: &Settings,
cx: &mut MutableAppContext,
) -> Option<ElementBox> {
if let Some(active_item) = self.active_item() {
let mut container = Flex::row();
if matches!(self.side, Side::Right) {
@ -118,10 +123,14 @@ impl Sidebar {
}
}
fn render_resize_handle(&self, settings: &Settings, cx: &AppContext) -> ElementBox {
fn render_resize_handle(
&self,
settings: &Settings,
mut cx: &mut MutableAppContext,
) -> ElementBox {
let width = self.width.clone();
let side = self.side;
MouseEventHandler::new::<Self, _>(self.side.id(), cx, |_| {
MouseEventHandler::new::<Self, _, _>(self.side.id(), &mut cx, |_, _| {
Container::new(Empty::new().boxed())
.with_style(&settings.theme.workspace.sidebar.resize_handle)
.boxed()