Skip to content

Non-spawning API for master and outstation creation #414

Description

@jadamcrain

Problem

The DNP3 library calls tokio::spawn internally in all spawn_* creation functions. This prevents callers from controlling which Tokio runtime or task set the futures are placed on.

Proposed Changes

Scope: TCP and serial transports. Rust API only — no FFI changes.

1. Master: builder + single task type

Add a MasterTask builder that separates configuration from execution:

let task = MasterTask::builder(config)
    .enable()
    .add_association(address, assoc_config, read_handler, assoc_handler, assoc_info)
    .tcp(endpoints, connect_strategy, listener)
    // or .serial(path, settings, retry_delay, listener)

MasterTask is a single concrete type regardless of transport. It exposes:

  • channel(&self) -> &MasterChannel — get the handle for post-spawn use
  • enable(), add_association() — synchronous, direct state mutation (no channel messages)
  • async fn run(self) — consumes the task and runs the event loop

The existing spawn_* functions are refactored to delegate to this, so no behavior changes for current users.

2. Outstation: same pattern

Same approach for outstation TCP client and serial — a single task type with pre-spawn configuration and async fn run(self).

3. Outstation TCP server: accept pre-bound TcpListener

Add a bind_with_listener(self, TcpListener) method that takes an already-bound listener synchronously. Refactor bind_no_spawn() to delegate to it.

Non-goals

  • FFI / language binding changes
  • TLS transport
  • Changes to existing public config types (semver)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions