Description
This test triggers a precondition failure: preconditionInEventLoop
func testRequestWithSharedEventLoopGroup() throws {
let httpBin = HttpBin()
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 8)
let httpClient = HTTPClient(eventLoopGroupProvider: .shared(eventLoopGroup))
defer {
try! eventLoopGroup.syncShutdownGracefully()
httpBin.shutdown()
}
let response = try httpClient.get(url: "http://localhost:\(httpBin.port)/events/10/1").wait()
XCTAssertEqual(.ok, response.status)
}
Note that I am using my own EventLoopGroup
to run the HTTPClient.
The problem is in execute
method. In the beginning of the function, we ask the event loop group for an EventLoop
and associate this event loop with the Task
. But, the ChannelBootstrap
will ask the event loop group again for an event loop which will likely be a different one. So in the end the Channel
will actually live on a different event loop than the Task
.
Then in
self.delegate.didReceivePart(task: self.task, body).whenComplete { result in
self.handleBackpressureResult(context: context, result: result)
}
The delegate will create a completion future from the eventLoop
property of Task
. So self.handleBackpressureResult
will be executed on this event loop and thus context.read()
will be executed on a different event loop than what the context expects -> 💥
Possible fixes
- Make sure that event loop of
Task
is the same as the one from the channel. - and/or: make sure that delegate callbacks hop back to the
EventLoop
of the channel