booblik

Transport

Generated page

Model gemma-mtp, commit f508a4b65b3f, 2026-08-15, sources: 6. Edit the code or the hand-written documentation instead.

Diagram

Transport modes

The server supports two distinct transport mechanisms, which are configured via ServerConfig in BooblikServer.kt:45-64.

ModeMechanismImplementation
SELECTORNon-blocking NIO with a dedicated selector thread and coroutine suspension.SelectorConnection
VIRTUAL_THREADSBlocking sockets where each session runs on a virtual thread.BlockingConnection

SelectorLoop mechanics

The SelectorLoop is the engine that turns NIO readiness into coroutine suspension. It operates on a dedicated thread to avoid deadlocks during SelectionKey.register calls on certain JDK implementations (SelectorLoop.kt:48-55).

To ensure thread safety, interest changes are not applied directly from other threads. Instead, they are posted to a ConcurrentLinkedQueue and applied by the loop thread itself (SelectorLoop.kt:94-96). Every change triggers a selector.wakeup() to ensure the loop processes the pending actions immediately (SelectorLoop.kt:95-96).

SelectorConnection and suspension

SelectorConnection implements the Connection interface by using the SelectorLoop to suspend coroutines when I/O is not ready. When readFully or writeFully encounters a zero-byte operation, it calls loop.awaitReadable(key) or loop.awaitWritable(key) (Connection.kt:65-75). This suspends the session coroutine, freeing up the thread until the SelectorLoop detects readiness and resumes the CancellableContinuation (SelectorLoop.kt:80-81).

BlockingConnection and virtual threads

BlockingConnection serves as the baseline for performance measurements (Connection.kt:100-102). While virtual threads make blocking code efficient, they do not virtualize disk I/O. Consequently, a transferTo call that triggers a page fault can pin the carrier thread to the disk, potentially stalling the executor (Connection.kt:104-106).

PartialFrame assembly

The protocol requires full frames to be assembled from the stream. The PartialFrameTest verifies that the selector correctly handles frames split across multiple packets (PartialFrameTest.kt:40-56) and even frames delivered one byte at a time (PartialFrameTest.kt:66-76). This ensures the SelectorConnection correctly waits for more data via loop.awaitReadable when a read returns zero (Connection.kt:66).

Connection failure and frame corruption

The server is designed to be resilient to malformed data. If a client sends an unserviceable request (e.g., an unsupported version), the broker responds with an ErrorCode but keeps the connection alive (PartialFrameTest.kt:80-109). However, an "absurd" frame length—such as a request claiming a massive size—is treated as a fatal error that drops the connection to protect the broker's memory (PartialFrameTest.kt:112-129).

Key files

FileLinesWhat is there
…/net/BooblikServer.kt25-34Transport enum defining the two execution models.
…/nio/Connection.kt55-142Connection interface and its two implementations.
…/nio/SelectorLoop.kt31-41The dedicated thread and management of the Selector.
…/net/PartialFrameTest.kt29-175Tests for split packets and frame assembly.

Behaviour that surprises

  • Interest changes are asynchronous: Because SelectionKey.interestOps can block or be ineffective during a select() call, SelectorLoop must queue changes and use selector.wakeup() to apply them safely (SelectorLoop.kt:26-28).
  • Disk I/O pins carrier threads: Even when using virtual threads, BlockingConnection can cause a carrier thread to stall if transferTo encounters a page fault during disk I/O (Connection.kt:104-106).
  • Zero-copy requires the raw channel: Wrapping a SocketChannel in a decorator can silently turn a sendfile path into a copy loop, making transferTarget a critical property for performance (Connection.kt:28-30).

On this page