close method
Closes the network
Implementation
@override
Future<void> close() async {
await _closedLock.synchronized(() async {
if (_isClosed) return;
_isClosed = true;
// Close all listeners
final listenersToClose = List<Listener>.from(_listeners); // Create a copy
for (final listener in listenersToClose) {
await listener.close(); // This might trigger onDone/onError, modifying original _listeners
}
_listeners.clear(); // Clear original list after all are processed
// Close all connections
await _connLock.synchronized(() async {
final allConnsToClose = <SwarmConn>[];
// Iterate over a copy of values if modification during iteration is possible
// or clear the map after collecting all connections.
final connectionLists = List<List<SwarmConn>>.from(_connections.values);
for (final connsList in connectionLists) {
allConnsToClose.addAll(connsList);
}
// It's safer to clear the original map after collecting all items if conn.close()
// could lead to re-entrant calls that modify _connections.
// However, if conn.close() only triggers notifiees that don't modify _connections directly,
// iterating _connections.values and then _connections.clear() might be okay.
// For max safety, let's clear after collecting.
// _connections.clear(); // Moved down
for (final conn in allConnsToClose) {
await conn.close();
}
_connections.clear(); // Clear after all connections are processed and closed.
});
// Notify all notifiees about closed listeners and connections
await _notifieeLock.synchronized(() async {
// Create copies of lists to iterate over, to prevent concurrent modification
final currentListenAddrs = List<MultiAddr>.from(_listenAddrs);
// For connections, we need a deep enough copy if the inner lists could change.
// However, connections should have been closed and removed from _connections by now.
// The _connections map should be empty here if the above logic is correct.
// Let's assume _notifiees list itself is stable during this block.
final notifieesCopy = List<Notifiee>.from(_notifiees);
for (final notifiee in notifieesCopy) {
// Notify about closed listeners
for (final addr in currentListenAddrs) {
notifiee.listenClose(this, addr);
}
// Notify about closed connections
// Since _connections should be empty, this loop might not run.
// If it can run, we need to be careful.
// The previous block iterates a copy of connections and closes them.
// Here, we should notify based on what *was* closed.
// This notification logic might be better integrated into the actual closing loops.
// For now, let's assume this is for any remaining state.
// The original code iterated _connections.values directly.
// If _connections is cleared above, this loop is problematic.
// Re-thinking: The notification for disconnected should happen when a conn is actually closed.
// SwarmConn.close() calls swarm.removeConnection(), which calls notifiee.disconnected.
// So, this explicit loop here might be redundant or even racy if removeConnection also iterates _notifiees.
// Let's stick to notifying about listenClose for addresses that were active.
// The disconnected notifications are handled by removeConnection.
}
});
await _transportsLock.synchronized(() async {
for (final transport in _transports){
await transport.dispose();
}
});
});
}