diff --git a/Sources/Hummingbird/Application.swift b/Sources/Hummingbird/Application.swift index 379a0021a..332c69a3c 100644 --- a/Sources/Hummingbird/Application.swift +++ b/Sources/Hummingbird/Application.swift @@ -174,7 +174,26 @@ public final class HBApplication: HBExtensible { // MARK: Methods - /// Run application + /// Start application and wait for it to stop + /// + /// This function can only be called from a non async context as it stalls + /// the current thread waiting for the application to finish + @available(*, noasync, message: "Use HBApplication.asyncRun instead.") + public func run() throws { + try self.start() + self.wait() + } + + /// Start application and wait for it to stop + /// + /// Version of `run` that can be called from asynchronous context + @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) + public func asyncRun() async throws { + try self.start() + await self.asyncWait() + } + + /// Start application public func start() throws { var startError: Error? let startSemaphore = DispatchSemaphore(value: 0) @@ -187,11 +206,25 @@ public final class HBApplication: HBExtensible { try startError.map { throw $0 } } - /// wait while server is running + /// Wait until server has stopped running + /// + /// This function can only be called from a non async context as it stalls + /// the current thread waiting for the application to finish + @available(*, noasync, message: "Use HBApplication.asyncWait instead.") public func wait() { self.lifecycle.wait() } + /// Wait until server has stopped running + /// + /// Version of `wait`` that can be called from asynchronous context + @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) + public func asyncWait() async { + await self.onExecutionQueue { app in + app.wait() + } + } + /// Shutdown application public func stop() { let stopSemaphore = DispatchSemaphore(value: 0) @@ -218,4 +251,18 @@ public final class HBApplication: HBExtensible { try self.eventLoopGroup.syncShutdownGracefully() } } + + /// Run closure on private execution queue + @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) + private func onExecutionQueue(_ process: @Sendable @escaping (HBApplication) -> Void) async { + let unsafeApp = HBUnsafeTransfer(self) + await withCheckedContinuation { continuation in + HBApplication.executionQueue.async { + process(unsafeApp.wrappedValue) + continuation.resume() + } + } + } + + private static let executionQueue = DispatchQueue(label: "hummingbird.execution") } diff --git a/Sources/PerformanceTest/main.swift b/Sources/PerformanceTest/main.swift index e4d5a848e..6589811dd 100644 --- a/Sources/PerformanceTest/main.swift +++ b/Sources/PerformanceTest/main.swift @@ -18,7 +18,7 @@ import NIOPosix // get environment let hostname = HBEnvironment.shared.get("SERVER_HOSTNAME") ?? "127.0.0.1" -let port = HBEnvironment.shared.get("SERVER_PORT", as: Int.self) ?? 8080 +let port = HBEnvironment.shared.get("SERVER_PORT", as: Int.self) ?? 8081 // create app let elg = MultiThreadedEventLoopGroup(numberOfThreads: 2) @@ -54,5 +54,4 @@ app.router.get("json") { _ in } // run app -try app.start() -app.wait() +try app.run()