hummingbird-project / hummingbird

Lightweight, flexible HTTP server framework written in Swift
https://hummingbird.codes/
Apache License 2.0
1.2k stars 53 forks source link

Handler hangs when `await`ing an `EventLoopFuture.get()` #239

Closed thoradam closed 1 year ago

thoradam commented 1 year ago

Minimal example:

let app = HBApplication(configuration: .init(address: .hostname("127.0.0.1", port: 8080)))
app.router.get("foo") { request in
    return try await MultiThreadedEventLoopGroup.singleton.any().makeSucceededFuture("foo").get()
}
try app.start()
app.wait()
> curl localhost:8080/foo
# no response, just hangs

I'm not very familiar with Swift NIO but I'm assuming this should work. This minimal example might look silly but I'm hitting this issue because I'm using SQLiteNIO to query data for responses but I want to write the code in terms of async/await. Configuring HBApplication with eventLoopGroupProvider: .shared(MultiThreadedEventLoopGroup.singleton) makes no difference.

Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
hummingbird 1.8.4
adam-fowler commented 1 year ago

So I just copied your code and it worked fine. The curl call return "foo". I've checked this with 5.8 and a 5.i9 weekly from a month or so ago. (I'm currently in transit and haven't had a chance to download latest Xcode etc). When I get back home I'll check with released version of 5.9

thoradam commented 1 year ago

Interesting. Some more context:

swift-nio        2.59.0
swift-nio-extras 1.19.1
swift-nio-http2  1.27.0
swift-nio-ssl    2.25.0

Also after having such a request hang (and after cancelling it from the client side), shutting down the server hangs at:

info HummingBird : [Lifecycle] stopping tasks [HTTP Server]
adam-fowler commented 1 year ago

What platform are you using?

thoradam commented 1 year ago

macOS 14 (aarch64) and Debian 12 (aarch64), same behavior on both

adam-fowler commented 1 year ago

Ok I've tested swift 5.9 on macOS 14 and could not replicate.

I'll try on my Ubuntu laptop tomorrow.

I can kinda see why this might occur as the 1.0 of swift service lifecycle causes the main thread to stall. This might not work well with Swift concurrency. I have never seen it hang though.

We are in the process of moving away from v1.0 of service lifecycle, but it requires a major version update and we are looking to get some other changes in alongside this change. So it won't happen in the near future.

adam-fowler commented 1 year ago

So I tried this on my Linux laptop and still cannot replicate the issue you are seeing. Would you be able to construct a swift package that demonstrates this issue. Maybe there is some setting I am missing

thoradam commented 1 year ago

Maybe there is some setting I am missing

Spot on. This is the culprit:

swiftSettings: [
    .enableExperimentalFeature("StrictConcurrency")
]

Sorry I wasted your time trying to reproduce this. I understand this is an experimental feature but I was under the impression it only affected compiler warnings/errors and not runtime behavior.

adam-fowler commented 1 year ago

Ok that's interesting. I can repro it now.

adam-fowler commented 1 year ago

I've got a fix as well I think. Hummingbird has the same issue as Vapor where it blocks the main thread when running. I'll steal Vapor's solution. Which in effect runs Hummingbird on a separate DispatchQueue.

Surprised "StrictConcurrency" actually changes code though

adam-fowler commented 1 year ago

Checkout #243 . It is also in v1.9.0