- bufferRing: add consumeFront() method to eliminate duplicate buffer
consumption code in tryReadV1/tryReadV2, move allocator.Put() outside
critical section to reduce lock hold time
- Session.IsClosed(): replace select-based check with atomic flag for
faster hot path execution, add 'closed' field with atomic operations
- shaperQueue: convert count to atomic int64, Len() and IsEmpty() now
lock-free using atomic.LoadInt64()
- recvLoop: move stream.fin() and stream.update() calls outside lock
scope in cmdFIN/cmdUPD handlers to reduce lock contention
- stream: extract checkWriteClosed() helper to deduplicate close checks
in writeV1/writeV2
All tests pass with race detector enabled.
Add CloseWrite() method to enable TCP-like half-close semantics without
protocol changes. This allows one side to signal EOF while still receiving
data from the peer.
Changes:
- Add chWriteClosed channel to track local write-side closure
- Add CloseWrite() method that sends FIN but keeps read side open
- Modify writeV1/writeV2 to check local write state instead of remote FIN
- Update Close() to also close write side for backward compatibility
The existing cmdFIN protocol command is reused - CloseWrite sends FIN to
signal write completion while the stream remains readable.
Usage:
stream.Write(data)
stream.CloseWrite() // send FIN, can still read
io.ReadAll(stream) // read peer's response
- Add writeRequestPool to reuse writeRequest objects and their result
channels in session.go, reducing allocations per write operation
- Add shaperHeapPool to reuse shaperHeap objects in shaper.go, avoiding
heap allocations when streams are created/destroyed
- Pre-allocate buffers and heads slices with capacity 8 in newStream()
to reduce slice growth allocations during data transfer
Benchmark results (128KB data transfer):
Before: 36 allocs/op, ~910 MB/s
After: 20 allocs/op, ~950 MB/s
This reduces memory allocations by 44% and improves throughput by ~5%.
cmdUPD is a v2-only command used for flow control window updates.
Previously, recvLoop would accept and process cmdUPD even when
the session was configured for protocol version 1, which violates
the protocol specification.
This change adds a version check before processing cmdUPD. If the
session is not running v2, it now correctly returns ErrInvalidProtocol
and terminates the receive loop.