mark syncShutdownGracefully noasync (#2381)
mark syncShutdownGracefully noasync Motivation: The code as-is blocks the calling thread. Modifications: * mark `EventLoopGroup.syncShutdownGracefully()` and `NIOThreadPool.syncShutdownGracefully()` noasync on Swift > 5.7 * offer NIOThreadPool.shutdownGracefully() * add renamed to syncShutdownGracefully()
This commit is contained in:
parent
e81bd62fb6
commit
5f5fa9a2b2
|
@ -1235,9 +1235,20 @@ extension EventLoopGroup {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public func syncShutdownGracefully() throws {
|
|
||||||
self._preconditionSafeToSyncShutdown(file: #fileID, line: #line)
|
|
||||||
|
|
||||||
|
#if swift(>=5.7)
|
||||||
|
@available(*, noasync, message: "this can end up blocking the calling thread", renamed: "shutdownGracefully()")
|
||||||
|
public func syncShutdownGracefully() throws {
|
||||||
|
try self._syncShutdownGracefully()
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
public func syncShutdownGracefully() throws {
|
||||||
|
try self._syncShutdownGracefully()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private func _syncShutdownGracefully() throws {
|
||||||
|
self._preconditionSafeToSyncShutdown(file: #fileID, line: #line)
|
||||||
let errorStorageLock = NIOLock()
|
let errorStorageLock = NIOLock()
|
||||||
var errorStorage: Error? = nil
|
var errorStorage: Error? = nil
|
||||||
let continuation = DispatchWorkItem {}
|
let continuation = DispatchWorkItem {}
|
||||||
|
|
|
@ -310,7 +310,33 @@ extension NIOThreadPool {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// Shuts down the thread pool gracefully.
|
||||||
|
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
||||||
|
@inlinable
|
||||||
|
public func shutdownGracefully() async throws {
|
||||||
|
return try await withCheckedThrowingContinuation { cont in
|
||||||
|
self.shutdownGracefully { error in
|
||||||
|
if let error = error {
|
||||||
|
cont.resume(throwing: error)
|
||||||
|
} else {
|
||||||
|
cont.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if swift(>=5.7)
|
||||||
|
@available(*, noasync, message: "this can end up blocking the calling thread", renamed: "shutdownGracefully()")
|
||||||
public func syncShutdownGracefully() throws {
|
public func syncShutdownGracefully() throws {
|
||||||
|
try self._syncShutdownGracefully()
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
public func syncShutdownGracefully() throws {
|
||||||
|
try self._syncShutdownGracefully()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private func _syncShutdownGracefully() throws {
|
||||||
let errorStorageLock = NIOLock()
|
let errorStorageLock = NIOLock()
|
||||||
var errorStorage: Swift.Error? = nil
|
var errorStorage: Swift.Error? = nil
|
||||||
let continuation = DispatchWorkItem {}
|
let continuation = DispatchWorkItem {}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// This source file is part of the SwiftNIO open source project
|
// This source file is part of the SwiftNIO open source project
|
||||||
//
|
//
|
||||||
// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors
|
// Copyright (c) 2017-2023 Apple Inc. and the SwiftNIO project authors
|
||||||
// Licensed under Apache License v2.0
|
// Licensed under Apache License v2.0
|
||||||
//
|
//
|
||||||
// See LICENSE.txt for license information
|
// See LICENSE.txt for license information
|
||||||
|
@ -29,6 +29,7 @@ extension NIOThreadPoolTest {
|
||||||
return [
|
return [
|
||||||
("testThreadNamesAreSetUp", testThreadNamesAreSetUp),
|
("testThreadNamesAreSetUp", testThreadNamesAreSetUp),
|
||||||
("testThreadPoolStartsMultipleTimes", testThreadPoolStartsMultipleTimes),
|
("testThreadPoolStartsMultipleTimes", testThreadPoolStartsMultipleTimes),
|
||||||
|
("testAsyncShutdownWorks", testAsyncShutdownWorks),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import XCTest
|
||||||
@testable import NIOPosix
|
@testable import NIOPosix
|
||||||
import Dispatch
|
import Dispatch
|
||||||
import NIOConcurrencyHelpers
|
import NIOConcurrencyHelpers
|
||||||
|
import NIOEmbedded
|
||||||
|
|
||||||
class NIOThreadPoolTest: XCTestCase {
|
class NIOThreadPoolTest: XCTestCase {
|
||||||
func testThreadNamesAreSetUp() {
|
func testThreadNamesAreSetUp() {
|
||||||
|
@ -108,4 +109,20 @@ class NIOThreadPoolTest: XCTestCase {
|
||||||
XCTAssertEqual(threadOne, threadTwo)
|
XCTAssertEqual(threadOne, threadTwo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAsyncShutdownWorks() throws {
|
||||||
|
guard #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) else { throw XCTSkip() }
|
||||||
|
XCTAsyncTest {
|
||||||
|
let threadPool = NIOThreadPool(numberOfThreads: 17)
|
||||||
|
let eventLoop = NIOAsyncTestingEventLoop()
|
||||||
|
|
||||||
|
threadPool.start()
|
||||||
|
try await threadPool.shutdownGracefully()
|
||||||
|
|
||||||
|
let future = threadPool.runIfActive(eventLoop: eventLoop) {
|
||||||
|
XCTFail("This shouldn't run because the pool is shutdown.")
|
||||||
|
}
|
||||||
|
await XCTAssertThrowsError(try await future.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This source file is part of the SwiftNIO open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2022 Apple Inc. and the SwiftNIO project authors
|
||||||
|
// Licensed under Apache License v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE.txt for license information
|
||||||
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This source file is part of the AsyncHTTPClient open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors
|
||||||
|
// Licensed under Apache License v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE.txt for license information
|
||||||
|
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
/*
|
||||||
|
* Copyright 2021, gRPC Authors All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
extension XCTestCase {
|
||||||
|
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
|
||||||
|
/// Cross-platform XCTest support for async-await tests.
|
||||||
|
///
|
||||||
|
/// Currently the Linux implementation of XCTest doesn't have async-await support.
|
||||||
|
/// Until it does, we make use of this shim which uses a detached `Task` along with
|
||||||
|
/// `XCTest.wait(for:timeout:)` to wrap the operation.
|
||||||
|
///
|
||||||
|
/// - NOTE: Support for Linux is tracked by https://bugs.swift.org/browse/SR-14403.
|
||||||
|
/// - NOTE: Implementation currently in progress: https://github.com/apple/swift-corelibs-xctest/pull/326
|
||||||
|
func XCTAsyncTest(
|
||||||
|
expectationDescription: String = "Async operation",
|
||||||
|
timeout: TimeInterval = 30,
|
||||||
|
file: StaticString = #filePath,
|
||||||
|
line: UInt = #line,
|
||||||
|
function: StaticString = #function,
|
||||||
|
operation: @escaping @Sendable () async throws -> Void
|
||||||
|
) {
|
||||||
|
let expectation = self.expectation(description: expectationDescription)
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
try await operation()
|
||||||
|
} catch {
|
||||||
|
XCTFail("Error thrown while executing \(function): \(error)", file: file, line: line)
|
||||||
|
Thread.callStackSymbols.forEach { print($0) }
|
||||||
|
}
|
||||||
|
expectation.fulfill()
|
||||||
|
}
|
||||||
|
self.wait(for: [expectation], timeout: timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
|
||||||
|
internal func XCTAssertThrowsError<T>(
|
||||||
|
_ expression: @autoclosure () async throws -> T,
|
||||||
|
file: StaticString = #filePath,
|
||||||
|
line: UInt = #line,
|
||||||
|
verify: (Error) -> Void = { _ in }
|
||||||
|
) async {
|
||||||
|
do {
|
||||||
|
_ = try await expression()
|
||||||
|
XCTFail("Expression did not throw error", file: file, line: line)
|
||||||
|
} catch {
|
||||||
|
verify(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
|
||||||
|
internal func XCTAssertNoThrow<T>(
|
||||||
|
_ expression: @autoclosure () async throws -> T,
|
||||||
|
file: StaticString = #file,
|
||||||
|
line: UInt = #line
|
||||||
|
) async {
|
||||||
|
do {
|
||||||
|
_ = try await expression()
|
||||||
|
} catch {
|
||||||
|
XCTFail("Expression did throw error", file: file, line: line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
|
||||||
|
internal func XCTAssertNoThrowWithResult<Result>(
|
||||||
|
_ expression: @autoclosure () async throws -> Result,
|
||||||
|
file: StaticString = #filePath,
|
||||||
|
line: UInt = #line
|
||||||
|
) async -> Result? {
|
||||||
|
do {
|
||||||
|
return try await expression()
|
||||||
|
} catch {
|
||||||
|
XCTFail("Expression did throw: \(error)", file: file, line: line)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue