@@ -108,13 +108,13 @@ public actor ServiceGroup: Sendable, Service {
108
108
self . maximumCancellationDuration = configuration. _maximumCancellationDuration
109
109
}
110
110
111
- /// Adds a service to the group.
111
+ /// Adds a new service to the group.
112
112
///
113
113
/// If the group is currently running, the added service will be started immediately.
114
114
/// If the group is gracefully shutting down, cancelling, or already finished, the added service will not be started.
115
115
/// - Parameters:
116
116
/// - serviceConfiguration: The service configuration to add.
117
- public func addService ( _ serviceConfiguration: ServiceGroupConfiguration . ServiceConfiguration ) async {
117
+ public func addServiceUnlessShutdown ( _ serviceConfiguration: ServiceGroupConfiguration . ServiceConfiguration ) async {
118
118
switch self . state {
119
119
case var . initial( services: services) :
120
120
self . state = . initial( services: [ ] )
@@ -125,18 +125,19 @@ public actor ServiceGroup: Sendable, Service {
125
125
await addedServiceChannel. send ( serviceConfiguration)
126
126
127
127
case . finished:
128
+ // Since this is a best effort operation we don't have to do anything here
128
129
return
129
130
}
130
131
}
131
132
132
- /// Adds a service to the group.
133
+ /// Adds a new service to the group.
133
134
///
134
135
/// If the group is currently running, the added service will be started immediately.
135
136
/// If the group is gracefully shutting down, cancelling, or already finished, the added service will not be started.
136
137
/// - Parameters:
137
138
/// - service: The service to add.
138
- public func addService ( _ service: any Service ) async {
139
- await self . addService ( ServiceGroupConfiguration . ServiceConfiguration ( service: service) )
139
+ public func addServiceUnlessShutdown ( _ service: any Service ) async {
140
+ await self . addServiceUnlessShutdown ( ServiceGroupConfiguration . ServiceConfiguration ( service: service) )
140
141
}
141
142
142
143
/// Runs all the services by spinning up a child task per service.
@@ -225,7 +226,7 @@ public actor ServiceGroup: Sendable, Service {
225
226
}
226
227
}
227
228
228
- fileprivate enum ChildTaskResult {
229
+ private enum ChildTaskResult {
229
230
case serviceFinished( service: ServiceGroupConfiguration . ServiceConfiguration , index: Int )
230
231
case serviceThrew( service: ServiceGroupConfiguration . ServiceConfiguration , index: Int , error: any Error )
231
232
case signalCaught( UnixSignal )
@@ -318,8 +319,9 @@ public actor ServiceGroup: Sendable, Service {
318
319
let gracefulShutdownManager = GracefulShutdownManager ( )
319
320
gracefulShutdownManagers. append ( gracefulShutdownManager)
320
321
321
- group. addServiceTask (
322
- serviceConfiguration,
322
+ self . addServiceTask (
323
+ group: & group,
324
+ service: serviceConfiguration,
323
325
gracefulShutdownManager: gracefulShutdownManager,
324
326
index: index
325
327
)
@@ -342,14 +344,14 @@ public actor ServiceGroup: Sendable, Service {
342
344
}
343
345
344
346
// Adds a task that listens to added services and funnels them into the task group
345
- group . addAddedServiceListenerTask ( addedServiceChannel)
347
+ self . addAddedServiceListenerTask ( group : & group , channel : addedServiceChannel)
346
348
347
349
// We are going to wait for any of the services to finish or
348
350
// the signal sequence to throw an error.
349
351
while !group. isEmpty {
350
- let nextEvent = try await group. next ( )
352
+ let result : ChildTaskResult ? = try await group. next ( )
351
353
352
- switch nextEvent {
354
+ switch result {
353
355
case . newServiceAdded( let serviceConfiguration) :
354
356
self . logger. debug (
355
357
" Starting added service " ,
@@ -367,14 +369,18 @@ public actor ServiceGroup: Sendable, Service {
367
369
" Mismatch between services and graceful shutdown managers "
368
370
)
369
371
370
- group. addServiceTask (
371
- serviceConfiguration,
372
+ self . addServiceTask (
373
+ group: & group,
374
+ service: serviceConfiguration,
372
375
gracefulShutdownManager: gracefulShutdownManager,
373
376
index: services. count - 1
374
377
)
375
378
376
379
// Each listener task can only handle a single added service, so we must add a new listener
377
- group. addAddedServiceListenerTask ( addedServiceChannel)
380
+ self . addAddedServiceListenerTask (
381
+ group: & group,
382
+ channel: addedServiceChannel
383
+ )
378
384
379
385
case . serviceFinished( let service, let index) :
380
386
if group. isCancelled {
@@ -782,8 +788,7 @@ public actor ServiceGroup: Sendable, Service {
782
788
break
783
789
784
790
case . newServiceAdded:
785
- // TBD: How do we treat added services during graceful shutdown?
786
- // Currently, we ignore them - but we make sure that `run` is never called
791
+ // Since adding services is best effort, we simply ignore this
787
792
break
788
793
}
789
794
}
@@ -845,17 +850,16 @@ public actor ServiceGroup: Sendable, Service {
845
850
cancellationTimeoutTask = nil
846
851
}
847
852
}
848
- }
849
853
850
- extension ThrowingTaskGroup where Failure == Error , ChildTaskResult == ServiceGroup . ChildTaskResult {
851
- mutating func addServiceTask (
852
- _ serviceConfiguration: ServiceGroupConfiguration . ServiceConfiguration,
854
+ private func addServiceTask (
855
+ group : inout ThrowingTaskGroup < ChildTaskResult , Error > ,
856
+ service serviceConfiguration: ServiceGroupConfiguration. ServiceConfiguration,
853
857
gracefulShutdownManager: GracefulShutdownManager,
854
858
index: Int
855
859
) {
856
860
// This must be addTask and not addTaskUnlessCancelled
857
861
// because we must run all the services for the shutdown logic to work.
858
- self . addTask {
862
+ group . addTask {
859
863
return await TaskLocals . $gracefulShutdownManager. withValue ( gracefulShutdownManager) {
860
864
do {
861
865
try await serviceConfiguration. service. run ( )
@@ -867,8 +871,11 @@ extension ThrowingTaskGroup where Failure == Error, ChildTaskResult == ServiceGr
867
871
}
868
872
}
869
873
870
- mutating func addAddedServiceListenerTask( _ channel: AsyncChannel < ServiceGroupConfiguration . ServiceConfiguration > ) {
871
- self . addTask {
874
+ private func addAddedServiceListenerTask(
875
+ group: inout ThrowingTaskGroup< ChildTaskResult , Error > ,
876
+ channel: AsyncChannel < ServiceGroupConfiguration . ServiceConfiguration >
877
+ ) {
878
+ group. addTask {
872
879
return await withTaskCancellationHandler {
873
880
var iterator = channel. makeAsyncIterator ( )
874
881
if let addedService = await iterator. next ( ) {
0 commit comments