buildInstances<TSubsystem extends Subsystem> static method
- List<
BuildServiceParameters> paramsList, { - bool atomicBatch = false,
Builds and initializes a batch of subsystems defined by paramsList.
Synchronous subsystems (BuildServiceParameters.isSynchronous = true) are initialized first sequentially to make foundational services available to asynchronous ones.
Partial failure behavior: If a subsystem fails during initialization or
post-initialization, only the failing instance is rolled back (disposed and
removed from the registry). Subsystems that were successfully initialized
earlier in the batch remain active in the registry. Set atomicBatch
to true to roll back all instances from the batch on any failure.
If an error occurs during initialization, the failed instance is disposed, removed from the registry, recorded in lastFailedRegistrations, and a SubsystemInitializationException is thrown.
Implementation
static Future<List<TSubsystem>> buildInstances<TSubsystem extends Subsystem>(
List<BuildServiceParameters> paramsList, {
bool atomicBatch = false,
}) async {
final instances = <TSubsystem, BuildServiceParameters>{};
final asyncInitInstances = <TSubsystem>[];
// Phase 1: Construction
for (final params in paramsList) {
if (await params.shouldBuild == false) {
continue;
}
TSubsystem? instance;
try {
instance = await _buildInstance<TSubsystem>(params);
} catch (error, stackTrace) {
_failedRegistrations.add(params);
if (atomicBatch) {
await _rollbackBatch(instances);
}
throw SubsystemInitializationException(
message: 'Failed to construct subsystem',
parameters: params,
cause: error,
stackTrace: stackTrace,
);
}
if (instance != null) {
instances[instance] = params;
if (params.isSynchronous) {
try {
await instance._initSubsystem(params);
} catch (error, stackTrace) {
_failedRegistrations.add(params);
await _cleanupFailedInstance(instance, params);
if (atomicBatch) {
await _rollbackBatch(instances);
}
throw SubsystemInitializationException(
message: 'Failed during synchronous initialization',
parameters: params,
cause: error,
stackTrace: stackTrace,
);
}
} else {
asyncInitInstances.add(instance);
}
}
}
// Phase 2: Asynchronous initialization
if (asyncInitInstances.isNotEmpty) {
final results = await Future.wait(
asyncInitInstances.map((instance) async {
final params = instances[instance]!;
try {
await instance._initSubsystem(params);
return null;
} catch (error, stackTrace) {
return _InitError(instance: instance, params: params, error: error, stackTrace: stackTrace);
}
}),
);
final firstError = results.whereType<_InitError>().firstOrNull;
if (firstError != null) {
_failedRegistrations.add(firstError.params);
await _cleanupFailedInstance(firstError.instance, firstError.params);
if (atomicBatch) {
await _rollbackBatch(instances);
}
throw SubsystemInitializationException(
message: 'Failed during asynchronous initialization',
parameters: firstError.params,
cause: firstError.error,
stackTrace: firstError.stackTrace,
);
}
}
// Phase 3: Post-initialization
for (final entry in instances.entries) {
final instance = entry.key;
final params = entry.value;
try {
await instance._postInitSubsystem(params);
} catch (error, stackTrace) {
_failedRegistrations.add(params);
await _cleanupFailedInstance(instance, params);
if (atomicBatch) {
await _rollbackBatch(instances);
}
throw SubsystemInitializationException(
message: 'Failed during post-initialization',
parameters: params,
cause: error,
stackTrace: stackTrace,
);
}
}
return instances.keys.toList();
}