starnix_core/task/
delayed_release.rs1use crate::task::CurrentTask;
6use starnix_logging::log_warn;
7use starnix_types::ownership::Releasable;
8use std::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 RELEASERS.with(|cell| {
16 let mut cell = cell.borrow_mut();
17 let list = &mut cell.as_mut().expect("DelayedReleaser hasn't been finalized").releasables;
18 list.push(Box::new(Some(to_release)));
19 });
20}
21
22impl<T> CurrentTaskReleasable for Option<T>
23where
24 for<'a> T: Releasable<Context<'a> = &'a CurrentTask>,
25{
26 fn release_with_context(&mut self, context: &CurrentTask) {
27 if let Some(this) = self.take() {
28 <T as Releasable>::release(this, context);
29 }
30 }
31}
32
33pub trait CurrentTaskReleasable {
35 fn release_with_context(&mut self, context: &CurrentTask);
36}
37
38struct DelayedCallback<F>(F);
39
40impl<F> Releasable for DelayedCallback<F>
41where
42 F: FnOnce(&CurrentTask) + 'static,
43{
44 type Context<'a> = &'a CurrentTask;
45
46 fn release<'a>(self, context: Self::Context<'a>) {
47 (self.0)(context);
48 }
49}
50
51pub fn register_delayed_call<F>(callback: F)
53where
54 F: FnOnce(&CurrentTask) + 'static,
55{
56 register_delayed_release(DelayedCallback(callback));
57}
58
59thread_local! {
60 static RELEASERS: RefCell<Option<LocalReleasers>> =
62 RefCell::new(Some(LocalReleasers::default()));
63}
64
65#[derive(Default)]
66struct LocalReleasers {
67 releasables: Vec<Box<dyn CurrentTaskReleasable>>,
69}
70
71impl LocalReleasers {
72 fn is_empty(&self) -> bool {
73 self.releasables.is_empty()
74 }
75}
76
77impl Releasable for LocalReleasers {
78 type Context<'a> = &'a CurrentTask;
79
80 fn release<'a>(self, context: &'a CurrentTask) {
81 let current_task = context;
82 for mut releasable in self.releasables {
83 releasable.release_with_context(current_task);
84 }
85 }
86}
87
88#[derive(Debug, Default)]
93pub struct DelayedReleaser {}
94
95impl DelayedReleaser {
96 pub fn apply(&self, current_task: &CurrentTask) {
98 let mut counter = 0u32;
99 loop {
100 let releasers = RELEASERS.with(|cell| {
101 std::mem::take(
102 cell.borrow_mut()
103 .as_mut()
104 .expect("DelayedReleaser hasn't been finalized yet")
105 .deref_mut(),
106 )
107 });
108 if releasers.is_empty() {
109 return;
110 }
111 releasers.release(current_task);
112 counter += 1;
113 if counter == 100 {
114 log_warn!("DelayedReleaser: applied >=100 delayed releases");
115 }
116 if counter > 10000 {
117 panic!("DelayedReleaser: applied >10000 delayed releases");
118 }
119 }
120 }
121
122 pub fn finalize() {
128 RELEASERS.with(|cell| {
129 let list = cell.borrow_mut().take().expect("DelayedReleaser hasn't been finalized");
130 assert!(list.is_empty());
131 });
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use crate::testing::spawn_kernel_and_run;
139 use std::sync::Arc;
140 use std::sync::atomic::{AtomicBool, Ordering};
141
142 #[::fuchsia::test]
143 async fn test_register_delayed_call() {
144 spawn_kernel_and_run(async move |current_task| {
145 let flag = Arc::new(AtomicBool::new(false));
146 let flag_clone = flag.clone();
147 register_delayed_call(move |_current_task| {
148 flag_clone.store(true, Ordering::SeqCst);
149 });
150
151 assert!(!flag.load(Ordering::SeqCst));
152 DelayedReleaser::default().apply(current_task);
153 assert!(flag.load(Ordering::SeqCst));
154 })
155 .await;
156 }
157}