Behavior of Pending Receive Callbacks on Canceled NWConnection (UDP) Registered to Custom Serial Dispatch Queue

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:

  1. 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?

  2. 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?

Answered by DTS Engineer in 825436022

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.

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.

Behavior of Pending Receive Callbacks on Canceled NWConnection (UDP) Registered to Custom Serial Dispatch Queue
 
 
Q