Compatibility constraint
add_outstation_no_spawn and bind_no_spawn were released in DNP3 1.6.0. This work must not change or remove those APIs.
All new types and entry points are additive and gated by the unstable feature. The existing 1.6.0 APIs retain their current signatures and behavior.
Problem
The existing TCP outstation no-spawn APIs return opaque futures:
pub fn add_outstation_no_spawn(...)
-> Result<(OutstationHandle, impl Future<Output = ()>), FilterError>
pub async fn bind_no_spawn(self)
-> Result<(ServerHandle, impl Future<Output = Shutdown>), std::io::Error>
These futures cannot be named in a struct field without boxing or type erasure. This is inconvenient for staged lifecycles that configure resources, store prepared tasks, and spawn them later.
A TCP outstation server also consists of independently scheduled tasks: one accept/routing task and one adapter task per outstation. Combining them into one aggregate future would prevent Tokio from scheduling different outstations concurrently across worker threads.
Design
Add two concrete task types behind unstable.
OutstationTask
OutstationTask represents one independently runnable DNP3 outstation. Its private variants support:
- an opened serial outstation;
- one TCP/TLS server outstation adapter.
The serial construction path returns the common task:
pub fn SerialOutstation::open(...)
-> std::io::Result<OutstationTask>
TCP server registration gains an additive method:
pub fn Server::add_outstation_task(...)
-> Result<(OutstationHandle, OutstationTask), FilterError>
TcpServerTask
TcpServerTask represents only the TCP accept and connection-routing loop:
pub fn Server::into_task(
self,
listener: tokio::net::TcpListener,
) -> (ServerHandle, TcpServerTask)
Each task exposes a consuming asynchronous run() method. The caller runs the TCP server task and every outstation task independently, preserving the existing N+1 scheduling model and cross-core parallelism.
The concrete task APIs attach no automatic task-root tracing spans, allowing callers to apply their own instrumentation.
Status
Implemented in PR #433 by commit 55c1b4f.
Compatibility constraint
add_outstation_no_spawnandbind_no_spawnwere released in DNP3 1.6.0. This work must not change or remove those APIs.All new types and entry points are additive and gated by the
unstablefeature. The existing 1.6.0 APIs retain their current signatures and behavior.Problem
The existing TCP outstation no-spawn APIs return opaque futures:
These futures cannot be named in a struct field without boxing or type erasure. This is inconvenient for staged lifecycles that configure resources, store prepared tasks, and spawn them later.
A TCP outstation server also consists of independently scheduled tasks: one accept/routing task and one adapter task per outstation. Combining them into one aggregate future would prevent Tokio from scheduling different outstations concurrently across worker threads.
Design
Add two concrete task types behind
unstable.OutstationTaskOutstationTaskrepresents one independently runnable DNP3 outstation. Its private variants support:The serial construction path returns the common task:
TCP server registration gains an additive method:
TcpServerTaskTcpServerTaskrepresents only the TCP accept and connection-routing loop:Each task exposes a consuming asynchronous
run()method. The caller runs the TCP server task and every outstation task independently, preserving the existing N+1 scheduling model and cross-core parallelism.The concrete task APIs attach no automatic task-root tracing spans, allowing callers to apply their own instrumentation.
Status
Implemented in PR #433 by commit
55c1b4f.OutstationTaskOutstationTaskfrom serial openServer::add_outstation_taskTcpServerTaskfromServer::into_task