Hi Everyone,
I’m working on a communication system for my app using NWConnection with the UDP protocol. The connection is registered to a custom serial dispatch queue. However, I’m trying to understand what the behavior will be in a scenario where the connection is canceled while there are still pending receive operations in progress.
Scenario Overview:
-
The sender is transmitting n = 100 packets to the receiver, out of which 40 packets have already been sent (i.e., delivered to the Receiver).
-
The receiver has posted m = 20 pending receive operations, where each receive operation is responsible for handling one packet.
-
The receiver has already successfully processed x = 10 packets.
-
At the time of cancellation, the receiver’s buffer still holds m = 20 packets that are pending for processing, and k = 10 pending receive callbacks are in the dispatch queue, waiting to be executed.
At same time when the 10th packet was processed another thread triggers .cancel() on this accepted NWConnection (on the receiver side), I need to understand the impact on the pending receive operations and their associated callbacks.
My Questions:
-
What happens to the k = 10 pending receive callbacks that are in the dispatch queue waiting to be triggered when the connection is canceled? Will these callbacks complete successfully and process the data? Or, because the connection is canceled, will they complete with failure?
-
What happens to the remaining pending receive operations that were initiated but have not yet been scheduled in the dispatch queue? For the pending receive operations that were already initiated (i.e., the network stack is waiting to receive the data, but the callback hasn’t been scheduled yet), will they fail immediately when the connection is canceled? Or is there any chance that the framework might still process these receives before the cancellation fully takes effect?
First up, if you have outstanding receives then, no matter what happens, their completion handlers will be called [1]. If you cancel a connection then either the completion handler will be called with a reasonable result or it’ll be called to indicate cancellation.
Beyond that, you need to draw a distinction between:
-
Received by the receiver
-
Dispatched to your connection’s queue
Once a closure has been dispatch to your connection queue there’s no way for it to not run. If the queue is serial, which it should be, it’ll run in order.
If no data has been received by the receiver at all, then any outstanding read closures will be called with a result. The exact result depends on the protocol, the type of close, and the origin of the close:
Protocol Force Local Remote
-------- ----- ----- ------
TCP no ECANCELED EOF > ENODATA
TCP yes ECANCELED ECONNRESET
UDP - ECANCELED nothing
In this table:
-
Local refers to the peer that called
close(). -
Remote refers to the peer on the other peer.
-
In the first line, the outstanding receive is called with EOF and then, if you receive again, it immediately fails with
ENODATA.
The gap between received by the receiver and dispatched to your connection queue is interesting. I suspect that it’ll be dropped and outstanding receives will complete with ECANCELED, but I don’t think we define any specific behaviour in that case.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] Well, unless the process crashes.