Class RegisteredBufferPoolImpl

java.lang.Object
express.mvp.myra.transport.RegisteredBufferPoolImpl
All Implemented Interfaces:
RegisteredBufferPool, AutoCloseable

public final class RegisteredBufferPoolImpl extends Object implements RegisteredBufferPool
Thread-safe pool implementation of pre-registered buffers for zero-copy I/O.

This implementation manages a fixed set of off-heap memory regions that can be pre-registered with the I/O backend (e.g., io_uring's registered buffers). Registration eliminates per-operation kernel address validation, providing approximately 1.7x throughput improvement.

Memory Layout

The pool allocates a single contiguous memory block, then slices it into individual buffers. This design provides several benefits:

  • TLB efficiency: Contiguous memory uses fewer TLB entries
  • Huge page support: Large pools automatically use 2MB huge pages
  • Simpler registration: Single memory region for kernel registration
  • Cache locality: Sequential buffers are cache-line adjacent

Page Alignment

Buffer sizes are aligned to 4KB page boundaries. This ensures:

  • Each buffer starts on a page boundary
  • DMA transfers work correctly for registered buffers
  • No false sharing between adjacent buffers

Thread Safety

This implementation uses an ArrayBlockingQueue for buffer management, providing thread-safe acquire/release with blocking semantics. The queue is lock-based but contention is typically low as I/O operations take much longer than queue operations.

Sizing Recommendations

Recommended Pool Sizes by Use Case
Use CaseBuffer CountBuffer Size
Client (low concurrency)64-1288-16 KB
Server (moderate load)256-51216-32 KB
High-throughput server1024-409632-64 KB
See Also:
  • Constructor Details

    • RegisteredBufferPoolImpl

      public RegisteredBufferPoolImpl(int numBuffers, int bufferSize)
      Creates a new buffer pool with the specified number of buffers and size.

      The actual buffer size may be larger than requested due to page alignment. For example, requesting 5000-byte buffers will allocate 8192 bytes per buffer (rounded up to 2 pages).

      Memory Calculation

      Total memory = numBuffers × alignedBufferSize, where:

      alignedBufferSize = ((bufferSize + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE
      
      Parameters:
      numBuffers - the number of buffers in the pool (must be positive)
      bufferSize - the requested size of each buffer in bytes (must be positive)
      Throws:
      IllegalArgumentException - if numBuffers or bufferSize is not positive
      OutOfMemoryError - if unable to allocate the required memory
  • Method Details

    • acquire

      public RegisteredBuffer acquire()
      Description copied from interface: RegisteredBufferPool
      Acquires a buffer from the pool, blocking if necessary.

      If no buffers are currently available, this method blocks until one is released by another thread. The returned buffer has been cleared (position=0, limit=capacity).

      Specified by:
      acquire in interface RegisteredBufferPool
      Returns:
      a registered buffer ready for use
    • tryAcquire

      public RegisteredBuffer tryAcquire()
      Description copied from interface: RegisteredBufferPool
      Attempts to acquire a buffer without blocking.

      Returns immediately with a buffer if one is available, or null if all buffers are in use. This is useful for non-blocking I/O patterns.

      Specified by:
      tryAcquire in interface RegisteredBufferPool
      Returns:
      a registered buffer, or null if none available
    • release

      public void release(RegisteredBuffer buffer)
      Description copied from interface: RegisteredBufferPool
      Releases a buffer back to the pool for reuse.

      The buffer is automatically cleared (position reset, limit set to capacity) before being made available for the next acquire. This method is also called automatically when RegisteredBuffer.close() is invoked.

      This method is idempotent; releasing an already-released buffer has no effect.

      Specified by:
      release in interface RegisteredBufferPool
      Parameters:
      buffer - the buffer to release; must have been acquired from this pool
    • capacity

      public int capacity()
      Description copied from interface: RegisteredBufferPool
      Returns the total number of buffers in the pool.

      This is the fixed capacity set at pool creation time.

      Specified by:
      capacity in interface RegisteredBufferPool
      Returns:
      the total buffer count
    • available

      public int available()
      Description copied from interface: RegisteredBufferPool
      Returns the number of buffers currently available for acquisition.

      Note: This value may change immediately after the call returns due to concurrent acquire/release operations.

      Specified by:
      available in interface RegisteredBufferPool
      Returns:
      the current available buffer count
    • inUse

      public int inUse()
      Description copied from interface: RegisteredBufferPool
      Returns the number of buffers currently in use.

      This equals capacity() - available(). Note: This value may change immediately after the call returns.

      Specified by:
      inUse in interface RegisteredBufferPool
      Returns:
      the current in-use buffer count
    • getAllBuffers

      public RegisteredBuffer[] getAllBuffers()
      Returns the array of all buffers for backend registration.

      This method is called by the I/O backend during initialization to register all buffers with the kernel. For io_uring, this triggers LibUring.registerBuffers(MemorySegment, MemorySegment, int).

      Note: The returned array should not be modified.

      Returns:
      array of all buffers in the pool (length equals capacity())
    • getBufferSegment

      public MemorySegment getBufferSegment(RegisteredBuffer buffer)
      Returns the raw memory segment backing a buffer.

      This method is used by the I/O backend to access the underlying memory for registration and I/O operations.

      Parameters:
      buffer - the buffer to get the segment from
      Returns:
      the memory segment backing the buffer
    • close

      public void close()
      Description copied from interface: RegisteredBufferPool
      Closes the pool and releases all underlying resources.

      After closing:

      This method blocks until all buffers can be reclaimed. Ensure all I/O operations using buffers from this pool have completed before closing.

      Specified by:
      close in interface AutoCloseable
      Specified by:
      close in interface RegisteredBufferPool