setService method
Implementation
@override
Future<void> setService(String serviceName) async {
if (_serviceScopeImpl != null) {
throw Exception('$name: stream scope already attached to a service: ${_serviceScopeImpl!.name}');
}
if (_protocolScopeImpl == null || _peerProtoScope == null) {
throw StateError('$name: stream scope not attached to a protocol before setting service');
}
_logger.fine('$name: Setting service to $serviceName for peer ${_peerScopeImpl.peer}, protocol ${_protocolScopeImpl!.protocol}');
// 1. Get necessary scopes
final newServiceScope = _rcmgr.getServiceScopeInternal(serviceName);
final systemScope = _rcmgr.systemScope;
final limiter = _rcmgr.limiter;
// Explicitly cast to the concrete PeerId type
final newPeerSvcScope = newServiceScope.getPeerSubScope(
_peerScopeImpl.peer,
limiter,
systemScope
);
// 2. Resource Juggling (Reserve in new scopes, no release from prior scopes like transient)
// Streams are typically additive to service scopes on top of protocol scopes.
network_errors.ResourceLimitExceededException? reservationError;
bool reservedInSvc = false;
bool reservedInPeerSvc = false;
try {
try {
newServiceScope.addStream(direction);
reservedInSvc = true;
} on network_errors.ResourceLimitExceededException catch (e) {
_logger.severe('Failed to add stream to new service scope - $direction - $e') ;
reservationError = e;
}
// Other exceptions will propagate up
if (reservationError == null) {
try {
newPeerSvcScope.addStream(direction);
reservedInPeerSvc = true;
} on network_errors.ResourceLimitExceededException catch (e) {
_logger.severe('Failed to add stream to new peer service scope - $direction - $e') ;
reservationError = e;
}
// Other exceptions will propagate up
}
if (reservationError == null) {
_serviceScopeImpl = newServiceScope;
_peerSvcScope = newPeerSvcScope;
// Update edges: Stream is now primarily parented by its peer, its peer-protocol scope, and its peer-service scope.
// Propagation to global protocol, global service, and system scopes will occur from these.
List<ResourceScopeImpl> oldEdges = List.from(this.edges);
List<ResourceScopeImpl> newEdges = [
_peerScopeImpl,
_peerProtoScope!, // Known to be non-null from check above
newPeerSvcScope
];
for (var oldEdge in oldEdges) {
if (!newEdges.contains(oldEdge)) {
oldEdge.decRef();
}
}
for (var newEdge in newEdges) {
if (!oldEdges.contains(newEdge)) {
newEdge.incRef();
}
}
this.edges = newEdges;
_logger.fine('$name: Successfully set service to $serviceName. Edges updated.');
}
} on network_errors.ResourceLimitExceededException catch (e) {
_logger.severe('Resource limits exceeded - $e') ;
reservationError = e;
} catch (e) {
_logger.severe('$name: Unexpected error during setService resource juggling: $e');
if (e is Exception && reservationError == null) {
throw Exception('$name: Failed to set service due to an unexpected error: $e');
}
}
if (reservationError != null) {
if (reservedInPeerSvc) newPeerSvcScope.removeStream(direction);
if (reservedInSvc) newServiceScope.removeStream(direction);
_logger.fine('$name: Failed to reserve resources for service $serviceName: $reservationError.');
throw reservationError;
}
}