@@ -230,7 +230,11 @@ public protocol PackageContainerProvider {
230
230
associatedtype Container : PackageContainer
231
231
232
232
/// Get the container for a particular identifier asynchronously.
233
- func getContainer( for identifier: Container . Identifier , completion: @escaping ( Result < Container , AnyError > ) -> Void )
233
+ func getContainer(
234
+ for identifier: Container . Identifier ,
235
+ skipUpdate: Bool ,
236
+ completion: @escaping ( Result < Container , AnyError > ) -> Void
237
+ )
234
238
}
235
239
236
240
/// An individual constraint onto a container.
@@ -1039,10 +1043,7 @@ public class DependencyResolver<
1039
1043
1040
1044
// Never prefetch when running in incomplete mode.
1041
1045
if !isInIncompleteMode && isPrefetchingEnabled {
1042
- // Ask all of these containers upfront to do async cloning.
1043
- for constraint in constraints {
1044
- provider. getContainer ( for: constraint. identifier) { _ in }
1045
- }
1046
+ prefetch ( containers: constraints. map ( { $0. identifier } ) )
1046
1047
}
1047
1048
1048
1049
// Update the active constraint set to include all active constraints.
@@ -1137,7 +1138,16 @@ public class DependencyResolver<
1137
1138
// MARK: Container Management
1138
1139
1139
1140
/// The active set of managed containers.
1140
- public private( set) var containers : [ Identifier : Container ] = [ : ]
1141
+ public var containers : [ Identifier : Container ] {
1142
+ return containersLock. withLock {
1143
+ _containers
1144
+ }
1145
+ }
1146
+ private var containersLock = Lock ( )
1147
+ private var _containers : [ Identifier : Container ] = [ : ]
1148
+
1149
+ /// The set of containers requested so far.
1150
+ private var requestedContainers : Set < Identifier > = [ ]
1141
1151
1142
1152
/// Get the container for the given identifier, loading it if necessary.
1143
1153
private func getContainer( for identifier: Identifier ) throws -> Container {
@@ -1152,15 +1162,40 @@ public class DependencyResolver<
1152
1162
1153
1163
/// Add a managed container.
1154
1164
private func addContainer( for identifier: Identifier ) throws -> Container {
1155
- assert ( !containers. keys. contains ( identifier) )
1156
1165
// Get the container synchronously from provider.
1157
- let container = try await { provider. getContainer ( for: identifier, completion: $0) }
1158
- containers [ identifier] = container
1166
+ let skipUpdate = requestedContainers. contains ( identifier)
1167
+ requestedContainers. insert ( identifier)
1168
+ let container = try await { provider. getContainer ( for: identifier, skipUpdate: skipUpdate, completion: $0) }
1169
+
1170
+ return set ( container, for: identifier)
1171
+ }
1159
1172
1160
- // Inform the delegate we are considering a new container .
1161
- delegate . added ( container : identifier )
1173
+ /// Starts prefetching the given containers .
1174
+ private func prefetch ( containers identifiers : [ Identifier ] ) {
1162
1175
1163
- return container
1176
+ // Prefetch the containers which are missing.
1177
+ for identifier in identifiers where !requestedContainers. contains ( identifier) {
1178
+ requestedContainers. insert ( identifier)
1179
+ provider. getContainer ( for: identifier, skipUpdate: false ) {
1180
+ // We ignore the errors here and expect them to be caught later.
1181
+ guard case . success( let container) = $0 else { return }
1182
+ self . set ( container, for: identifier)
1183
+ }
1184
+ }
1185
+ }
1186
+
1187
+ /// Set the container for the given identifier to containers store.
1188
+ ///
1189
+ /// If the container is already present, it is not inserted again and the old
1190
+ /// copy is returned.
1191
+ @discardableResult
1192
+ private func set( _ container: Container , for identifier: Identifier ) -> Container {
1193
+ return containersLock. withLock {
1194
+ if let container = _containers [ identifier] { return container }
1195
+ self . _containers [ identifier] = container
1196
+ self . delegate. added ( container: identifier)
1197
+ return container
1198
+ }
1164
1199
}
1165
1200
}
1166
1201
0 commit comments