How do I completely stop & cleanup nw_connection_create?

185 Views Asked by At

I'm creating a socket using apple's Network.Framework with nw_connection_create() (which as far as I can tell is the modern, non-deprecated approach to make a TCP connection)

My flow

  • Create end point nw_endpoint_create_host()
  • Create a connection nw_connection_create()
  • I create a dispatch queue dispatch_queue_create(DISPATCH_QUEUE_SERIAL)
  • assign with nw_connection_set_queue()
  • set status callback nw_connection_set_state_changed_handler()
  • Start nw_connection_start
  • Request first recv (incrementing RecvCallCount atomic first - see below) nw_connection_receive

I send & recieve packets/data, status, no problem. Every callback from recv decrements RecvCallCount atomic so I know I'm no longer waiting for a callback.

When I want to shutdown the socket

  • nw_connection_force_cancel()
  • nil or release the connection
  • nil or release the dispatch queue
  • My class destructs

After this, I can get spurious callbacks from the recv callback, or state changed handler. (Which crash as they access my object which has been destructed) The thunk seems to come from nowhere according to xcode, as I'm down to a single thread (Not sure how this is true)

  • I have a mutex around any use of the connection handle, as calling send or recv after nw_connection_force_cancel() can crash (or possibly I had null'd it)
  • Same effect with ARC on or off (with retain/release in applicable places)
  • Same effects on ios & macos

There's no documentation mentioning this, but I can only assume there's a 1:1 call & response with nw_connection_receive given the effects, so I tried setting an atomic (a ref counter) for every recv call and then made a .wait() in my destructor to wait for the atomic to get to zero (if not zero). This was fine for a while, but I'm getting some cases where the recv callback never occurs.

Am I supposed to

  • Never free until I've had the cancel status update?
  • Stop the nw_connection_t in a different way?
  • Flush the dispatch queue? (I tried enqueueing blocks and they come back so I assume nothing at destruction time is waiting on the queue)
  • Flush the connection (can't see a way to do this)

What is the correct way to end this socket? flush the dispatch queue, flush any recv callbacks.

I'm unable to find a solid example using the nw_ functions which actually cleans up, so I could easily be missing something obvious.

0

There are 0 best solutions below