Skip to content

Commit 84ccf18

Browse files
committed
shovel added services into task group results
1 parent 7dce0c3 commit 84ccf18

File tree

1 file changed

+52
-39
lines changed

1 file changed

+52
-39
lines changed

Sources/ServiceLifecycle/ServiceGroup.swift

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ public actor ServiceGroup: Sendable, Service {
234234
case gracefulShutdownFinished
235235
case gracefulShutdownTimedOut
236236
case cancellationCaught
237+
case newServiceAdded(ServiceGroupConfiguration.ServiceConfiguration)
237238
}
238239

239240
private func _run(
@@ -333,55 +334,48 @@ public actor ServiceGroup: Sendable, Service {
333334
"We did not create a graceful shutdown manager per service"
334335
)
335336

336-
var _unownedTaskGroupHandledCarefully = group
337-
group.addTask {
338-
// This is the task that listens to added services and starts them while the group is running
339-
340-
await withTaskCancellationHandler {
341-
// Channel will be finished in `shutdownGracefully`, we must not add services after graceful shutdown has started
342-
for await serviceConfiguration in addedServiceChannel {
343-
self.logger.debug(
344-
"Starting added service",
345-
metadata: [
346-
self.loggingConfiguration.keys.serviceKey: "\(serviceConfiguration.service)"
347-
]
348-
)
349-
350-
let gracefulShutdownManager = GracefulShutdownManager()
351-
gracefulShutdownManagers.append(gracefulShutdownManager)
352-
services.append(serviceConfiguration)
353-
354-
precondition(
355-
services.count == gracefulShutdownManagers.count,
356-
"Mismatch between services and graceful shutdown managers"
357-
)
358-
359-
_unownedTaskGroupHandledCarefully.addServiceTask(
360-
serviceConfiguration,
361-
gracefulShutdownManager: gracefulShutdownManager,
362-
index: services.count - 1
363-
)
364-
}
365-
} onCancel: {
366-
addedServiceChannel.finish()
367-
}
368-
369-
return .gracefulShutdownFinished
370-
}
371-
372337
group.addTask {
373338
// This child task is waiting forever until the group gets cancelled.
374339
let (stream, _) = AsyncStream.makeStream(of: Void.self)
375340
await stream.first { _ in true }
376341
return .cancellationCaught
377342
}
378343

344+
// Adds a task that listens to added services and funnels them into the task group
345+
group.addAddedServiceListenerTask(addedServiceChannel)
346+
379347
// We are going to wait for any of the services to finish or
380348
// the signal sequence to throw an error.
381349
while !group.isEmpty {
382-
let result: ChildTaskResult? = try await group.next()
350+
let nextEvent = try await group.next()
351+
352+
switch nextEvent {
353+
case .newServiceAdded(let serviceConfiguration):
354+
self.logger.debug(
355+
"Starting added service",
356+
metadata: [
357+
self.loggingConfiguration.keys.serviceKey: "\(serviceConfiguration.service)"
358+
]
359+
)
360+
361+
let gracefulShutdownManager = GracefulShutdownManager()
362+
gracefulShutdownManagers.append(gracefulShutdownManager)
363+
services.append(serviceConfiguration)
364+
365+
precondition(
366+
services.count == gracefulShutdownManagers.count,
367+
"Mismatch between services and graceful shutdown managers"
368+
)
369+
370+
group.addServiceTask(
371+
serviceConfiguration,
372+
gracefulShutdownManager: gracefulShutdownManager,
373+
index: services.count - 1
374+
)
375+
376+
// Each listener task can only handle a single added service, so we must add a new listener
377+
group.addAddedServiceListenerTask(addedServiceChannel)
383378

384-
switch result {
385379
case .serviceFinished(let service, let index):
386380
if group.isCancelled {
387381
// The group is cancelled and we expect all services to finish
@@ -786,6 +780,11 @@ public actor ServiceGroup: Sendable, Service {
786780
// We are going to continue the result loop since we have to wait for our service
787781
// to finish.
788782
break
783+
784+
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
787+
break
789788
}
790789
}
791790
}
@@ -866,9 +865,23 @@ extension ThrowingTaskGroup where Failure == Error, ChildTaskResult == ServiceGr
866865
}
867866
}
868867
}
869-
870868
}
871869

870+
mutating func addAddedServiceListenerTask(_ channel: AsyncChannel<ServiceGroupConfiguration.ServiceConfiguration>) {
871+
self.addTask {
872+
return await withTaskCancellationHandler {
873+
var iterator = channel.makeAsyncIterator()
874+
if let addedService = await iterator.next() {
875+
return .newServiceAdded(addedService)
876+
}
877+
878+
return .gracefulShutdownFinished
879+
} onCancel: {
880+
// Without this we can get stuck in `addService` if the group
881+
channel.finish()
882+
}
883+
}
884+
}
872885
}
873886

874887
// This should be removed once we support Swift 5.9+

0 commit comments

Comments
 (0)