1use std::{
2 cell::{BorrowMutError, Cell, RefCell, RefMut},
3 ops::{Deref, DerefMut},
4 rc::Rc,
5};
6
7use crate::{
8 Applier, NodeError, NodeId, SlotsHost,
9 slot::{DetachedSubtree, SlotLifecycleCoordinator, dispose_detached_node_now},
10};
11
12#[derive(Default)]
14pub struct NodeDisposal {
15 nodes: Vec<(NodeId, u32)>,
16 lifecycle: SlotLifecycleCoordinator,
17 slot_hosts: Vec<Rc<SlotsHost>>,
18}
19
20impl NodeDisposal {
21 pub fn new(nodes: Vec<(NodeId, u32)>) -> Self {
32 Self {
33 nodes,
34 lifecycle: SlotLifecycleCoordinator::default(),
35 slot_hosts: Vec::new(),
36 }
37 }
38
39 pub(crate) fn from_subcomposed(
40 disposal: crate::subcompose::SubcomposeDisposal,
41 applier: &dyn Applier,
42 ) -> Self {
43 Self {
44 nodes: disposal
45 .nodes
46 .into_iter()
47 .map(|id| (id, applier.node_generation(id)))
48 .collect(),
49 lifecycle: SlotLifecycleCoordinator::default(),
50 slot_hosts: disposal.slot_hosts,
51 }
52 }
53
54 pub(crate) fn retain_subtree(&mut self, subtree: DetachedSubtree) {
55 subtree.append_disposal_roots(&mut self.nodes);
56 self.lifecycle.queue_subtree_disposal(subtree);
57 }
58}
59
60#[derive(Default)]
61struct NodeDisposalQueue {
62 batches: RefCell<Vec<NodeDisposal>>,
63 pending: Cell<bool>,
64}
65
66impl NodeDisposalQueue {
67 fn enqueue(&self, disposal: NodeDisposal) {
68 if !disposal.nodes.is_empty()
69 || !disposal.slot_hosts.is_empty()
70 || disposal.lifecycle.pending_drops_len() != 0
71 {
72 self.batches.borrow_mut().push(disposal);
73 self.pending.set(true);
74 }
75 }
76
77 fn flush<A: Applier + ?Sized>(
78 &self,
79 applier: &mut A,
80 dispose: fn(&mut A, NodeId) -> Result<(), NodeError>,
81 ) -> Result<(), NodeError> {
82 while self.pending.get() {
83 let next = self.batches.borrow_mut().pop();
84 let Some(mut batch) = next else {
85 self.pending.set(false);
86 break;
87 };
88 if let Some((node, generation)) = batch.nodes.pop() {
89 let resume_at = self.batches.borrow().len();
90 self.batches.borrow_mut().push(batch);
91 if applier.node_generation(node) == generation
92 && let Err(error) = dispose(applier, node)
93 {
94 self.batches.borrow_mut()[resume_at]
95 .nodes
96 .push((node, generation));
97 return Err(error);
98 }
99 } else if let Some(host) = batch.slot_hosts.pop() {
100 self.batches.borrow_mut().push(batch);
101 drop(host);
102 } else if let Some(payload) = batch.lifecycle.pop_pending_drop() {
103 self.batches.borrow_mut().push(batch);
104 drop(payload);
105 }
106 }
107 Ok(())
108 }
109}
110
111pub trait ApplierHost {
113 fn borrow_dyn(&self) -> ApplierGuard<'_, dyn Applier>;
114
115 fn dispose_nodes(&self, disposal: NodeDisposal) -> Result<(), NodeError>;
137
138 fn compact(&self) {}
140}
141
142pub struct ConcreteApplierHost<A: Applier + 'static> {
143 inner: RefCell<A>,
144 disposals: NodeDisposalQueue,
145}
146
147impl<A: Applier + 'static> ConcreteApplierHost<A> {
148 pub fn new(applier: A) -> Self {
149 Self {
150 inner: RefCell::new(applier),
151 disposals: NodeDisposalQueue::default(),
152 }
153 }
154
155 pub fn borrow_typed(&self) -> ApplierGuard<'_, A> {
156 self.guard(self.inner.borrow_mut())
157 }
158
159 pub fn try_borrow_typed(&self) -> Result<ApplierGuard<'_, A>, BorrowMutError> {
160 self.inner.try_borrow_mut().map(|inner| self.guard(inner))
161 }
162
163 fn guard<'a>(&'a self, inner: RefMut<'a, A>) -> ApplierGuard<'a, A> {
164 ApplierGuard {
165 inner,
166 disposals: &self.disposals,
167 dispose: |applier, node| dispose_detached_node_now(applier, node),
168 }
169 }
170
171 pub fn try_into_inner(mut self) -> Result<A, (Self, NodeError)> {
182 if let Err(error) = self.disposals.flush(self.inner.get_mut(), |applier, node| {
183 dispose_detached_node_now(applier, node)
184 }) {
185 return Err((self, error));
186 }
187 Ok(self.inner.into_inner())
188 }
189}
190
191impl<A: Applier + 'static> ApplierHost for ConcreteApplierHost<A> {
192 fn borrow_dyn(&self) -> ApplierGuard<'_, dyn Applier> {
193 ApplierGuard {
194 inner: RefMut::map(self.inner.borrow_mut(), |applier| {
195 applier as &mut dyn Applier
196 }),
197 disposals: &self.disposals,
198 dispose: dispose_detached_node_now,
199 }
200 }
201
202 fn dispose_nodes(&self, disposal: NodeDisposal) -> Result<(), NodeError> {
203 self.disposals.enqueue(disposal);
204 if let Ok(mut applier) = self.inner.try_borrow_mut() {
205 return self.disposals.flush(&mut *applier, |applier, node| {
206 dispose_detached_node_now(applier, node)
207 });
208 }
209 Ok(())
210 }
211
212 fn compact(&self) {
213 self.borrow_typed().compact();
214 }
215}
216
217pub struct ApplierGuard<'a, A: Applier + ?Sized + 'static> {
219 inner: RefMut<'a, A>,
220 disposals: &'a NodeDisposalQueue,
221 dispose: fn(&mut A, NodeId) -> Result<(), NodeError>,
222}
223
224impl<A: Applier + ?Sized + 'static> ApplierGuard<'_, A> {
225 fn flush_disposals(&mut self) -> Result<(), NodeError> {
226 self.disposals.flush(&mut *self.inner, self.dispose)
227 }
228}
229
230impl<A: Applier + ?Sized + 'static> Deref for ApplierGuard<'_, A> {
231 type Target = A;
232
233 fn deref(&self) -> &Self::Target {
234 &self.inner
235 }
236}
237
238impl<A: Applier + ?Sized + 'static> DerefMut for ApplierGuard<'_, A> {
239 fn deref_mut(&mut self) -> &mut Self::Target {
240 &mut self.inner
241 }
242}
243
244impl<A: Applier + ?Sized + 'static> Drop for ApplierGuard<'_, A> {
245 fn drop(&mut self) {
246 if self.disposals.pending.get()
247 && let Err(error) = self.flush_disposals()
248 {
249 log::error!("deferred node disposal failed: {error}");
250 }
251 }
252}