starnix_core/task/
delayed_release.rs1use crate::task::CurrentTask;
6use fuchsia_rcu::rcu_run_callbacks;
7use starnix_logging::log_warn;
8use starnix_types::ownership::Releasable;
9use std::cell::RefCell;
10use std::ops::DerefMut;
11
12pub fn register_delayed_release<T: for<'a> Releasable<Context<'a> = &'a CurrentTask> + 'static>(
14 to_release: T,
15) {
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 RELEASERS: RefCell<Option<LocalReleasers>> =
63 RefCell::new(Some(LocalReleasers::default()));
64}
65
66#[derive(Default)]
67struct LocalReleasers {
68 releasables: Vec<Box<dyn CurrentTaskReleasable>>,
70}
71
72impl LocalReleasers {
73 fn is_empty(&self) -> bool {
74 self.releasables.is_empty()
75 }
76}
77
78impl Releasable for LocalReleasers {
79 type Context<'a> = &'a CurrentTask;
80
81 fn release<'a>(self, context: &'a CurrentTask) {
82 let current_task = context;
83 for mut releasable in self.releasables {
84 releasable.release_with_context(current_task);
85 }
86 }
87}
88
89#[derive(Debug, Default)]
94pub struct DelayedReleaser {}
95
96impl DelayedReleaser {
97 pub fn apply(&self, current_task: &CurrentTask) {
99 let mut counter = 0u32;
100 loop {
101 let ran_rcu = rcu_run_callbacks();
102 let releasers = RELEASERS.with(|cell| {
103 std::mem::take(
104 cell.borrow_mut()
105 .as_mut()
106 .expect("DelayedReleaser hasn't been finalized yet")
107 .deref_mut(),
108 )
109 });
110 if releasers.is_empty() && !ran_rcu {
111 return;
112 }
113 releasers.release(current_task);
114 counter += 1;
115 if counter == 100 {
116 log_warn!("DelayedReleaser: applied >=100 delayed releases");
117 }
118 if counter > 10000 {
119 panic!("DelayedReleaser: applied >10000 delayed releases");
120 }
121 }
122 }
123
124 pub fn finalize() {
130 RELEASERS.with(|cell| {
131 let list = cell.borrow_mut().take().expect("DelayedReleaser hasn't been finalized");
132 assert!(list.is_empty());
133 });
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use crate::testing::spawn_kernel_and_run;
141 use std::sync::Arc;
142 use std::sync::atomic::{AtomicBool, Ordering};
143
144 #[::fuchsia::test]
145 async fn test_register_delayed_call() {
146 spawn_kernel_and_run(async move |current_task| {
147 let flag = Arc::new(AtomicBool::new(false));
148 let flag_clone = flag.clone();
149 register_delayed_call(move |_current_task| {
150 flag_clone.store(true, Ordering::SeqCst);
151 });
152
153 assert!(!flag.load(Ordering::SeqCst));
154 DelayedReleaser::default().apply(current_task);
155 assert!(flag.load(Ordering::SeqCst));
156 })
157 .await;
158 }
159}