Panic if element state is used twice in the same frame
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
7b666af0cf
commit
ddc1f237a6
2 changed files with 46 additions and 10 deletions
|
@ -685,6 +685,7 @@ pub struct MutableAppContext {
|
||||||
next_entity_id: usize,
|
next_entity_id: usize,
|
||||||
next_window_id: usize,
|
next_window_id: usize,
|
||||||
next_subscription_id: usize,
|
next_subscription_id: usize,
|
||||||
|
frame_count: usize,
|
||||||
subscriptions: Arc<Mutex<HashMap<usize, BTreeMap<usize, SubscriptionCallback>>>>,
|
subscriptions: Arc<Mutex<HashMap<usize, BTreeMap<usize, SubscriptionCallback>>>>,
|
||||||
observations: Arc<Mutex<HashMap<usize, BTreeMap<usize, ObservationCallback>>>>,
|
observations: Arc<Mutex<HashMap<usize, BTreeMap<usize, ObservationCallback>>>>,
|
||||||
release_observations: Arc<Mutex<HashMap<usize, BTreeMap<usize, ReleaseObservationCallback>>>>,
|
release_observations: Arc<Mutex<HashMap<usize, BTreeMap<usize, ReleaseObservationCallback>>>>,
|
||||||
|
@ -729,6 +730,7 @@ impl MutableAppContext {
|
||||||
next_entity_id: 0,
|
next_entity_id: 0,
|
||||||
next_window_id: 0,
|
next_window_id: 0,
|
||||||
next_subscription_id: 0,
|
next_subscription_id: 0,
|
||||||
|
frame_count: 0,
|
||||||
subscriptions: Default::default(),
|
subscriptions: Default::default(),
|
||||||
observations: Default::default(),
|
observations: Default::default(),
|
||||||
release_observations: Default::default(),
|
release_observations: Default::default(),
|
||||||
|
@ -920,6 +922,7 @@ impl MutableAppContext {
|
||||||
window_id: usize,
|
window_id: usize,
|
||||||
titlebar_height: f32,
|
titlebar_height: f32,
|
||||||
) -> HashMap<usize, ElementBox> {
|
) -> HashMap<usize, ElementBox> {
|
||||||
|
self.start_frame();
|
||||||
let view_ids = self
|
let view_ids = self
|
||||||
.views
|
.views
|
||||||
.keys()
|
.keys()
|
||||||
|
@ -943,6 +946,10 @@ impl MutableAppContext {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn start_frame(&mut self) {
|
||||||
|
self.frame_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update<T, F: FnOnce(&mut Self) -> T>(&mut self, callback: F) -> T {
|
pub fn update<T, F: FnOnce(&mut Self) -> T>(&mut self, callback: F) -> T {
|
||||||
self.pending_flushes += 1;
|
self.pending_flushes += 1;
|
||||||
let result = callback(self);
|
let result = callback(self);
|
||||||
|
@ -1397,7 +1404,12 @@ impl MutableAppContext {
|
||||||
.element_states
|
.element_states
|
||||||
.entry(key)
|
.entry(key)
|
||||||
.or_insert_with(|| Box::new(T::default()));
|
.or_insert_with(|| Box::new(T::default()));
|
||||||
ElementStateHandle::new(TypeId::of::<Tag>(), id, &self.cx.ref_counts)
|
ElementStateHandle::new(
|
||||||
|
TypeId::of::<Tag>(),
|
||||||
|
id,
|
||||||
|
self.frame_count,
|
||||||
|
&self.cx.ref_counts,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_dropped_entities(&mut self) {
|
fn remove_dropped_entities(&mut self) {
|
||||||
|
@ -3368,8 +3380,15 @@ pub struct ElementStateHandle<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: 'static> ElementStateHandle<T> {
|
impl<T: 'static> ElementStateHandle<T> {
|
||||||
fn new(tag_type_id: TypeId, id: ElementStateId, ref_counts: &Arc<Mutex<RefCounts>>) -> Self {
|
fn new(
|
||||||
ref_counts.lock().inc_element_state(tag_type_id, id);
|
tag_type_id: TypeId,
|
||||||
|
id: ElementStateId,
|
||||||
|
frame_id: usize,
|
||||||
|
ref_counts: &Arc<Mutex<RefCounts>>,
|
||||||
|
) -> Self {
|
||||||
|
ref_counts
|
||||||
|
.lock()
|
||||||
|
.inc_element_state(tag_type_id, id, frame_id);
|
||||||
Self {
|
Self {
|
||||||
value_type: PhantomData,
|
value_type: PhantomData,
|
||||||
tag_type_id,
|
tag_type_id,
|
||||||
|
@ -3508,12 +3527,17 @@ impl Drop for Subscription {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct RefCounts {
|
struct RefCounts {
|
||||||
entity_counts: HashMap<usize, usize>,
|
entity_counts: HashMap<usize, usize>,
|
||||||
element_state_counts: HashMap<(TypeId, ElementStateId), usize>,
|
element_state_counts: HashMap<(TypeId, ElementStateId), ElementStateRefCount>,
|
||||||
dropped_models: HashSet<usize>,
|
dropped_models: HashSet<usize>,
|
||||||
dropped_views: HashSet<(usize, usize)>,
|
dropped_views: HashSet<(usize, usize)>,
|
||||||
dropped_element_states: HashSet<(TypeId, ElementStateId)>,
|
dropped_element_states: HashSet<(TypeId, ElementStateId)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ElementStateRefCount {
|
||||||
|
ref_count: usize,
|
||||||
|
frame_id: usize,
|
||||||
|
}
|
||||||
|
|
||||||
impl RefCounts {
|
impl RefCounts {
|
||||||
fn inc_model(&mut self, model_id: usize) {
|
fn inc_model(&mut self, model_id: usize) {
|
||||||
match self.entity_counts.entry(model_id) {
|
match self.entity_counts.entry(model_id) {
|
||||||
|
@ -3537,11 +3561,21 @@ impl RefCounts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inc_element_state(&mut self, tag_type_id: TypeId, id: ElementStateId) {
|
fn inc_element_state(&mut self, tag_type_id: TypeId, id: ElementStateId, frame_id: usize) {
|
||||||
match self.element_state_counts.entry((tag_type_id, id)) {
|
match self.element_state_counts.entry((tag_type_id, id)) {
|
||||||
Entry::Occupied(mut entry) => *entry.get_mut() += 1,
|
Entry::Occupied(mut entry) => {
|
||||||
|
let entry = entry.get_mut();
|
||||||
|
if entry.frame_id == frame_id || entry.ref_count >= 2 {
|
||||||
|
panic!("used the same element state more than once in the same frame");
|
||||||
|
}
|
||||||
|
entry.ref_count += 1;
|
||||||
|
entry.frame_id = frame_id;
|
||||||
|
}
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
entry.insert(1);
|
entry.insert(ElementStateRefCount {
|
||||||
|
ref_count: 1,
|
||||||
|
frame_id,
|
||||||
|
});
|
||||||
self.dropped_element_states.remove(&(tag_type_id, id));
|
self.dropped_element_states.remove(&(tag_type_id, id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3567,9 +3601,9 @@ impl RefCounts {
|
||||||
|
|
||||||
fn dec_element_state(&mut self, tag_type_id: TypeId, id: ElementStateId) {
|
fn dec_element_state(&mut self, tag_type_id: TypeId, id: ElementStateId) {
|
||||||
let key = (tag_type_id, id);
|
let key = (tag_type_id, id);
|
||||||
let count = self.element_state_counts.get_mut(&key).unwrap();
|
let entry = self.element_state_counts.get_mut(&key).unwrap();
|
||||||
*count -= 1;
|
entry.ref_count -= 1;
|
||||||
if *count == 0 {
|
if entry.ref_count == 0 {
|
||||||
self.element_state_counts.remove(&key);
|
self.element_state_counts.remove(&key);
|
||||||
self.dropped_element_states.insert(key);
|
self.dropped_element_states.insert(key);
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@ impl Presenter {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalidate(&mut self, mut invalidation: WindowInvalidation, cx: &mut MutableAppContext) {
|
pub fn invalidate(&mut self, mut invalidation: WindowInvalidation, cx: &mut MutableAppContext) {
|
||||||
|
cx.start_frame();
|
||||||
for view_id in invalidation.removed {
|
for view_id in invalidation.removed {
|
||||||
invalidation.updated.remove(&view_id);
|
invalidation.updated.remove(&view_id);
|
||||||
self.rendered_views.remove(&view_id);
|
self.rendered_views.remove(&view_id);
|
||||||
|
@ -81,6 +82,7 @@ impl Presenter {
|
||||||
invalidation: Option<WindowInvalidation>,
|
invalidation: Option<WindowInvalidation>,
|
||||||
cx: &mut MutableAppContext,
|
cx: &mut MutableAppContext,
|
||||||
) {
|
) {
|
||||||
|
cx.start_frame();
|
||||||
if let Some(invalidation) = invalidation {
|
if let Some(invalidation) = invalidation {
|
||||||
for view_id in invalidation.removed {
|
for view_id in invalidation.removed {
|
||||||
self.rendered_views.remove(&view_id);
|
self.rendered_views.remove(&view_id);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue