safer_ring/pool/
pooled_buffer.rs

1//! RAII wrapper for pool-managed buffers.
2
3use std::pin::Pin;
4use std::sync::{Arc, Mutex};
5
6use crate::buffer::PinnedBuffer;
7
8use super::PoolInner;
9
10/// A buffer borrowed from a pool that automatically returns on drop.
11///
12/// Provides RAII semantics - the buffer is automatically returned to the pool
13/// when dropped, ensuring no buffer leaks even in error conditions.
14///
15/// # Safety
16///
17/// Maintains all safety guarantees of [`PinnedBuffer`] while adding
18/// automatic pool return semantics.
19pub struct PooledBuffer {
20    /// The actual buffer (None after being returned)
21    buffer: Option<PinnedBuffer<[u8]>>,
22    /// Reference to the pool for return on drop
23    pool: Arc<Mutex<PoolInner>>,
24}
25
26impl PooledBuffer {
27    /// Create a new pooled buffer.
28    ///
29    /// This is an internal constructor used by the pool.
30    pub(super) fn new(buffer: PinnedBuffer<[u8]>, pool: Arc<Mutex<PoolInner>>) -> Self {
31        Self {
32            buffer: Some(buffer),
33            pool,
34        }
35    }
36
37    /// Get a reference to the underlying buffer.
38    ///
39    /// # Panics
40    ///
41    /// Panics if the buffer has already been returned (internal bug).
42    pub fn buffer(&self) -> &PinnedBuffer<[u8]> {
43        self.buffer
44            .as_ref()
45            .expect("Buffer should be present - this is a bug")
46    }
47
48    /// Get a mutable reference to the underlying buffer.
49    ///
50    /// # Panics
51    ///
52    /// Panics if the buffer has already been returned (internal bug).
53    pub fn buffer_mut(&mut self) -> &mut PinnedBuffer<[u8]> {
54        self.buffer
55            .as_mut()
56            .expect("Buffer should be present - this is a bug")
57    }
58
59    /// Get a pinned mutable slice reference to the buffer data.
60    ///
61    /// This maintains pinning guarantees required for io_uring operations.
62    pub fn as_mut_slice(&mut self) -> Pin<&mut [u8]> {
63        self.buffer_mut().as_mut_slice()
64    }
65
66    /// Get an immutable slice reference to the buffer data.
67    pub fn as_slice(&self) -> &[u8] {
68        self.buffer().as_slice()
69    }
70
71    /// Get the length of the buffer.
72    pub fn len(&self) -> usize {
73        self.buffer().len()
74    }
75
76    /// Check if the buffer is empty.
77    pub fn is_empty(&self) -> bool {
78        self.buffer().is_empty()
79    }
80
81    /// Detach the buffer from the pool, taking ownership.
82    ///
83    /// This removes the buffer from pool management, preventing automatic
84    /// return on drop. Use when you need to transfer ownership elsewhere.
85    pub fn detach(mut self) -> PinnedBuffer<[u8]> {
86        self.buffer
87            .take()
88            .expect("Buffer should be present - this is a bug")
89    }
90}
91
92impl Drop for PooledBuffer {
93    /// Automatically return the buffer to the pool when dropped.
94    ///
95    /// Ensures proper resource management even in error conditions.
96    fn drop(&mut self) {
97        if let Some(buffer) = self.buffer.take() {
98            // Return buffer to pool - ignore lock errors during drop
99            if let Ok(mut inner) = self.pool.lock() {
100                inner.available.push_back(buffer);
101                inner.in_use = inner.in_use.saturating_sub(1);
102            }
103            // If lock fails during drop, we're likely in a panic situation
104            // and the buffer will be dropped normally, which is acceptable
105        }
106    }
107}
108
109// Debug implementation that doesn't expose buffer contents
110impl std::fmt::Debug for PooledBuffer {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        f.debug_struct("PooledBuffer")
113            .field("len", &self.len())
114            .field("has_buffer", &self.buffer.is_some())
115            .finish()
116    }
117}
118
119// SAFETY: PooledBuffer can be sent between threads when the underlying buffer is Send
120unsafe impl Send for PooledBuffer {}
121
122// SAFETY: PooledBuffer can be shared between threads when the underlying buffer is Sync
123// However, since we provide mutable access, it's typically used with exclusive ownership
124unsafe impl Sync for PooledBuffer {}