I was having a look through the documentation for NEPacketTunnelProvider
, and wanted to know if it's possible for startTunnel(..)
and stopTunnel(..)
to run simultaneously, and thus require synchronization between resources they deal with?
For example, if the VPN is toggled rapidly from system settings, could the setup that occurs in my startTunnel()
definition (class instantiation and setTunnelNetworkSettings(value)
) potentially occur after the tear-down logic (resource cleanup, setTunnelNetworkSettings(nil)
), leaving the system in a state where the VPN is deactivated, but the configuration is in place?
All the entry points to your provider are called on a single Dispatch queue associated with your provider. So, for example, it’s not possible for stopTunnel(…)
to be called while startTunnel(…)
is actually running.
However, that’s not as strong a guarantee as it might seem. Each of those methods is asynchronous. You can consider them as either Swift async functions or as synchronous functions with completion handlers. Either way, this asynchronicity is an issue:
- If you view these as Swift async functions then this is a bit like the actor reentrancy gotcha. Any time the function suspends another function can start running in your ‘actor’, that is, on your provider’s Dispatch queue. And regardless of what else you do or don’t do when you start your tunnel, you can’t avoid suspension points because you have to call
setTunnelNetworkSettings(…)
at some point. - Alternative, if you look at this from a Dispatch perspective, the serialisation guarantee only applies while your code is running on the provider’s queue. If you call
setTunnelNetworkSettings(…)
and then return, you no longer have code running on the queue and thus the system is free to callstopTunnel(…)
.
So, you should be prepared for stopTunnel(…)
to come in while startTunnel(…)
is in progress. If that happens, cancel what you’re doing, such that you return from (or call the completion handler for) startTunnel(…)
promptly. And once things are torn down sufficiently [1], return from (or call the completion handler for) stopTunnel(…)
.
You don’t have to worry about the reverse case though. NE won’t attempt to start your tunnel while a stop is in progress.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] That is, sufficient for your purposes. You don’t have to worry about the NE side of this.