Skip to content

Commit 895537e

Browse files
Fixed an issue where a manager would not be properly shut down unless it was cancelled
1 parent a23acd0 commit 895537e

File tree

2 files changed

+15
-42
lines changed

2 files changed

+15
-42
lines changed

Sources/WebPush/WebPushManager.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ public actor WebPushManager: Sendable {
4242
/// The internal executor to use when delivering messages.
4343
var executor: Executor
4444

45+
/// An internal flag indicating if a manager was shutdown already.
46+
var didShutdown = false
47+
4548
/// An internal lookup of keys as provided by the VAPID configuration.
4649
let vapidKeyLookup: [VAPID.Key.ID : VAPID.Key]
4750

@@ -121,6 +124,12 @@ public actor WebPushManager: Sendable {
121124
self.executor = executor
122125
}
123126

127+
deinit {
128+
if !didShutdown, case let .httpClient(httpClient) = executor {
129+
try? httpClient.syncShutdown()
130+
}
131+
}
132+
124133
/// Load an up-to-date Authorization header for the specified endpoint and signing key combo.
125134
/// - Parameters:
126135
/// - endpoint: The endpoint we'll be contacting to send push messages for a given subscriber.
@@ -448,6 +457,11 @@ public actor WebPushManager: Sendable {
448457
extension WebPushManager: Service {
449458
public func run() async throws {
450459
logger.info("Starting up WebPushManager")
460+
guard !didShutdown else {
461+
assertionFailure("The manager was already shutdown and cannot be started.")
462+
logger.error("The manager was already shutdown and cannot be started. Run your application server in debug mode to catch this.")
463+
return
464+
}
451465
try await withTaskCancellationOrGracefulShutdownHandler {
452466
try await gracefulShutdown()
453467
} onCancelOrGracefulShutdown: { [logger, executor] in
@@ -462,6 +476,7 @@ extension WebPushManager: Service {
462476
])
463477
}
464478
}
479+
didShutdown = true
465480
}
466481
}
467482

Tests/WebPushTests/WebPushManagerTests.swift

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,6 @@ struct WebPushManagerTests {
8080
.mockedKeyID2 : .mockedKey2,
8181
.mockedKeyID3 : .mockedKey3,
8282
])
83-
await withThrowingTaskGroup(of: Void.self) { group in
84-
group.addTask {
85-
try await manager.run()
86-
}
87-
group.cancelAll()
88-
}
8983
}
9084

9185
/// This is needed to cover the `uniquingKeysWith` safety call completely.
@@ -94,25 +88,13 @@ struct WebPushManagerTests {
9488
configuration.unsafeUpdateKeys(primaryKey: .mockedKey1, keys: [.mockedKey1], deprecatedKeys: [.mockedKey1])
9589
let manager = WebPushManager(vapidConfiguration: configuration)
9690
#expect(await manager.vapidKeyLookup == [.mockedKeyID1 : .mockedKey1])
97-
await withThrowingTaskGroup(of: Void.self) { group in
98-
group.addTask {
99-
try await manager.run()
100-
}
101-
group.cancelAll()
102-
}
10391
}
10492
}
10593

10694
@Suite("VAPID Key Retrieval") struct VAPIDKeyRetrieval {
10795
@Test func retrievesPrimaryKey() async {
10896
let manager = WebPushManager(vapidConfiguration: .mockedConfiguration)
10997
#expect(manager.nextVAPIDKeyID == .mockedKeyID1)
110-
await withThrowingTaskGroup(of: Void.self) { group in
111-
group.addTask {
112-
try await manager.run()
113-
}
114-
group.cancelAll()
115-
}
11698
}
11799

118100
@Test func alwaysRetrievesPrimaryKey() async throws {
@@ -122,12 +104,6 @@ struct WebPushManagerTests {
122104
for _ in 0..<100_000 {
123105
#expect(manager.nextVAPIDKeyID == .mockedKeyID1)
124106
}
125-
await withThrowingTaskGroup(of: Void.self) { group in
126-
group.addTask {
127-
try await manager.run()
128-
}
129-
group.cancelAll()
130-
}
131107
}
132108

133109
@Test func retrievesFallbackKeys() async throws {
@@ -139,12 +115,6 @@ struct WebPushManagerTests {
139115
keyCounts[manager.nextVAPIDKeyID, default: 0] += 1
140116
}
141117
#expect(abs(keyCounts[.mockedKeyID1, default: 0] - keyCounts[.mockedKeyID2, default: 0]) < 1_000) /// If this test fails, increase this number accordingly
142-
await withThrowingTaskGroup(of: Void.self) { group in
143-
group.addTask {
144-
try await manager.run()
145-
}
146-
group.cancelAll()
147-
}
148118
}
149119

150120
@Test func neverRetrievesDeprecatedKeys() async throws {
@@ -154,12 +124,6 @@ struct WebPushManagerTests {
154124
for _ in 0..<100_000 {
155125
#expect(manager.nextVAPIDKeyID != .mockedKeyID3)
156126
}
157-
await withThrowingTaskGroup(of: Void.self) { group in
158-
group.addTask {
159-
try await manager.run()
160-
}
161-
group.cancelAll()
162-
}
163127
}
164128

165129
@Test func keyStatus() async throws {
@@ -170,12 +134,6 @@ struct WebPushManagerTests {
170134
#expect(manager.keyStatus(for: .mockedKeyID2) == .valid)
171135
#expect(manager.keyStatus(for: .mockedKeyID3) == .deprecated)
172136
#expect(manager.keyStatus(for: .mockedKeyID4) == .unknown)
173-
await withThrowingTaskGroup(of: Void.self) { group in
174-
group.addTask {
175-
try await manager.run()
176-
}
177-
group.cancelAll()
178-
}
179137
}
180138
}
181139

0 commit comments

Comments
 (0)