Skip to main content

starnix_core/task/
delayed_release.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
12/// Register the container to be deferred released.
13pub 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
34/// An object-safe/dyn-compatible trait to wrap `Releasable` types.
35pub 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
52/// Register a callback to be run during delayed release.
53pub 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    /// Container of all `FileObject` that are not used anymore, but have not been closed yet.
62    static RELEASERS: RefCell<Option<LocalReleasers>> =
63        RefCell::new(Some(LocalReleasers::default()));
64}
65
66#[derive(Default)]
67struct LocalReleasers {
68    /// The list of entities to be deferred released.
69    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/// Service to handle delayed releases.
90///
91/// Delayed releases are cleanup code that is run at specific point where the lock level is
92/// known. The starnix kernel must ensure that delayed releases are run regularly.
93#[derive(Debug, Default)]
94pub struct DelayedReleaser {}
95
96impl DelayedReleaser {
97    /// Run all current delayed releases for the current thread.
98    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    /// Prevent any further releasables from being registered on this thread.
125    ///
126    /// This function should be called during thread teardown to ensure that we do not
127    /// register any new releasables on this thread after we have finalized the delayed
128    /// releasables for the last time.
129    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}