reparent method
Moves this scope under newEdges. Its current resources are counted in
the ancestors it gains and released from the ones it loses; an ancestor
it keeps (the system scope, usually) keeps counting them once. This is
how go-libp2p's SetPeer and SetProtocol juggle resources. Throws, and
changes nothing, if a gained ancestor's limit would be exceeded.
Implementation
void reparent(List<ResourceScopeImpl> newEdges) {
// A scope that is done holds nothing and refers to nothing; moving it
// would only take references that are never dropped. (A stream whose
// remote peer reset it is done before the protocol is set.)
if (_isDone) return;
if (_owner != null) {
throw StateError('$name: a span cannot be reparented');
}
final oldAncestors = _ancestors.toSet();
final newAncestors = _closure(newEdges);
final gained = newAncestors.where((a) => !oldAncestors.contains(a)).toList();
final lost = oldAncestors.where((a) => !newAncestors.contains(a)).toList();
final st = stat;
final reserved = <ResourceScopeImpl>[];
for (final scope in gained) {
final err = scope._isDone ? network_errors.ResourceScopeClosedException() : scope._resources.reserveStat(st);
if (err != null) {
for (final r in reserved) {
r._releaseResourcesForChild(st);
}
throw _wrapError(err);
}
reserved.add(scope);
}
for (final scope in lost) {
scope._releaseResourcesForChild(st);
}
for (final e in edges) {
if (!newEdges.contains(e)) e.decRef();
}
for (final e in newEdges) {
if (!edges.contains(e)) e.incRef();
}
edges = List.of(newEdges);
}