pub struct BufPool { /* private fields */ }Expand description
An implementation of a low-latency memory-budgeted buffer pool managing fixed-sized buffer allocations
§Blocking drop
Dropping the BufPool instance from memory blocks unitl all the allocated instances of
[BufPoolAllocations] and all there references are dropped from memory.
This is in place to avoid memory leaks, as well as to enable sending BufPoolAllocation
objects across threads while being tied to the lifecyle of BufPool.
§Memory Allocations
All the allocations are allocated using the global memory allocator as requested (on-the-fly).
While a single allocation retunred as BufPoolAllocation is a large contineous slice of
memory w/ size as BufPoolCfg::buffer_size.bytes() * n_buffers.
Memory layout structure,
allocation = [[buf0][buf1][buf2]]
where,
- every buffer is of size `buffer_size`
- each buffer is pointed using `*mut u8`§Backpressure
Every allocation reserves a memory budget and is only allowed to allocate memory if enough budget (i.e. memory space) is available. Otherwise, the caller is blocked/polled till enough space is available.
When the BufPoolAllocation and all its references are dropped, the underlying memory is
deallocated while relaxing the budget and dropping the backpressure (if any).
NOTE: There is no faireness guarantee for the caller’s who are polled when faced with backpressure, as the waiting callers are awaken opportunistically.
§Example
use frozen_core::{
bufpool::{BufPool, BufPoolCfg, BufferPointer},
utils::BufferSize,
};
const BUF_SIZE: BufferSize = BufferSize::S16;
let pool = BufPool::new(BufPoolCfg {
buffer_size: BUF_SIZE,
max_memory: BUF_SIZE as usize * 0x40,
});
let alloc = pool.allocate(0x30);
assert_eq!(alloc.length(), 0x30);
assert!(!alloc.first().is_null());
assert_eq!(alloc.allocated_bytes(), BUF_SIZE as usize * 0x30);
let ptrs: Vec<BufferPointer> = alloc.iter().collect();
assert_eq!(ptrs.len(), 0x30);Implementations§
Source§impl BufPool
impl BufPool
Sourcepub fn new(cfg: BufPoolCfg) -> Self
pub fn new(cfg: BufPoolCfg) -> Self
Create a new instance of BufPool
§Debug Assertions
In debug builds, this function uses debug_assertion to prevent invalid configurations.
Caller must refer to BufPoolCfg for details about the config params.
§Example
use frozen_core::{
bufpool::{BufPool, BufPoolCfg},
utils::BufferSize,
};
const BUF_SIZE: BufferSize = BufferSize::S16;
let pool = BufPool::new(BufPoolCfg {
buffer_size: BUF_SIZE,
max_memory: BUF_SIZE as usize * 0x0A,
});
let alloc = pool.allocate(1);
assert_eq!(alloc.length(), 1);
assert_eq!(alloc.allocated_bytes(), BUF_SIZE as usize);Sourcepub fn allocate(&self, required: usize) -> BufPoolAllocation
pub fn allocate(&self, required: usize) -> BufPoolAllocation
Allocate required number of buffers each of BufPoolCfg::buffer_size size
§Allocation Layout
The allocation is a large contineous slice of memory w/ size as
BufPoolCfg::buffer_size.bytes() * n_buffersMemory layout structure,
allocation = [[buf0][buf1][buf2]]
where,
- every buffer is of size `buffer_size`
- each buffer is pointed using `*mut u8`§RAII Safety
The allocation object, i.e. BufPoolAllocation, is RAII safe. The allocated memory is
automatically deallocated as soon as the last reference to the object is dropped, while also
relaxing the memory budget to eliminate backpressure (if any).
§Important Considerations
The number of buffers required should never exceed u16::MAX. This is an abstract soft
limit and should be enforced by the public interface to avoid any weird exhaustion issues
or unknown bugs across the storage system.
As u16::MAX is large enough value to almost never cause any problems for a single write
operation, this soft limit acts as a guidline to safely operate with arithmatic operations
across storage engine(s), including but not limited to [frozen_core].
§Example
use frozen_core::{
bufpool::{BufPool, BufPoolCfg, BufferPointer},
utils::BufferSize,
};
const BUF_SIZE: BufferSize = BufferSize::S16;
let pool = BufPool::new(BufPoolCfg {
buffer_size: BUF_SIZE,
max_memory: BUF_SIZE as usize * 0x14,
});
let alloc = pool.allocate(0x0A);
assert_eq!(alloc.length(), 0x0A);
assert!(!alloc.first().is_null());
assert_eq!(alloc.allocated_bytes(), BUF_SIZE as usize * 0x0A);
let ptrs: Vec<BufferPointer> = alloc.iter().collect();
assert_eq!(ptrs.len(), 0x0A);