i’m currently running into a
Fatal error: SWIFT TASK CONTINUATION MISUSE:
request(_:sections:deadline:) tried to resume its continuation
more than once, returning
read(descriptor:pointer:size:): Connection reset by peer (errno: 104),
this corresponds to the following code location, where i am writing a
CheckedContinuation to an
if case .failure(let error) = $0
continuation.resume(returning: .failure(.io(error, written: false)))
as you can see, i’m using the
failure(_:) case to resume the continuation if the
writeAndFlush operation fails, and this apparently took place even though the continuation also made it into the channel pipeline and was resumed there as well.
is it actually possible for an
OutboundIn value to enter the pipeline even though
Just from this code snippet it is hard to tell but it looks like there is another place that resumes the continuation.
Yes, the failing of a write only happens after the write travelled the channel pipeline and gets either succeeded or failed at some point. Most likely you pass the write into the channel pipeline and where ever your resume it you also fail the write which triggers the double resume.
correct, the channel handler in the pipeline also contains logic that resumes the continuation.
ah, that’s what i suspected. how should i handle this case? switching on the returned
Result case doesn’t seem sufficient to detect if the continuation needs resuming.
I think you should either only resume the continuation in one place. This can be either your channel handler or the
whenComplete callback on the write’s future. I personally would probably do the latter for simplicity though the former can have better performance since you can avoid allocating a promise.
the continuation is the continuation that receives the response gathered by the channel handler (a
MongoWire.Message<ByteBufferView>), so it can’t yield a successful result from the
whenComplete callback, because the response has not been received yet.
however, the channel handler can’t be exclusively responsible for resuming the continuation either, because if the continuation never makes it into the pipeline in the first place, then the caller will hang indefinitely.
Right I missed that the continuation is typed and not
Void. Without known the details have where the continuation is being constructed and what the other handlers in the pipeline are doing it is hard to say what the right thing to do is.
Overall you just have to make sure that it is only resumed once. This is one of the reasons why we created the
NIOAsyncChannel since bridging one off continuations into a channel pipeline can become tricky.
One thing that you could do is pass an additional
EventLoopPromise down the channel pipeline instead of the continuation and add a
whenComplete to that promise. You can properly type that promise and a promise protects you against double resumption.
withCheckedThrowingContinuation call is just one of those older “NIO-to-async” adapters, so using an
EventLoopPromise and then
awaiting on the future’s
get() is probably the way to go. no need to have a
CheckedContinuation at all actually.
it is an older codebase written around 2022 or so. if it were a new project i would certainly use