NWPathMonitor crashes

I'm seeing crashes within NWPathMonitor.pathUpdateHandler and in various parts of NWPath (sometimes on dealloc of NWPath).

One thing I noticed is that we are using a global concurrent queue in the call to NWPathMonitor.start(queue:). Could this be the reason? The crash isn't easily reproducible, but I'll see if I can find a way to test this.

It would be nice to have an authoritative answer about this, though. The documentation makes no mention that the queue must be serial. And NWPath is a struct, so it seems unexpected to require a serial queue.

To be clear, I'm doing something like this:



let nwPathMonitor = NWPathMonitor()
nwPathMonitor.pathUpdateHandler = { [weak self] path in
    guard let self else { return }

    // do stuff with path
}

nwPathMonitor.start(queue: DispatchQueue.global(qos: .background))

Replies

One thing I noticed is that we are using a global concurrent queue … Could this be the reason?

While I recommend against using the global concurrent queue — see Avoid Dispatch Global Concurrent Queues — using it in the context should not cause a crash per se. However, it’s easy to see how it might cause problems in your code. For example, the snippet you posted captures self, which suggests you’re using self in your real program. If so, be aware that your use of the global concurrent queue could cause concurrent accesses to self. If, for example, two events get delivered in rapid succession, the system could start running the handler for the second event while the handler for the first one is still running.

So, I recommend that you switch to a serial queue, just to keep the concurrency under control. If you continue to have problems, post an example crash report and we’ll chat further.

See Posting a Crash Report for details on how to do that.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks for the reply. I'm just using self to store some of the contents from NWPath and they are properly protected for asynchronous access using semaphores.

So far I haven't found a good way to stress test this callback, other than to plug/unplug an ethernet cable on my development system while running under simulator. But if I find a way to reliably reproduce this, I'll provide a proper crash report. In the mean time, I can include some screenshots from the crash reporter...

Any tips you can provide would be greatly welcomed! In case it matters, we have several different libraries each creating their own instance of NWPathMonitor within a single app. They all appear to have proper thread safety precautions. Unfortunately there doesn't appear to be a way distinguish the different instances by looking at the stack traces.

using semaphores.

Semaphores, eh? My experience is that are rarely the right tool for that job.

So far I haven't found a good way to stress test this callback, other than to plug/unplug an ethernet cable on my development system while running under simulator.

The simulator uses the Mac’s networking stack, so you can cycle the network up and down with Mac tools and techniques. My go-to tool for this sort of thing is the networksetup tool. See the networksetup man page for details.

In the mean time, I can include some screenshots from the crash reporter...

Is that the Xcode debugger?

Unfortunately there doesn't appear to be a way distinguish the different instances by looking at the stack traces.

Yeah, that’s tricky. My only suggestion on that front is to modify each allocation site to log the NWPath monitor’s address, at which point you should be able to match that to address visible in the debugger.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"