Skip to content

Commit

Permalink
Add async version of wait, add run and asyncRun (#243)
Browse files Browse the repository at this point in the history
* Add async version of wait, add run and asyncRun

`wait` stalls the thread it is running on. If that thread is used by the task executor it will mean running Tasks could stall. Added `asyncWait` to run application on a separate DispatchQueue.

Also added a `run` and `asyncRun` which is a combination of `start` and `wait`

* Update Sources/Hummingbird/Application.swift

Co-authored-by: Joannis Orlandos <[email protected]>

* Update Sources/Hummingbird/Application.swift

Co-authored-by: Joannis Orlandos <[email protected]>

* PR changes requested

---------

Co-authored-by: Joannis Orlandos <[email protected]>
  • Loading branch information
adam-fowler and Joannis authored Oct 6, 2023
1 parent 0adb5e6 commit 3c7f440
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 5 deletions.
51 changes: 49 additions & 2 deletions Sources/Hummingbird/Application.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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")
}
5 changes: 2 additions & 3 deletions Sources/PerformanceTest/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -54,5 +54,4 @@ app.router.get("json") { _ in
}

// run app
try app.start()
app.wait()
try app.run()

0 comments on commit 3c7f440

Please sign in to comment.