Implement network manager simulator lifecycle (#53)
All checks were successful
Native code generation / deterministic (push) Successful in 8m55s
Imaging and meshing gate / native (push) Successful in 2m45s
JPEG 2000 feature / linux (push) Successful in 1m41s
Native Rust workspace compile / compile (push) Successful in 1m57s
Skia feature / linux (push) Successful in 31m12s
All checks were successful
Native code generation / deterministic (push) Successful in 8m55s
Imaging and meshing gate / native (push) Successful in 2m45s
JPEG 2000 feature / linux (push) Successful in 1m41s
Native Rust workspace compile / compile (push) Successful in 1m57s
Skia feature / linux (push) Successful in 31m12s
This commit is contained in:
79
docs/network-manager.md
Normal file
79
docs/network-manager.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Native network manager and simulator lifecycle
|
||||
|
||||
`NetworkManager`, `Simulator`, `PacketEventDictionary`, and
|
||||
`CapsEventDictionary` implement the native Rust boundary corresponding to the
|
||||
LibreMetaverse connection and dispatch layer. They use the generated packet
|
||||
codecs and the bounded `UDPBase` transport; they do not invoke .NET code or
|
||||
start a helper process.
|
||||
|
||||
## Ownership and execution
|
||||
|
||||
A manager owns bounded incoming and outgoing channels plus one keepalive
|
||||
worker. Each simulator owns a `UDPBase` transport running on a dedicated
|
||||
current-thread Tokio executor so the mapped synchronous connection API does not
|
||||
depend on the caller already having entered a runtime. Async connection methods
|
||||
move blocking compatibility work to short-lived named standard threads and
|
||||
return their result through a runtime-neutral one-shot future. Every worker
|
||||
holds only a weak manager reference between operations. Dropping the last
|
||||
manager and subscription guard therefore releases all manager-owned
|
||||
`GridClient` handles, cancels the workers, joins their threads, and closes
|
||||
simulator transports.
|
||||
|
||||
The public `SimulatorCollection` is a shared snapshot collection. Lookup,
|
||||
addition, removal, current-simulator selection, and disconnect transitions are
|
||||
synchronized. User callbacks receive cloned handles only after internal locks
|
||||
have been released. `Subscription` is an RAII guard: dropping or closing it
|
||||
removes exactly its registration through a weak registry reference.
|
||||
|
||||
## Dispatch semantics
|
||||
|
||||
Packet callbacks preserve registration order. `PacketType::Default` handlers
|
||||
are selected before handlers for the concrete packet type. Once any callback
|
||||
registered for a packet type requests asynchronous dispatch, that packet type
|
||||
remains asynchronous after later removals, matching the conservative C#
|
||||
delegate state. A bounded worker executes each asynchronous default/specific
|
||||
batch in order. Callback panics are isolated and cannot terminate a processor
|
||||
or poison a callback registry.
|
||||
|
||||
CAPS dispatch snapshots the default (`""`) and named callback chains, then
|
||||
invokes them in that order without holding the registry lock. The built-in
|
||||
`EnableSimulator` handler honors `Agent.MultipleSims`, ignores duplicate
|
||||
endpoints, validates ports, and connects each newly advertised region with its
|
||||
reported handle and dimensions. Incoming `DisableSimulator`, `KickUser`,
|
||||
`RegionHandshake`, `StartPingCheck`, and generic-streaming UDP packets perform
|
||||
their reference lifecycle actions before user dispatch. Ping replies preserve
|
||||
the incoming ping identifier and immediately flush pending acknowledgements
|
||||
when the simulator reports a nonzero `OldestUnacked` sequence.
|
||||
|
||||
## Connection and shutdown behavior
|
||||
|
||||
Connecting reuses an existing endpoint or inserts one simulator atomically,
|
||||
starts the bounded manager workers, raises cancellable `SimConnecting`, sends a
|
||||
reliable `UseCircuitCode`, and waits up to `Timing.LoginTimeout` for its actual
|
||||
protocol ACK. Async compatibility methods use a runtime-neutral blocking
|
||||
bridge, so they can be polled without an ambient Tokio runtime. Circuit-code
|
||||
changes propagate to every tracked simulator. A default connection stores its
|
||||
seed capability, updates `CurrentSim`, raises `SimChanged`, and then raises
|
||||
`SimConnected`. A received region handshake is decoded and answered with the
|
||||
reference `RegionHandshakeReply` flags before the simulator is marked complete.
|
||||
|
||||
The keepalive uses the same two-interval disconnect-candidate transition as the
|
||||
C# timer: traffic clears the candidate, the first silent interval marks it and
|
||||
sends a ping, and the second shuts the manager down with `NetworkTimeout`.
|
||||
Concurrent disconnect attempts are serialized so one simulator and one global
|
||||
transition are emitted. Per-simulator removal reports `NetworkTimeout`; losing
|
||||
the last simulator reports `SimShutdown`. Explicit shutdown preserves the
|
||||
caller-provided reason/message and sends `CloseCircuit` only for client or
|
||||
network-timeout shutdowns.
|
||||
|
||||
The deterministic tests use loopback fake simulators and require no live grid:
|
||||
|
||||
```sh
|
||||
cargo test -p libremetaverse --test network_manager
|
||||
```
|
||||
|
||||
They cover ACK-gated circuit setup, typed handshake reply/state, ping response,
|
||||
CAPS enable, UDP disable, current/seed selection, callback ordering and
|
||||
filtering, RAII unregistration, runtime-neutral async calls, concurrent
|
||||
registration/removal, reentrant/concurrent disconnect, and keepalive timeout
|
||||
reasons.
|
||||
Reference in New Issue
Block a user