@@ -1244,4 +1244,59 @@ class HTTPClientTests: XCTestCase {
12441244 }
12451245 }
12461246 }
1247+
1248+ func testRaceNewRequestsVsShutdown( ) {
1249+ let numberOfWorkers = 20
1250+ let allWorkersReady = DispatchSemaphore ( value: 0 )
1251+ let allWorkersGo = DispatchSemaphore ( value: 0 )
1252+ let allDone = DispatchGroup ( )
1253+
1254+ let httpBin = HTTPBin ( )
1255+ defer {
1256+ XCTAssertNoThrow ( try httpBin. shutdown ( ) )
1257+ }
1258+ let httpClient = HTTPClient ( eventLoopGroupProvider: . createNew)
1259+ defer {
1260+ XCTAssertThrowsError ( try httpClient. syncShutdown ( ) ) { error in
1261+ XCTAssertEqual ( . alreadyShutdown, error as? HTTPClientError )
1262+ }
1263+ }
1264+
1265+ let url = " http://localhost: \( httpBin. port) /get "
1266+ XCTAssertNoThrow ( XCTAssertEqual ( . ok, try httpClient. get ( url: url) . wait ( ) . status) )
1267+
1268+ for w in 0 ..< numberOfWorkers {
1269+ let q = DispatchQueue ( label: " worker \( w) " )
1270+ q. async ( group: allDone) {
1271+ func go( ) {
1272+ allWorkersReady. signal ( ) // tell the driver we're ready
1273+ allWorkersGo. wait ( ) // wait for the driver to let us go
1274+
1275+ do {
1276+ while true {
1277+ let result = try httpClient. get ( url: url) . wait ( ) . status
1278+ XCTAssertEqual ( . ok, result)
1279+ }
1280+ } catch {
1281+ // ok, we failed, pool probably shutdown
1282+ XCTAssertEqual ( . cancelled, error as? HTTPClientError )
1283+ }
1284+ }
1285+ go ( )
1286+ }
1287+ }
1288+
1289+ for _ in 0 ..< numberOfWorkers {
1290+ allWorkersReady. wait ( )
1291+ }
1292+ // now all workers should be waiting for the go signal
1293+
1294+ for _ in 0 ..< numberOfWorkers {
1295+ allWorkersGo. signal ( )
1296+ }
1297+ Thread . sleep ( until: . init( timeIntervalSinceNow: 0.2 ) )
1298+ XCTAssertNoThrow ( try httpClient. syncShutdown ( ) )
1299+ // all workers should be running, let's wait for them to finish
1300+ allDone. wait ( )
1301+ }
12471302}
0 commit comments