Pooling Options¶
PoolingOptions configures the bounded object pools used by the network layer to
avoid hot-path allocations during accept, receive, callback dispatch, and timing-wheel
registration.
Source Mapping¶
src/Nalix.Network/Options/PoolingOptions.cssrc/Nalix.Hosting/Bootstrap.cssrc/Nalix.Network/Listeners/TcpListener/TcpListener.Core.cssrc/Nalix.Network/Internal/Transport/SocketConnection.cssrc/Nalix.Network/Internal/Transport/AsyncCallback.cssrc/Nalix.Network/Internal/Time/TimingWheel.cssrc/Nalix.Network/Internal/Pooling/*.cs
Defaults and Validation¶
| Pool | Capacity property | Default | Preallocate property | Default | Validation |
|---|---|---|---|---|---|
| Accept context | AcceptContextCapacity |
4096 |
AcceptContextPreallocate |
20 |
Capacity 1..1_000_000; preallocate 0..1_000_000; preallocate must be <= capacity. |
| Socket async event args | SocketArgsCapacity |
4096 |
SocketArgsPreallocate |
32 |
Capacity 1..1_000_000; preallocate 0..1_000_000; preallocate must be <= capacity. |
| Receive context | ReceiveContextCapacity |
4096 |
ReceiveContextPreallocate |
32 |
Capacity 1..1_000_000; preallocate 0..1_000_000; preallocate must be <= capacity. |
| Timeout task | TimeoutTaskCapacity |
8192 |
TimeoutTaskPreallocate |
64 |
Capacity 1..1_000_000; preallocate 0..1_000_000; preallocate must be <= capacity. |
| Connect event context | ConnectEventContextCapacity |
4096 |
ConnectEventContextPreallocate |
32 |
Capacity 1..1_000_000; preallocate 0..1_000_000; preallocate must be <= capacity. |
Validate() first runs data-annotation validation for every numeric property, then
performs pair-wise checks so no *Preallocate value can exceed its matching
*Capacity value.
Hosting Initialization¶
Bootstrap.Initialize() materializes this option set during server startup:
This ensures the active configuration file includes the network pool sizing knobs even before the TCP listener, callback dispatcher, or timing wheel are constructed.
Runtime Initialization Flow¶
flowchart TD
A["Bootstrap.Initialize()"] --> B["Load PoolingOptions into active config"]
C["TcpListenerBase constructor"] --> D["Validate PoolingOptions"]
D --> E["Set capacity: PooledAcceptContext"]
D --> F["Set capacity: PooledSocketAsyncEventArgs"]
E --> G["Prealloc accept contexts"]
F --> H["Prealloc socket args"]
I["AsyncCallback static constructor"] --> J["Validate PoolingOptions"]
J --> K["Set capacity: PooledConnectEventContext"]
K --> L["Prealloc callback wrappers"]
M["TimingWheel constructor"] --> N["Validate PoolingOptions"]
N --> O["Set capacity: TimeoutTask"]
O --> P["Prealloc timeout tasks"]
The option object can therefore be validated multiple times by different subsystems. Validation is deterministic and only reads the configured values.
Pool Consumers¶
Accept Context Pool¶
TcpListenerBase configures PooledAcceptContext capacity and preallocation during
listener construction. Each accept-loop worker rents an accept context while an
AcceptAsync operation is in flight. The default AcceptContextPreallocate = 20
matches the typical default accept-worker count.
Socket Async Event Args Pool¶
TcpListenerBase also configures PooledSocketAsyncEventArgs. These SAEA wrappers
are shared by accept and receive paths. PooledSocketReceiveContext.EnsureArgsBound()
lazily rents a PooledSocketAsyncEventArgs when a receive context first needs one.
Receive Context Pool¶
SocketConnection rents one PooledSocketReceiveContext when the connection starts
and keeps it for the receive loop. The context wraps a reusable
ManualResetValueTaskSourceCore<int> so asynchronous receives avoid per-call
TaskCompletionSource allocations, while synchronous completions return through the
ValueTask<int> fast path.
Receive context capacity
The current source defines ReceiveContextCapacity and
ReceiveContextPreallocate, but no source path currently calls
SetMaxCapacity<PooledSocketReceiveContext>(...) or
Prealloc<PooledSocketReceiveContext>(...). The receive context is still pooled
through ObjectPoolManager, but these two knobs are not wired to a runtime
initializer in the audited source.
Timeout Task Pool¶
TimingWheel validates PoolingOptions, configures TimeoutTaskCapacity, and
preallocates TimeoutTaskPreallocate. Each active timing-wheel registration owns one
TimeoutTask until the connection disconnects, is refreshed, or times out.
Connect Event Context Pool¶
AsyncCallback loads PoolingOptions in its static constructor, configures the
PooledConnectEventContext capacity, and preallocates callback wrappers. Normal and
high-priority connection callbacks use these wrappers before queuing work to the
ThreadPool. The wrapper is returned in EXECUTE_AND_RETURN(...) after the callback
runs.
Ownership and Return Contracts¶
- Capacity controls the maximum number of returned objects retained by
ObjectPoolManager; objects beyond the ceiling are discarded and later collected. - Preallocation eagerly creates reusable instances at subsystem startup to reduce the first-use allocation spike.
- Pooled objects implement
IPoolableand reset their internal state before reuse. PooledSocketReceiveContext.ResetForPool()waits for in-flight SAEA work to settle before returning the underlyingPooledSocketAsyncEventArgs; if the SAEA remains busy, it is intentionally not returned to the pool to avoid buffer corruption.PooledConnectEventContextis returned after callback execution, and optionally releases the owning connection's pending-packet slot.
Tuning Guidance¶
- Set capacities to peak concurrent usage plus headroom, not to total historical traffic volume.
- Keep preallocation near steady-state warm usage. Very high preallocation reduces early allocations but increases startup memory and initialization time.
- Align
AcceptContextCapacityandAcceptContextPreallocatewith TCP listener accept-worker count. - Size
SocketArgsCapacityfor accept workers plus peak concurrent receive contexts, because accept and receive paths share the SAEA pool. - Size
TimeoutTaskCapacityat least as high as expected active timing-wheel registrations; use additional headroom under bursty connect/disconnect traffic. - Size
ConnectEventContextCapacitywithNetworkCallbackOptionslimits so callback wrappers do not churn under normal backpressure settings.