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_sync::{FileOpsCore, Locked};
9use starnix_types::ownership::Releasable;
10use std::cell::RefCell;
11use std::ops::DerefMut;
12
13/// Register the container to be deferred released.
14pub fn register_delayed_release<
15    T: for<'a> Releasable<Context<'a> = CurrentTaskAndLocked<'a>> + 'static,
16>(
17    to_release: T,
18) {
19    RELEASERS.with(|cell| {
20        let mut cell = cell.borrow_mut();
21        let list = &mut cell.as_mut().expect("DelayedReleaser hasn't been finalized").releasables;
22        list.push(Box::new(Some(to_release)));
23    });
24}
25
26impl<T> CurrentTaskAndLockedReleasable for Option<T>
27where
28    for<'a> T: Releasable<Context<'a> = CurrentTaskAndLocked<'a>>,
29{
30    fn release_with_context(&mut self, context: CurrentTaskAndLocked<'_>) {
31        if let Some(this) = self.take() {
32            <T as Releasable>::release(this, context);
33        }
34    }
35}
36
37pub type CurrentTaskAndLocked<'a> = (&'a mut Locked<FileOpsCore>, &'a CurrentTask);
38
39/// An object-safe/dyn-compatible trait to wrap `Releasable` types.
40pub trait CurrentTaskAndLockedReleasable {
41    fn release_with_context(&mut self, context: CurrentTaskAndLocked<'_>);
42}
43
44thread_local! {
45    /// Container of all `FileObject` that are not used anymore, but have not been closed yet.
46    static RELEASERS: RefCell<Option<LocalReleasers>> =
47        RefCell::new(Some(LocalReleasers::default()));
48}
49
50#[derive(Default)]
51struct LocalReleasers {
52    /// The list of entities to be deferred released.
53    releasables: Vec<Box<dyn CurrentTaskAndLockedReleasable>>,
54}
55
56impl LocalReleasers {
57    fn is_empty(&self) -> bool {
58        self.releasables.is_empty()
59    }
60}
61
62impl Releasable for LocalReleasers {
63    type Context<'a> = CurrentTaskAndLocked<'a>;
64
65    fn release<'a>(self, context: CurrentTaskAndLocked<'a>) {
66        let (locked, current_task) = context;
67        for mut releasable in self.releasables {
68            releasable.release_with_context((locked, current_task));
69        }
70    }
71}
72
73/// Service to handle delayed releases.
74///
75/// Delayed releases are cleanup code that is run at specific point where the lock level is
76/// known. The starnix kernel must ensure that delayed releases are run regularly.
77#[derive(Debug, Default)]
78pub struct DelayedReleaser {}
79
80impl DelayedReleaser {
81    /// Run all current delayed releases for the current thread.
82    pub fn apply<'a>(&self, locked: &'a mut Locked<FileOpsCore>, current_task: &'a CurrentTask) {
83        let mut counter = 0u32;
84        loop {
85            rcu_run_callbacks();
86            let releasers = RELEASERS.with(|cell| {
87                std::mem::take(
88                    cell.borrow_mut()
89                        .as_mut()
90                        .expect("DelayedReleaser hasn't been finalized yet")
91                        .deref_mut(),
92                )
93            });
94            if releasers.is_empty() {
95                return;
96            }
97            releasers.release((locked, current_task));
98            counter += 1;
99            if counter == 100 {
100                log_warn!("DelayedReleaser: applied >=100 delayed releases");
101            }
102            if counter > 10000 {
103                panic!("DelayedReleaser: applied >10000 delayed releases");
104            }
105        }
106    }
107
108    /// Prevent any further releasables from being registered on this thread.
109    ///
110    /// This function should be called during thread teardown to ensure that we do not
111    /// register any new releasables on this thread after we have finalized the delayed
112    /// releasables for the last time.
113    pub fn finalize() {
114        RELEASERS.with(|cell| {
115            let list = cell.borrow_mut().take().expect("DelayedReleaser hasn't been finalized");
116            assert!(list.is_empty());
117        });
118    }
119}