setService method

  1. @override
Future<void> setService(
  1. String serviceName
)
override

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;
  }
}