setPeer method
Implementation
@override
Future<void> setPeer(PeerId peerId) async {
if (_peerScopeImpl != null) {
throw Exception('$name: connection scope already attached to a peer: ${_peerScopeImpl!.name}');
}
_logDebug('$name: Setting peer to $peerId');
// 1. Get PeerScope from the ResourceManager.
// Note: _rcmgr._getPeerScope is not public, but ConnectionScopeImpl is in the same library.
// A cleaner way might be for ResourceManagerImpl to expose a method like `internalGetPeerScope`.
// For now, direct access is assumed as they are tightly coupled.
final newPeerScope = _rcmgr.getPeerScopeInternal(peerId);
// 2. Identify original transient scope and get the global system scope.
// ConnectionScopeImpl is initially parented only by the transient scope.
if (edges.isEmpty || edges[0] is! TransientScopeImpl) {
_logDebug('$name: Initial Edges: ${edges.map((e) => e.name).join(', ')}');
throw StateError('$name: Expected initial parent to be TransientScopeImpl.');
}
final transientScope = edges[0] as TransientScopeImpl;
// Get the system scope from the resource manager.
// This assumes _rcmgr.systemScope provides the correct SystemScopeImpl instance.
final systemScope = _rcmgr.systemScope;
// 3. Resource Juggling
// Get current stats of this connection scope.
// These resources were initially reserved against the transient scope (and system).
// final currentStats = stat; // Not directly needed if addConn/removeConn handle their own accounting.
network_errors.ResourceLimitExceededException? reservationError;
try {
// Reserve in the new peer scope.
// addConn will attempt to reserve resources and propagate to its parents (system).
try {
newPeerScope.addConn(direction, useFd);
} on network_errors.ResourceLimitExceededException catch (e) {
reservationError = e;
}
// Other exceptions will propagate up and be caught by the outer try-catch
if (reservationError == null) {
// If reservation in peer scope is successful, release from transient scope.
// removeConn will release resources and propagate to its parents (system).
transientScope.removeConn(direction, useFd);
// Note: Memory associated with the connection is handled by addConn/removeConn internally.
// Update internal state and edges
_peerScopeImpl = newPeerScope;
// The connection scope is now parented by the specific peer scope and the global system scope.
// The peer scope itself is parented by the system scope.
this.edges = [newPeerScope, systemScope];
// Decrement ref count of transient scope as this connection is no longer its direct child for these resources.
// This is tricky: the transient scope itself is a long-lived scope.
// The resources are moved, not the scope itself being "done".
// The original `addConn` on `this` (ConnectionScopeImpl) during `ResourceManagerImpl.openConnection`
// reserved against its initial parents (transient, system).
// Calling `transientScope.removeConn` correctly decrements the counts on the transient scope
// and its parent (system).
// The ref counts on transient/system scopes are managed by their lifecycle, not per-resource juggling.
_logDebug('$name: Successfully set peer to $peerId. Resources transferred from transient to peer scope.');
}
} on network_errors.ResourceLimitExceededException catch (e) {
reservationError = e;
}
if (reservationError != null) {
// Failed to reserve in peer scope.
// Rollback is tricky. The connection is already open.
// The Go version might close the connection here or mark it as unmanaged.
// For now, we'll throw, indicating failure to associate with peer.
// The caller (likely network layer) would then decide to close the connection.
_logDebug('$name: Failed to reserve resources in peer scope for $peerId: $reservationError. Connection may need to be closed.');
throw reservationError;
}
}