starnix_core/task/
delayed_release.rs1use crate::task::CurrentTask;
6use starnix_logging::log_warn;
7use starnix_types::ownership::Releasable;
8use std::cell::{Cell, RefCell};
9use std::ops::DerefMut;
10
11pub fn register_delayed_release<T: for<'a> Releasable<Context<'a> = &'a CurrentTask> + 'static>(
13 to_release: T,
14) {
15 HAS_PENDING_RELEASERS.with(|flag| flag.set(true));
16 RELEASERS.with(|cell| {
17 let mut cell = cell.borrow_mut();
18 let list = &mut cell.as_mut().expect("DelayedReleaser hasn't been finalized").releasables;
19 list.push(Box::new(Some(to_release)));
20 });
21}
22
23impl<T> CurrentTaskReleasable for Option<T>
24where
25 for<'a> T: Releasable<Context<'a> = &'a CurrentTask>,
26{
27 fn release_with_context(&mut self, context: &CurrentTask) {
28 if let Some(this) = self.take() {
29 <T as Releasable>::release(this, context);
30 }
31 }
32}
33
34pub trait CurrentTaskReleasable {
36 fn release_with_context(&mut self, context: &CurrentTask);
37}
38
39struct DelayedCallback<F>(F);
40
41impl<F> Releasable for DelayedCallback<F>
42where
43 F: FnOnce(&CurrentTask) + 'static,
44{
45 type Context<'a> = &'a CurrentTask;
46
47 fn release<'a>(self, context: Self::Context<'a>) {
48 (self.0)(context);
49 }
50}
51
52pub fn register_delayed_call<F>(callback: F)
54where
55 F: FnOnce(&CurrentTask) + 'static,
56{
57 register_delayed_release(DelayedCallback(callback));
58}
59
60thread_local! {
61 static HAS_PENDING_RELEASERS: Cell<bool> = const { Cell::new(false) };
63
64 static RELEASERS: RefCell<Option<LocalReleasers>> =
66 RefCell::new(Some(LocalReleasers::default()));
67}
68
69#[derive(Default)]
70struct LocalReleasers {
71 releasables: Vec<Box<dyn CurrentTaskReleasable>>,
73}
74
75impl LocalReleasers {
76 fn is_empty(&self) -> bool {
77 self.releasables.is_empty()
78 }
79}
80
81impl Releasable for LocalReleasers {
82 type Context<'a> = &'a CurrentTask;
83
84 fn release<'a>(self, context: &'a CurrentTask) {
85 let current_task = context;
86 for mut releasable in self.releasables {
87 releasable.release_with_context(current_task);
88 }
89 }
90}
91
92#[derive(Debug, Default)]
97pub struct DelayedReleaser {}
98
99impl DelayedReleaser {
100 #[inline]
102 pub fn apply(&self, current_task: &CurrentTask) {
103 debug_assert!(
104 RELEASERS.with(|cell| cell.borrow().is_some()),
105 "DelayedReleaser has been finalized"
106 );
107 if !HAS_PENDING_RELEASERS.with(|flag| flag.replace(false)) {
108 return;
109 }
110 self.apply_slow(current_task);
111 }
112
113 #[cold]
114 #[inline(never)]
115 fn apply_slow(&self, current_task: &CurrentTask) {
116 let mut counter = 0u32;
117 loop {
118 HAS_PENDING_RELEASERS.with(|flag| flag.set(false));
119 let releasers = RELEASERS.with(|cell| {
120 std::mem::take(
121 cell.borrow_mut()
122 .as_mut()
123 .expect("DelayedReleaser hasn't been finalized yet")
124 .deref_mut(),
125 )
126 });
127 if releasers.is_empty() {
128 return;
129 }
130 releasers.release(current_task);
131 counter += 1;
132 if counter == 100 {
133 log_warn!("DelayedReleaser: applied >=100 delayed releases");
134 }
135 if counter > 10000 {
136 panic!("DelayedReleaser: applied >10000 delayed releases");
137 }
138 }
139 }
140
141 pub fn finalize() {
147 HAS_PENDING_RELEASERS.with(|flag| flag.set(false));
148 RELEASERS.with(|cell| {
149 let list = cell.borrow_mut().take().expect("DelayedReleaser hasn't been finalized");
150 assert!(list.is_empty());
151 });
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use crate::testing::spawn_kernel_and_run;
159 use std::sync::Arc;
160 use std::sync::atomic::{AtomicBool, Ordering};
161
162 #[::fuchsia::test]
163 async fn test_register_delayed_call() {
164 spawn_kernel_and_run(async move |current_task| {
165 let flag = Arc::new(AtomicBool::new(false));
166 let flag_clone = flag.clone();
167 register_delayed_call(move |_current_task| {
168 flag_clone.store(true, Ordering::SeqCst);
169 });
170
171 assert!(!flag.load(Ordering::SeqCst));
172 DelayedReleaser::default().apply(current_task);
173 assert!(flag.load(Ordering::SeqCst));
174 })
175 .await;
176 }
177
178 #[::fuchsia::test]
179 async fn test_delayed_call_registered_during_apply() {
180 spawn_kernel_and_run(async move |current_task| {
181 let flag = Arc::new(AtomicBool::new(false));
182 let flag_clone = flag.clone();
183 register_delayed_call(move |_current_task| {
184 register_delayed_call(move |_current_task| {
186 flag_clone.store(true, Ordering::SeqCst);
187 });
188 });
189
190 DelayedReleaser::default().apply(current_task);
191 assert!(flag.load(Ordering::SeqCst));
192 })
193 .await;
194 }
195}