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 starnix_logging::log_warn;
7use starnix_types::ownership::Releasable;
8use std::cell::RefCell;
9use std::ops::DerefMut;
10
11/// Register the container to be deferred released.
12pub 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
33/// An object-safe/dyn-compatible trait to wrap `Releasable` types.
34pub 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
51/// Register a callback to be run during delayed release.
52pub 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    /// Container of all `FileObject` that are not used anymore, but have not been closed yet.
61    static RELEASERS: RefCell<Option<LocalReleasers>> =
62        RefCell::new(Some(LocalReleasers::default()));
63}
64
65#[derive(Default)]
66struct LocalReleasers {
67    /// The list of entities to be deferred released.
68    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/// Service to handle delayed releases.
89///
90/// Delayed releases are cleanup code that is run at specific point where the lock level is
91/// known. The starnix kernel must ensure that delayed releases are run regularly.
92#[derive(Debug, Default)]
93pub struct DelayedReleaser {}
94
95impl DelayedReleaser {
96    /// Run all current delayed releases for the current thread.
97    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    /// Prevent any further releasables from being registered on this thread.
123    ///
124    /// This function should be called during thread teardown to ensure that we do not
125    /// register any new releasables on this thread after we have finalized the delayed
126    /// releasables for the last time.
127    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}