base_scheduler/
heap_buffer_pool.rs1use crate::{BufferLease, Cancellation, Cancelled};
5use event_listener::Event;
6#[cfg(not(test))]
7use std::process::abort;
8use std::{
9 cell::UnsafeCell,
10 future::{Future, poll_fn},
11 io,
12 pin::pin,
13 ptr::NonNull,
14 sync::{
15 Arc, Mutex,
16 atomic::{AtomicUsize, Ordering},
17 },
18 task::Poll,
19};
20#[cfg(test)]
21use tests::abort;
22
23#[derive(Clone)]
27pub struct HeapBufferPool(pub(crate) Arc<Pool>);
28
29pub(crate) struct Slot {
30 pub(crate) readers: AtomicUsize,
31}
32
33pub(crate) struct Pool {
34 slab: Arc<Slab>,
35 waiters: Mutex<usize>,
36}
37
38const CLOSED: usize = 1 << (usize::BITS - 1);
40const WAITING: usize = CLOSED >> 1;
41const AVAILABLE: usize = WAITING - 1;
42const WIDTH: usize = usize::BITS as usize - 2;
43
44#[cfg(not(test))]
46const COUNT_LIMIT: usize = isize::MAX as usize;
47#[cfg(test)]
48const COUNT_LIMIT: usize = 128;
49
50pub(crate) struct Slab {
52 pub(crate) slots: Box<[Slot]>,
53 bytes: Box<[UnsafeCell<u8>]>,
54 offset: usize,
55 stride: usize,
56 free: Box<[AtomicUsize]>,
58 returned: Event,
59 size: usize,
60 alignment: usize,
61}
62
63unsafe impl Sync for Slab {}
65
66impl HeapBufferPool {
67 pub fn new(buffers: usize, buffer_size: usize) -> io::Result<Self> {
68 Self::with_options(crate::BufferPoolOptions::new(buffers, buffer_size))
69 }
70
71 pub fn with_options(options: crate::BufferPoolOptions) -> io::Result<Self> {
73 options.validate()?;
74 let crate::BufferPoolOptions {
75 buffers,
76 buffer_size,
77 alignment,
78 } = options;
79 let stride = (buffer_size + alignment - 1) & !(alignment - 1);
80 let allocation_size = (buffers - 1) * stride + buffer_size + alignment - 1;
81 let bytes = vec![0u8; allocation_size].into_boxed_slice();
82 let offset = (bytes.as_ptr() as usize).wrapping_neg() & (alignment - 1);
83 let bytes = unsafe { Box::from_raw(Box::into_raw(bytes) as *mut [UnsafeCell<u8>]) };
85 let returned = Event::new();
86 returned.notify(0);
87 let free = (0..buffers.div_ceil(WIDTH))
88 .map(|word| {
89 let bits = (buffers - word * WIDTH).min(WIDTH);
90 AtomicUsize::new((1 << bits) - 1)
91 })
92 .collect();
93 let slab = Arc::new(Slab {
94 slots: (0..buffers)
95 .map(|_| Slot {
96 readers: AtomicUsize::new(0),
97 })
98 .collect(),
99 bytes,
100 offset,
101 stride,
102 free,
103 returned,
104 size: buffer_size,
105 alignment,
106 });
107 for _ in 0..buffers {
109 let _ = Arc::into_raw(Arc::clone(&slab));
110 }
111 Ok(Self(Arc::new(Pool {
112 slab,
113 waiters: Mutex::new(0),
114 })))
115 }
116
117 pub fn capacity(&self) -> usize {
118 self.0.slab.slots.len()
119 }
120 pub fn buffer_size(&self) -> usize {
121 self.0.slab.size
122 }
123
124 pub fn alignment(&self) -> usize {
125 self.0.slab.alignment
126 }
127
128 pub fn try_acquire(&self) -> Option<BufferLease> {
130 for (word_index, word) in self.0.slab.free.iter().enumerate() {
131 let mut available = word.load(Ordering::Relaxed);
132 while available & AVAILABLE != 0 {
133 let bit = 1 << (available & AVAILABLE).trailing_zeros();
134 match word.compare_exchange_weak(
135 available,
136 available & !bit,
137 Ordering::Acquire,
138 Ordering::Relaxed,
139 ) {
140 Ok(_) => {
141 let index = word_index * WIDTH + bit.trailing_zeros() as usize;
142 let slab =
144 unsafe { NonNull::new_unchecked(Arc::as_ptr(&self.0.slab).cast_mut()) };
145 return Some(BufferLease::new(slab, index, self.0.slab.size));
146 }
147 Err(current) => available = current,
148 }
149 }
150 }
151 None
152 }
153
154 pub async fn acquire(&self, cancellation: &Cancellation) -> Result<BufferLease, Cancelled> {
156 cancellation.check()?;
157 if let Some(buffer) = self.try_acquire() {
158 return Ok(buffer);
159 }
160 let mut cancelled = pin!(cancellation.cancelled());
161 let _waiting = PoolWaiter::new(&self.0);
162 loop {
163 event_listener::listener!(self.0.slab.returned => listener);
164 if let Some(buffer) = self.try_acquire() {
165 return Ok(buffer);
166 }
167 let mut listener = pin!(listener);
168 poll_fn(|context| {
169 if cancelled.as_mut().poll(context).is_ready() {
170 return Poll::Ready(Err(Cancelled));
171 }
172 listener.as_mut().poll(context).map(|_| Ok(()))
173 })
174 .await?;
175 }
176 }
177}
178
179impl Drop for Pool {
180 fn drop(&mut self) {
181 for word in &self.slab.free {
183 let mut available = word.swap(CLOSED, Ordering::AcqRel) & AVAILABLE;
184 while available != 0 {
185 available &= available - 1;
186 unsafe { Arc::decrement_strong_count(Arc::as_ptr(&self.slab)) };
188 }
189 }
190 }
191}
192
193struct PoolWaiter<'a>(&'a Pool);
194
195impl<'a> PoolWaiter<'a> {
196 fn new(pool: &'a Pool) -> Self {
197 let mut waiters = pool.waiters.lock().unwrap();
198 if *waiters == 0 {
199 for word in &pool.slab.free {
200 word.fetch_or(WAITING, Ordering::Release);
201 }
202 }
203 if *waiters >= COUNT_LIMIT {
204 abort();
205 }
206 *waiters += 1;
207 Self(pool)
208 }
209}
210
211impl Drop for PoolWaiter<'_> {
212 fn drop(&mut self) {
213 let mut waiters = self.0.waiters.lock().unwrap();
214 *waiters -= 1;
215 if *waiters == 0 {
216 for word in &self.0.slab.free {
217 word.fetch_and(!WAITING, Ordering::Release);
218 }
219 }
220 }
221}
222
223impl Slab {
224 pub(crate) fn pointer(&self, index: usize) -> *mut u8 {
225 assert!(index < self.slots.len());
226 unsafe {
228 self.bytes
229 .as_ptr()
230 .cast::<u8>()
231 .cast_mut()
232 .add(self.offset + index * self.stride)
233 }
234 }
235
236 pub(crate) unsafe fn release(slab: NonNull<Self>, index: usize) {
240 let word = unsafe { &slab.as_ref().free[index / WIDTH] };
242 let bit = 1 << (index % WIDTH);
243 let mut state = word.load(Ordering::Relaxed);
244 let mut notification = None;
245 loop {
246 if state & CLOSED != 0 {
247 unsafe { Arc::decrement_strong_count(slab.as_ptr()) };
249 return;
250 }
251 if state & WAITING != 0 && notification.is_none() {
252 unsafe {
254 Arc::increment_strong_count(slab.as_ptr());
255 notification = Some(Arc::from_raw(slab.as_ptr()));
256 }
257 }
258 match word.compare_exchange_weak(
259 state,
260 state | bit,
261 Ordering::Release,
262 Ordering::Relaxed,
263 ) {
264 Ok(_) => break,
265 Err(current) => state = current,
266 }
267 }
268 if let Some(slab) = notification {
270 slab.returned.notify_additional(1);
271 }
272 }
273
274 pub(crate) fn retain(&self, index: usize) {
275 if self.slots[index].readers.fetch_add(1, Ordering::Relaxed) >= COUNT_LIMIT {
277 abort();
278 }
279 }
280
281 pub(crate) unsafe fn release_reader(slab: NonNull<Self>, index: usize) {
282 if unsafe {
284 slab.as_ref().slots[index]
285 .readers
286 .fetch_sub(1, Ordering::AcqRel)
287 } == 1
288 {
289 unsafe { Self::release(slab, index) };
291 }
292 }
293}
294
295impl crate::BufferPool for HeapBufferPool {
296 type Buffer = BufferLease;
297 fn capacity(&self) -> usize {
298 HeapBufferPool::capacity(self)
299 }
300 fn buffer_size(&self) -> usize {
301 HeapBufferPool::buffer_size(self)
302 }
303 fn alignment(&self) -> usize {
304 HeapBufferPool::alignment(self)
305 }
306 fn try_acquire(&self) -> Option<Self::Buffer> {
307 HeapBufferPool::try_acquire(self)
308 }
309 async fn acquire(&self, cancellation: &Cancellation) -> Result<Self::Buffer, Cancelled> {
310 HeapBufferPool::acquire(self, cancellation).await
311 }
312}
313
314#[cfg(test)]
315mod tests;