1use crate::fiber::{Fiber, FiberInner};
4use crate::utils::{BoxFuture, block_on, lock};
5use crate::{CordisError, ErrorCode, Result};
6use std::fmt::{self, Debug, Formatter};
7use std::future::Future;
8use std::sync::atomic::{AtomicBool, Ordering};
9use std::sync::{Arc, Mutex, Weak};
10
11pub struct AsyncDisposer {
13 callback: Option<Box<dyn FnOnce() -> BoxFuture<Result<()>> + Send + 'static>>,
14}
15
16impl AsyncDisposer {
17 pub fn from_sync<F>(callback: F) -> Self
19 where
20 F: FnOnce() -> Result<()> + Send + 'static,
21 {
22 Self {
23 callback: Some(Box::new(move || Box::pin(async move { callback() }))),
24 }
25 }
26
27 pub fn infallible<F>(callback: F) -> Self
29 where
30 F: FnOnce() + Send + 'static,
31 {
32 Self::from_sync(move || {
33 callback();
34 Ok(())
35 })
36 }
37
38 pub fn from_async<F, Fut>(callback: F) -> Self
40 where
41 F: FnOnce() -> Fut + Send + 'static,
42 Fut: Future<Output = Result<()>> + Send + 'static,
43 {
44 Self {
45 callback: Some(Box::new(move || Box::pin(callback()))),
46 }
47 }
48
49 pub async fn run(mut self) -> Result<()> {
51 match self.callback.take() {
52 Some(callback) => callback().await,
53 None => Ok(()),
54 }
55 }
56}
57
58impl Debug for AsyncDisposer {
59 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
60 f.debug_struct("AsyncDisposer")
61 .field("pending", &self.callback.is_some())
62 .finish()
63 }
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
68pub struct EffectMeta {
69 pub label: String,
71 pub children: Vec<EffectMeta>,
73}
74
75impl EffectMeta {
76 pub fn new(label: impl Into<String>) -> Self {
78 Self {
79 label: label.into(),
80 children: Vec::new(),
81 }
82 }
83}
84
85pub(crate) struct EffectCell {
86 pub(crate) id: u64,
87 owner: Weak<FiberInner>,
88 disposed: AtomicBool,
89 disposer: Mutex<Option<AsyncDisposer>>,
90 children: Mutex<Vec<Arc<EffectCell>>>,
91 meta: Mutex<EffectMeta>,
92}
93
94impl EffectCell {
95 pub(crate) fn new(
96 id: u64,
97 owner: Weak<FiberInner>,
98 label: impl Into<String>,
99 disposer: AsyncDisposer,
100 ) -> Arc<Self> {
101 Arc::new(Self {
102 id,
103 owner,
104 disposed: AtomicBool::new(false),
105 disposer: Mutex::new(Some(disposer)),
106 children: Mutex::new(Vec::new()),
107 meta: Mutex::new(EffectMeta::new(label)),
108 })
109 }
110
111 async fn dispose(self: &Arc<Self>) -> Result<()> {
112 if self.disposed.swap(true, Ordering::AcqRel) {
113 return Ok(());
114 }
115
116 if let Some(owner) = self.owner.upgrade() {
117 owner.remove_effect(self.id);
118 }
119
120 let children = {
121 let mut children = lock(&self.children);
122 std::mem::take(&mut *children)
123 };
124 let mut first_error = None;
125 for child in children.into_iter().rev() {
126 if let Err(error) = Box::pin(child.dispose()).await {
127 if first_error.is_none() {
128 first_error = Some(error);
129 }
130 }
131 }
132
133 let disposer = lock(&self.disposer).take();
134 if let Some(disposer) = disposer {
135 if let Err(error) = disposer.run().await {
136 if first_error.is_none() {
137 first_error = Some(error);
138 }
139 }
140 }
141
142 match first_error {
143 Some(error) => Err(error),
144 None => Ok(()),
145 }
146 }
147
148 pub(crate) fn cancel(&self) {
149 if self.disposed.swap(true, Ordering::AcqRel) {
150 return;
151 }
152 lock(&self.disposer).take();
153 lock(&self.children).clear();
154 if let Some(owner) = self.owner.upgrade() {
155 owner.remove_effect(self.id);
156 }
157 }
158
159 fn adopt(self: &Arc<Self>, child: Arc<EffectCell>) -> Result<()> {
160 let mut children = lock(&self.children);
167 if self.disposed.load(Ordering::Acquire) {
168 return Err(CordisError::new(ErrorCode::InactiveEffect));
169 }
170 if child.disposed.load(Ordering::Acquire) {
171 return Ok(());
172 }
173 if let Some(owner) = child.owner.upgrade() {
174 owner.remove_effect(child.id);
175 }
176 lock(&self.meta).children.push(lock(&child.meta).clone());
177 children.push(child);
178 Ok(())
179 }
180}
181
182#[derive(Clone)]
188pub struct EffectHandle {
189 pub(crate) cell: Arc<EffectCell>,
190}
191
192impl EffectHandle {
193 pub(crate) fn new(cell: Arc<EffectCell>) -> Self {
194 Self { cell }
195 }
196
197 pub fn dispose(&self) -> Result<()> {
199 block_on(self.dispose_async())
200 }
201
202 pub async fn dispose_async(&self) -> Result<()> {
204 self.cell.dispose().await
205 }
206
207 pub fn cancel(&self) {
212 self.cell.cancel();
213 }
214
215 pub fn adopt(&self, child: EffectHandle) -> Result<()> {
217 self.cell.adopt(child.cell)
218 }
219
220 pub fn meta(&self) -> EffectMeta {
222 lock(&self.cell.meta).clone()
223 }
224
225 pub fn is_disposed(&self) -> bool {
227 self.cell.disposed.load(Ordering::Acquire)
228 }
229
230 pub fn owner(&self) -> Option<Fiber> {
232 self.cell.owner.upgrade().map(Fiber::from_inner)
233 }
234}
235
236impl Debug for EffectHandle {
237 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
238 f.debug_struct("EffectHandle")
239 .field("id", &self.cell.id)
240 .field("meta", &self.meta())
241 .field("disposed", &self.is_disposed())
242 .finish()
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249 use std::thread;
250 use std::time::Duration;
251
252 #[test]
255 fn dispose_runs_adopted_children_in_reverse_order() {
256 let runs = Arc::new(Mutex::new(Vec::new()));
257 let parent = EffectCell::new(
258 1,
259 Weak::new(),
260 "parent",
261 AsyncDisposer::from_sync(|| Ok(())),
262 );
263 for id in [2_u64, 3] {
264 let runs = runs.clone();
265 let child = EffectCell::new(
266 id,
267 Weak::new(),
268 "child",
269 AsyncDisposer::from_sync(move || {
270 lock(&runs).push(id);
271 Ok(())
272 }),
273 );
274 parent.adopt(child).unwrap();
275 }
276 block_on(parent.dispose()).unwrap();
277 assert_eq!(*lock(&runs), vec![3, 2]);
278 }
279
280 #[test]
285 fn adopt_racing_parent_disposal_is_rejected() {
286 let parent = EffectCell::new(
287 1,
288 Weak::new(),
289 "parent",
290 AsyncDisposer::from_sync(|| Ok(())),
291 );
292 let child_ran = Arc::new(AtomicBool::new(false));
293 let child_ran_in_disposer = child_ran.clone();
294 let child = EffectCell::new(
295 2,
296 Weak::new(),
297 "child",
298 AsyncDisposer::from_sync(move || {
299 child_ran_in_disposer.store(true, Ordering::SeqCst);
300 Ok(())
301 }),
302 );
303
304 let gate = lock(&parent.children);
306 let adopting_parent = parent.clone();
307 let adopt_handle = thread::spawn(move || adopting_parent.adopt(child));
308 thread::sleep(Duration::from_millis(50));
309 let disposing_parent = parent.clone();
310 let dispose_handle = thread::spawn(move || block_on(disposing_parent.dispose()));
311 thread::sleep(Duration::from_millis(50));
312 drop(gate);
313
314 assert!(adopt_handle.join().unwrap().is_err());
315 dispose_handle.join().unwrap().unwrap();
316 assert!(!child_ran.load(Ordering::SeqCst));
317 }
318}