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>;
116
117 fn dispose_nodes(&self, disposal: NodeDisposal) -> Result<(), NodeError>;
139
140 fn compact(&self) {}
142}
143
144pub struct ConcreteApplierHost<A: Applier + 'static> {
146 inner: RefCell<A>,
147 disposals: NodeDisposalQueue,
148}
149
150impl<A: Applier + 'static> ConcreteApplierHost<A> {
151 pub fn new(applier: A) -> Self {
153 Self {
154 inner: RefCell::new(applier),
155 disposals: NodeDisposalQueue::default(),
156 }
157 }
158
159 pub fn borrow_typed(&self) -> ApplierGuard<'_, A> {
161 self.guard(self.inner.borrow_mut())
162 }
163
164 pub fn try_borrow_typed(&self) -> Result<ApplierGuard<'_, A>, BorrowMutError> {
167 self.inner.try_borrow_mut().map(|inner| self.guard(inner))
168 }
169
170 fn guard<'a>(&'a self, inner: RefMut<'a, A>) -> ApplierGuard<'a, A> {
171 ApplierGuard {
172 inner,
173 disposals: &self.disposals,
174 dispose: |applier, node| dispose_detached_node_now(applier, node),
175 }
176 }
177
178 pub fn try_into_inner(mut self) -> Result<A, (Self, NodeError)> {
189 if let Err(error) = self.disposals.flush(self.inner.get_mut(), |applier, node| {
190 dispose_detached_node_now(applier, node)
191 }) {
192 return Err((self, error));
193 }
194 Ok(self.inner.into_inner())
195 }
196}
197
198impl<A: Applier + 'static> ApplierHost for ConcreteApplierHost<A> {
199 fn borrow_dyn(&self) -> ApplierGuard<'_, dyn Applier> {
200 ApplierGuard {
201 inner: RefMut::map(self.inner.borrow_mut(), |applier| {
202 applier as &mut dyn Applier
203 }),
204 disposals: &self.disposals,
205 dispose: dispose_detached_node_now,
206 }
207 }
208
209 fn dispose_nodes(&self, disposal: NodeDisposal) -> Result<(), NodeError> {
210 self.disposals.enqueue(disposal);
211 if let Ok(mut applier) = self.inner.try_borrow_mut() {
212 return self.disposals.flush(&mut *applier, |applier, node| {
213 dispose_detached_node_now(applier, node)
214 });
215 }
216 Ok(())
217 }
218
219 fn compact(&self) {
220 self.borrow_typed().compact();
221 }
222}
223
224pub struct ApplierGuard<'a, A: Applier + ?Sized + 'static> {
226 inner: RefMut<'a, A>,
227 disposals: &'a NodeDisposalQueue,
228 dispose: fn(&mut A, NodeId) -> Result<(), NodeError>,
229}
230
231impl<A: Applier + ?Sized + 'static> ApplierGuard<'_, A> {
232 fn flush_disposals(&mut self) -> Result<(), NodeError> {
233 self.disposals.flush(&mut *self.inner, self.dispose)
234 }
235}
236
237impl<A: Applier + ?Sized + 'static> Deref for ApplierGuard<'_, A> {
238 type Target = A;
239
240 fn deref(&self) -> &Self::Target {
241 &self.inner
242 }
243}
244
245impl<A: Applier + ?Sized + 'static> DerefMut for ApplierGuard<'_, A> {
246 fn deref_mut(&mut self) -> &mut Self::Target {
247 &mut self.inner
248 }
249}
250
251impl<A: Applier + ?Sized + 'static> Drop for ApplierGuard<'_, A> {
252 fn drop(&mut self) {
253 if self.disposals.pending.get()
254 && let Err(error) = self.flush_disposals()
255 {
256 log::error!("deferred node disposal failed: {error}");
257 }
258 }
259}