@@ -108,13 +108,13 @@ public actor ServiceGroup: Sendable, Service {
108108 self . maximumCancellationDuration = configuration. _maximumCancellationDuration
109109 }
110110
111- /// Adds a service to the group.
111+ /// Adds a new service to the group.
112112 ///
113113 /// If the group is currently running, the added service will be started immediately.
114114 /// If the group is gracefully shutting down, cancelling, or already finished, the added service will not be started.
115115 /// - Parameters:
116116 /// - serviceConfiguration: The service configuration to add.
117- public func addService ( _ serviceConfiguration: ServiceGroupConfiguration . ServiceConfiguration ) async {
117+ public func addServiceUnlessShutdown ( _ serviceConfiguration: ServiceGroupConfiguration . ServiceConfiguration ) async {
118118 switch self . state {
119119 case var . initial( services: services) :
120120 self . state = . initial( services: [ ] )
@@ -125,18 +125,19 @@ public actor ServiceGroup: Sendable, Service {
125125 await addedServiceChannel. send ( serviceConfiguration)
126126
127127 case . finished:
128+ // Since this is a best effort operation we don't have to do anything here
128129 return
129130 }
130131 }
131132
132- /// Adds a service to the group.
133+ /// Adds a new service to the group.
133134 ///
134135 /// If the group is currently running, the added service will be started immediately.
135136 /// If the group is gracefully shutting down, cancelling, or already finished, the added service will not be started.
136137 /// - Parameters:
137138 /// - 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) )
140141 }
141142
142143 /// Runs all the services by spinning up a child task per service.
@@ -225,7 +226,7 @@ public actor ServiceGroup: Sendable, Service {
225226 }
226227 }
227228
228- fileprivate enum ChildTaskResult {
229+ private enum ChildTaskResult {
229230 case serviceFinished( service: ServiceGroupConfiguration . ServiceConfiguration , index: Int )
230231 case serviceThrew( service: ServiceGroupConfiguration . ServiceConfiguration , index: Int , error: any Error )
231232 case signalCaught( UnixSignal )
@@ -318,8 +319,9 @@ public actor ServiceGroup: Sendable, Service {
318319 let gracefulShutdownManager = GracefulShutdownManager ( )
319320 gracefulShutdownManagers. append ( gracefulShutdownManager)
320321
321- group. addServiceTask (
322- serviceConfiguration,
322+ self . addServiceTask (
323+ group: & group,
324+ service: serviceConfiguration,
323325 gracefulShutdownManager: gracefulShutdownManager,
324326 index: index
325327 )
@@ -342,14 +344,14 @@ public actor ServiceGroup: Sendable, Service {
342344 }
343345
344346 // 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)
346348
347349 // We are going to wait for any of the services to finish or
348350 // the signal sequence to throw an error.
349351 while !group. isEmpty {
350- let nextEvent = try await group. next ( )
352+ let result : ChildTaskResult ? = try await group. next ( )
351353
352- switch nextEvent {
354+ switch result {
353355 case . newServiceAdded( let serviceConfiguration) :
354356 self . logger. debug (
355357 " Starting added service " ,
@@ -367,14 +369,18 @@ public actor ServiceGroup: Sendable, Service {
367369 " Mismatch between services and graceful shutdown managers "
368370 )
369371
370- group. addServiceTask (
371- serviceConfiguration,
372+ self . addServiceTask (
373+ group: & group,
374+ service: serviceConfiguration,
372375 gracefulShutdownManager: gracefulShutdownManager,
373376 index: services. count - 1
374377 )
375378
376379 // 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+ )
378384
379385 case . serviceFinished( let service, let index) :
380386 if group. isCancelled {
@@ -782,8 +788,7 @@ public actor ServiceGroup: Sendable, Service {
782788 break
783789
784790 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
787792 break
788793 }
789794 }
@@ -845,17 +850,16 @@ public actor ServiceGroup: Sendable, Service {
845850 cancellationTimeoutTask = nil
846851 }
847852 }
848- }
849853
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,
853857 gracefulShutdownManager: GracefulShutdownManager,
854858 index: Int
855859 ) {
856860 // This must be addTask and not addTaskUnlessCancelled
857861 // because we must run all the services for the shutdown logic to work.
858- self . addTask {
862+ group . addTask {
859863 return await TaskLocals . $gracefulShutdownManager. withValue ( gracefulShutdownManager) {
860864 do {
861865 try await serviceConfiguration. service. run ( )
@@ -867,8 +871,11 @@ extension ThrowingTaskGroup where Failure == Error, ChildTaskResult == ServiceGr
867871 }
868872 }
869873
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 {
872879 return await withTaskCancellationHandler {
873880 var iterator = channel. makeAsyncIterator ( )
874881 if let addedService = await iterator. next ( ) {
0 commit comments