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 {}