mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-11 21:13:02 +00:00
Checkpoint
This commit is contained in:
parent
80c0a6ead3
commit
1f84cdb88c
5 changed files with 167 additions and 38 deletions
|
@ -67,6 +67,7 @@ impl App {
|
|||
pending_effects: Default::default(),
|
||||
observers: SubscriberSet::new(),
|
||||
event_handlers: SubscriberSet::new(),
|
||||
release_handlers: SubscriberSet::new(),
|
||||
layout_id_buffer: Default::default(),
|
||||
})
|
||||
}))
|
||||
|
@ -88,6 +89,7 @@ impl App {
|
|||
|
||||
type Handler = Box<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>;
|
||||
type EventHandler = Box<dyn Fn(&dyn Any, &mut AppContext) -> bool + Send + Sync + 'static>;
|
||||
type ReleaseHandler = Box<dyn Fn(&mut dyn Any, &mut AppContext) + Send + Sync + 'static>;
|
||||
type FrameCallback = Box<dyn FnOnce(&mut WindowContext) + Send>;
|
||||
|
||||
pub struct AppContext {
|
||||
|
@ -107,6 +109,7 @@ pub struct AppContext {
|
|||
pub(crate) pending_effects: VecDeque<Effect>,
|
||||
pub(crate) observers: SubscriberSet<EntityId, Handler>,
|
||||
pub(crate) event_handlers: SubscriberSet<EntityId, EventHandler>,
|
||||
pub(crate) release_handlers: SubscriberSet<EntityId, ReleaseHandler>,
|
||||
pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
|
||||
}
|
||||
|
||||
|
@ -146,10 +149,15 @@ impl AppContext {
|
|||
}
|
||||
|
||||
fn flush_effects(&mut self) {
|
||||
while let Some(effect) = self.pending_effects.pop_front() {
|
||||
match effect {
|
||||
Effect::Notify { emitter } => self.apply_notify_effect(emitter),
|
||||
Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event),
|
||||
loop {
|
||||
self.release_dropped_entities();
|
||||
if let Some(effect) = self.pending_effects.pop_front() {
|
||||
match effect {
|
||||
Effect::Notify { emitter } => self.apply_notify_effect(emitter),
|
||||
Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event),
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,6 +179,23 @@ impl AppContext {
|
|||
}
|
||||
}
|
||||
|
||||
fn release_dropped_entities(&mut self) {
|
||||
loop {
|
||||
let dropped = self.entities.take_dropped();
|
||||
if dropped.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
for (entity_id, mut entity) in dropped {
|
||||
self.observers.remove(&entity_id);
|
||||
self.event_handlers.remove(&entity_id);
|
||||
for release_callback in self.release_handlers.remove(&entity_id) {
|
||||
release_callback(&mut entity, self);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_notify_effect(&mut self, emitter: EntityId) {
|
||||
self.observers
|
||||
.clone()
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use crate::Context;
|
||||
use anyhow::{anyhow, Result};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
|
||||
use slotmap::{SecondaryMap, SlotMap};
|
||||
use std::{
|
||||
any::Any,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||
Arc, Weak,
|
||||
|
@ -14,29 +15,33 @@ use std::{
|
|||
|
||||
slotmap::new_key_type! { pub struct EntityId; }
|
||||
|
||||
pub(crate) struct EntityMap {
|
||||
ref_counts: Arc<RwLock<RefCounts>>,
|
||||
entities: Arc<Mutex<SecondaryMap<EntityId, Box<dyn Any + Send + Sync>>>>,
|
||||
pub(crate) struct EntityMap(Arc<RwLock<EntityMapState>>);
|
||||
|
||||
struct EntityMapState {
|
||||
ref_counts: SlotMap<EntityId, AtomicUsize>,
|
||||
entities: SecondaryMap<EntityId, Box<dyn Any + Send + Sync>>,
|
||||
dropped_entities: Vec<(EntityId, Box<dyn Any + Send + Sync>)>,
|
||||
}
|
||||
|
||||
impl EntityMap {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ref_counts: Arc::new(RwLock::new(SlotMap::with_key())),
|
||||
entities: Arc::new(Mutex::new(SecondaryMap::new())),
|
||||
}
|
||||
Self(Arc::new(RwLock::new(EntityMapState {
|
||||
ref_counts: SlotMap::with_key(),
|
||||
entities: SecondaryMap::new(),
|
||||
dropped_entities: Vec::new(),
|
||||
})))
|
||||
}
|
||||
|
||||
/// 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)))
|
||||
let id = self.0.write().ref_counts.insert(1.into());
|
||||
Slot(Handle::new(id, Arc::downgrade(&self.0)))
|
||||
}
|
||||
|
||||
/// 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));
|
||||
self.0.write().entities.insert(handle.id, Box::new(entity));
|
||||
handle
|
||||
}
|
||||
|
||||
|
@ -44,8 +49,9 @@ impl EntityMap {
|
|||
pub fn lease<T: 'static + Send + Sync>(&self, handle: &Handle<T>) -> Lease<T> {
|
||||
let id = handle.id;
|
||||
let entity = Some(
|
||||
self.entities
|
||||
.lock()
|
||||
self.0
|
||||
.write()
|
||||
.entities
|
||||
.remove(id)
|
||||
.expect("Circular entity lease. Is the entity already being updated?")
|
||||
.downcast::<T>()
|
||||
|
@ -56,8 +62,9 @@ impl EntityMap {
|
|||
|
||||
/// 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()
|
||||
self.0
|
||||
.write()
|
||||
.entities
|
||||
.insert(lease.id, lease.entity.take().unwrap());
|
||||
}
|
||||
|
||||
|
@ -65,9 +72,13 @@ impl EntityMap {
|
|||
WeakHandle {
|
||||
id,
|
||||
entity_type: PhantomData,
|
||||
ref_counts: Arc::downgrade(&self.ref_counts),
|
||||
entity_map: Arc::downgrade(&self.0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_dropped(&self) -> Vec<(EntityId, Box<dyn Any + Send + Sync>)> {
|
||||
mem::take(&mut self.0.write().dropped_entities)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lease<T> {
|
||||
|
@ -104,17 +115,15 @@ pub struct Slot<T: Send + Sync + 'static>(Handle<T>);
|
|||
pub struct Handle<T: Send + Sync> {
|
||||
pub(crate) id: EntityId,
|
||||
entity_type: PhantomData<T>,
|
||||
ref_counts: Weak<RwLock<RefCounts>>,
|
||||
entity_map: Weak<RwLock<EntityMapState>>,
|
||||
}
|
||||
|
||||
type RefCounts = SlotMap<EntityId, AtomicUsize>;
|
||||
|
||||
impl<T: 'static + Send + Sync> Handle<T> {
|
||||
pub fn new(id: EntityId, ref_counts: Weak<RwLock<RefCounts>>) -> Self {
|
||||
fn new(id: EntityId, entity_map: Weak<RwLock<EntityMapState>>) -> Self {
|
||||
Self {
|
||||
id,
|
||||
entity_type: PhantomData,
|
||||
ref_counts,
|
||||
entity_map,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,7 +131,7 @@ impl<T: 'static + Send + Sync> Handle<T> {
|
|||
WeakHandle {
|
||||
id: self.id,
|
||||
entity_type: self.entity_type,
|
||||
ref_counts: self.ref_counts.clone(),
|
||||
entity_map: self.entity_map.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,40 +151,66 @@ impl<T: 'static + Send + Sync> Handle<T> {
|
|||
|
||||
impl<T: Send + Sync> Clone for Handle<T> {
|
||||
fn clone(&self) -> Self {
|
||||
if let Some(entity_map) = self.entity_map.upgrade() {
|
||||
let entity_map = entity_map.read();
|
||||
let count = entity_map
|
||||
.ref_counts
|
||||
.get(self.id)
|
||||
.expect("detected over-release of a handle");
|
||||
let prev_count = count.fetch_add(1, SeqCst);
|
||||
assert_ne!(prev_count, 0, "Detected over-release of a handle.");
|
||||
}
|
||||
|
||||
Self {
|
||||
id: self.id,
|
||||
entity_type: PhantomData,
|
||||
ref_counts: self.ref_counts.clone(),
|
||||
entity_map: self.entity_map.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> Drop for Handle<T> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(_ref_counts) = self.ref_counts.upgrade() {
|
||||
// todo!()
|
||||
// if let Some(count) = ref_counts.read().get(self.id) {
|
||||
// let prev_count = count.fetch_sub(1, SeqCst);
|
||||
// assert_ne!(prev_count, 0, "Detected over-release of a handle.");
|
||||
// }
|
||||
if let Some(entity_map) = self.entity_map.upgrade() {
|
||||
let entity_map = entity_map.upgradable_read();
|
||||
let count = entity_map
|
||||
.ref_counts
|
||||
.get(self.id)
|
||||
.expect("Detected over-release of a handle.");
|
||||
let prev_count = count.fetch_sub(1, SeqCst);
|
||||
assert_ne!(prev_count, 0, "Detected over-release of a handle.");
|
||||
if prev_count == 1 {
|
||||
// We were the last reference to this entity, so we can remove it.
|
||||
let mut entity_map = RwLockUpgradableReadGuard::upgrade(entity_map);
|
||||
let entity = entity_map
|
||||
.entities
|
||||
.remove(self.id)
|
||||
.expect("entity was removed twice");
|
||||
entity_map.ref_counts.remove(self.id);
|
||||
entity_map.dropped_entities.push((self.id, entity));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WeakHandle<T> {
|
||||
pub(crate) id: EntityId,
|
||||
pub(crate) entity_type: PhantomData<T>,
|
||||
pub(crate) ref_counts: Weak<RwLock<RefCounts>>,
|
||||
entity_type: PhantomData<T>,
|
||||
entity_map: Weak<RwLock<EntityMapState>>,
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> WeakHandle<T> {
|
||||
pub fn upgrade(&self, _: &impl Context) -> Option<Handle<T>> {
|
||||
let ref_counts = self.ref_counts.upgrade()?;
|
||||
ref_counts.read().get(self.id).unwrap().fetch_add(1, SeqCst);
|
||||
let entity_map = &self.entity_map.upgrade()?;
|
||||
entity_map
|
||||
.read()
|
||||
.ref_counts
|
||||
.get(self.id)?
|
||||
.fetch_add(1, SeqCst);
|
||||
Some(Handle {
|
||||
id: self.id,
|
||||
entity_type: self.entity_type,
|
||||
ref_counts: self.ref_counts.clone(),
|
||||
entity_map: self.entity_map.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,36 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn on_release(
|
||||
&mut self,
|
||||
on_release: impl Fn(&mut T, &mut AppContext) + Send + Sync + 'static,
|
||||
) -> Subscription {
|
||||
self.app.release_handlers.insert(
|
||||
self.entity_id,
|
||||
Box::new(move |this, cx| {
|
||||
let this = this.downcast_mut().expect("invalid entity type");
|
||||
on_release(this, cx);
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn observe_release<E: Send + Sync + 'static>(
|
||||
&mut self,
|
||||
handle: &Handle<E>,
|
||||
on_release: impl Fn(&mut T, &mut E, &mut ModelContext<'_, T>) + Send + Sync + 'static,
|
||||
) -> Subscription {
|
||||
let this = self.handle();
|
||||
self.app.release_handlers.insert(
|
||||
handle.id,
|
||||
Box::new(move |entity, cx| {
|
||||
let entity = entity.downcast_mut().expect("invalid entity type");
|
||||
if let Some(this) = this.upgrade(cx) {
|
||||
this.update(cx, |this, cx| on_release(this, entity, cx));
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn notify(&mut self) {
|
||||
self.app.pending_effects.push_back(Effect::Notify {
|
||||
emitter: self.entity_id,
|
||||
|
|
|
@ -59,6 +59,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn remove(&self, emitter: &EmitterKey) -> impl IntoIterator<Item = Callback> {
|
||||
let subscribers = self.0.lock().subscribers.remove(&emitter);
|
||||
subscribers.unwrap_or_default().into_values()
|
||||
}
|
||||
|
||||
pub fn retain<F>(&self, emitter: &EmitterKey, mut f: F)
|
||||
where
|
||||
F: FnMut(&mut Callback) -> bool,
|
||||
|
|
|
@ -961,6 +961,40 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn on_release(
|
||||
&mut self,
|
||||
on_release: impl Fn(&mut S, &mut WindowContext) + Send + Sync + 'static,
|
||||
) -> Subscription {
|
||||
let window_handle = self.window.handle;
|
||||
self.app.release_handlers.insert(
|
||||
self.entity_id,
|
||||
Box::new(move |this, cx| {
|
||||
let this = this.downcast_mut().expect("invalid entity type");
|
||||
// todo!("are we okay with silently swallowing the error?")
|
||||
let _ = cx.update_window(window_handle.id, |cx| on_release(this, cx));
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn observe_release<E: Send + Sync + 'static>(
|
||||
&mut self,
|
||||
handle: &Handle<E>,
|
||||
on_release: impl Fn(&mut S, &mut E, &mut ViewContext<'_, '_, S>) + Send + Sync + 'static,
|
||||
) -> Subscription {
|
||||
let this = self.handle();
|
||||
let window_handle = self.window.handle;
|
||||
self.app.release_handlers.insert(
|
||||
handle.id,
|
||||
Box::new(move |entity, cx| {
|
||||
let entity = entity.downcast_mut().expect("invalid entity type");
|
||||
// todo!("are we okay with silently swallowing the error?")
|
||||
let _ = cx.update_window(window_handle.id, |cx| {
|
||||
this.update(cx, |this, cx| on_release(this, entity, cx))
|
||||
});
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn notify(&mut self) {
|
||||
self.window_cx.notify();
|
||||
self.window_cx
|
||||
|
|
Loading…
Reference in a new issue