Skip to main content

rs_matter/utils/storage/
pooled.rs

1/*
2 *
3 *    Copyright (c) 2024-2026 Project CHIP Authors
4 *
5 *    Licensed under the Apache License, Version 2.0 (the "License");
6 *    you may not use this file except in compliance with the License.
7 *    You may obtain a copy of the License at
8 *
9 *        http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *    Unless required by applicable law or agreed to in writing, software
12 *    distributed under the License is distributed on an "AS IS" BASIS,
13 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *    See the License for the specific language governing permissions and
15 *    limitations under the License.
16 */
17
18use core::cell::UnsafeCell;
19use core::future::Future;
20use core::ops::{Deref, DerefMut};
21use core::pin::pin;
22
23use embassy_futures::select::{select, Either};
24use embassy_sync::blocking_mutex::raw::RawMutex;
25use embassy_time::{Duration, Timer};
26
27use crate::utils::init::{init, Init, InitDefault, UnsafeCellInit};
28use crate::utils::sync::blocking::raw::MatterRawMutex;
29use crate::utils::sync::Signal;
30
31/// A trait for getting access to a `&mut T` buffer, potentially awaiting until a buffer becomes available.
32pub trait Buffers<T>
33where
34    T: ?Sized,
35{
36    type Buffer<'a>: DerefMut<Target = T>
37    where
38        Self: 'a;
39
40    /// Get a reference to a buffer.
41    /// Might await until a buffer is available, as it might be in use by somebody else.
42    ///
43    /// Depending on its internal implementation details, access to a buffer might also be denied
44    /// immediately, or after a certain amount of time (subject to the concrete implementation of the method).
45    /// In that case, the method will return `None`.
46    async fn get(&self) -> Option<Self::Buffer<'_>>;
47
48    /// Get a reference to a buffer immediately, without waiting.
49    /// If no buffer is available, return `None`.
50    fn get_immediate(&self) -> Option<Self::Buffer<'_>>;
51}
52
53impl<B, T> Buffers<T> for &B
54where
55    B: Buffers<T>,
56    T: ?Sized,
57{
58    type Buffer<'a>
59        = B::Buffer<'a>
60    where
61        Self: 'a;
62
63    fn get(&self) -> impl Future<Output = Option<Self::Buffer<'_>>> {
64        (*self).get()
65    }
66
67    fn get_immediate(&self) -> Option<Self::Buffer<'_>> {
68        (*self).get_immediate()
69    }
70}
71
72/// The default number of buffers held by a [`PooledBuffers`] pool.
73pub const DEFAULT_BUFFER_POOL_SIZE: usize = 10;
74
75/// A concrete implementation of `Buffers` utilizing an internal pool of buffers.
76/// Accessing a buffer would fail when all buffers are still used elsewhere after a wait timeout expires.
77pub struct PooledBuffers<T, const N: usize = DEFAULT_BUFFER_POOL_SIZE, M = MatterRawMutex> {
78    available: Signal<[bool; N], M>, // TODO XXX FIXME: Needs multiple wakers for work-stealing executors
79    pool: UnsafeCell<crate::utils::storage::Vec<T, N>>,
80    wait_timeout_ms: u32,
81}
82
83impl<T, const N: usize, M> PooledBuffers<T, N, M>
84where
85    M: RawMutex,
86{
87    /// Create a new instance of `PooledBuffers` with the default (zero) wait
88    /// timeout, i.e. buffer access is denied immediately when none is free.
89    #[inline(always)]
90    pub const fn new() -> Self {
91        Self::new_with_timeout(0)
92    }
93
94    /// Create a new instance of `PooledBuffers`.
95    ///
96    /// `wait_timeout_ms` is the maximum time to wait for a buffer to become available
97    /// before returning `None`.
98    #[inline(always)]
99    pub const fn new_with_timeout(wait_timeout_ms: u32) -> Self {
100        Self {
101            available: Signal::new([true; N]),
102            pool: UnsafeCell::new(crate::utils::storage::Vec::new()),
103            wait_timeout_ms,
104        }
105    }
106
107    /// Create an in-place initializer for `PooledBuffers` with the default (zero)
108    /// wait timeout, i.e. buffer access is denied immediately when none is free.
109    pub fn init() -> impl Init<Self> {
110        Self::init_with_timeout(0)
111    }
112
113    /// Create an in-place initializer for `PooledBuffers`.
114    ///
115    /// `wait_timeout_ms` is the maximum time to wait for a buffer to become available
116    /// before returning `None`.
117    pub fn init_with_timeout(wait_timeout_ms: u32) -> impl Init<Self> {
118        init!(Self {
119            available: Signal::new([true; N]),
120            pool <- UnsafeCell::init(crate::utils::storage::Vec::init()),
121            wait_timeout_ms,
122        })
123    }
124
125    fn init_buffers(pool: &UnsafeCell<crate::utils::storage::Vec<T, N>>)
126    where
127        T: InitDefault,
128    {
129        let buffers = unwrap!(unsafe { pool.get().as_mut() });
130
131        while buffers.len() < N {
132            // In-place initialization: each slot is written directly via pinned-init,
133            // never materializing a full `T` value on the stack. This is essential when
134            // `T` is a large buffer (e.g. 1 MiB) that would otherwise overflow the stack.
135            unwrap!(buffers.push_init_unchecked(T::init_default()));
136        }
137    }
138}
139
140impl<T, const N: usize, M> Default for PooledBuffers<T, N, M>
141where
142    M: RawMutex,
143{
144    fn default() -> Self {
145        Self::new()
146    }
147}
148
149unsafe impl<T, const N: usize, M> Send for PooledBuffers<T, N, M>
150where
151    T: Send,
152    M: RawMutex + Send,
153{
154}
155
156unsafe impl<T, const N: usize, M> Sync for PooledBuffers<T, N, M>
157where
158    T: Send,
159    M: RawMutex + Send + Sync,
160{
161}
162
163impl<T, const N: usize, M> Buffers<T> for PooledBuffers<T, N, M>
164where
165    T: InitDefault,
166    M: RawMutex,
167{
168    type Buffer<'b>
169        = PooledBuffer<'b, T, N, M>
170    where
171        Self: 'b;
172
173    async fn get(&self) -> Option<Self::Buffer<'_>> {
174        if self.wait_timeout_ms > 0 {
175            let mut wait = pin!(self.available.wait(|available| {
176                // Make sure the buffers are properly sized before starting to use them
177                Self::init_buffers(&self.pool);
178
179                if let Some(index) = available.iter().position(|a| *a) {
180                    available[index] = false;
181                    Some(index)
182                } else {
183                    None
184                }
185            }));
186
187            let mut timeout = pin!(Timer::after(Duration::from_millis(
188                self.wait_timeout_ms as u64
189            )));
190
191            let result = select(&mut wait, &mut timeout).await;
192
193            match result {
194                Either::First(index) => {
195                    let buffer = &mut unwrap!(unsafe { self.pool.get().as_mut() })[index];
196
197                    Some(PooledBuffer {
198                        index,
199                        buffer,
200                        access: self,
201                    })
202                }
203                Either::Second(()) => None,
204            }
205        } else {
206            self.get_immediate()
207        }
208    }
209
210    fn get_immediate(&self) -> Option<Self::Buffer<'_>> {
211        let index = self.available.modify(|available| {
212            // Make sure the buffers are properly sized before starting to use them
213            Self::init_buffers(&self.pool);
214
215            if let Some(index) = available.iter().position(|a| *a) {
216                available[index] = false;
217                (false, Some(index))
218            } else {
219                (false, None)
220            }
221        });
222
223        index.map(|index| {
224            let buffers = unwrap!(unsafe { self.pool.get().as_mut() });
225
226            let buffer = &mut buffers[index];
227
228            PooledBuffer {
229                index,
230                buffer,
231                access: self,
232            }
233        })
234    }
235}
236
237pub struct PooledBuffer<'a, T, const N: usize, M = MatterRawMutex>
238where
239    M: RawMutex,
240{
241    index: usize,
242    buffer: &'a mut T,
243    access: &'a PooledBuffers<T, N, M>,
244}
245
246impl<T, const N: usize, M> Drop for PooledBuffer<'_, T, N, M>
247where
248    M: RawMutex,
249{
250    fn drop(&mut self) {
251        self.access.available.modify(|available| {
252            available[self.index] = true;
253            (true, ())
254        });
255    }
256}
257
258impl<T, const N: usize, M> Deref for PooledBuffer<'_, T, N, M>
259where
260    M: RawMutex,
261{
262    type Target = T;
263
264    fn deref(&self) -> &Self::Target {
265        self.buffer.deref()
266    }
267}
268
269impl<T, const N: usize, M> DerefMut for PooledBuffer<'_, T, N, M>
270where
271    M: RawMutex,
272{
273    fn deref_mut(&mut self) -> &mut Self::Target {
274        self.buffer.deref_mut()
275    }
276}