NSTimer in PacketTunnelProvider Doesn't work

AppDelegate and PackagetTunnelProvider use NSTimer in the same way:

[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(expireTime) userInfo:nil repeats:NO];


The -expireTime in AppDelegate runs in 10 seconds, but not in PacketTunnelProvider.

What's the reason?

Accepted Reply

it works this way

Right, but I recommend you avoid this approach for two reasons:

  • It assumes that the main thread runs its run loop, which is not guaranteed by the NE provider API. An NE provider’s main thread could just as easily call

    dispatchMain
    , which would cause the main queue to run but not the main thread’s run loop.
  • It gets you into the business of cross run loop scheduling and unscheduling. While this is supported, in my experience it’s problematic for two reasons:

    • I’ve seen cases while cross run loop schedule exposes obscure bugs in the OS.

    • Even if the OS side of this works perfectly, it’s easy to fall foul of race conditions in your own code.

I have another question:

Probably best to put that in a new thread but I’m still ‘litigating’ this issue (-:

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

AppDelegate
and
PackagetTunnelProvider
use
NSTimer
in the same way:

The

-[NSTimer scheduledTimerXxx
] API creates an
NSTimer
that’s schedule in the run loop associated with the current thread. When you call it from app code, you’re typically running on the main thread, and the main thread is guaranteed to run its run loop, and hence call your timer. None of the threads within a packet tunnel provider are guaranteed to run their run loop. If you must use
NSTimer
, or any other run-loop based construct, you’ll need to create a thread specifically dedicated to running the run loop and schedule your run loop sources on that.

However, in most cases it’s possible to avoid run loop based constructs in a packet tunnel provider, and that’s what I recommend you do. For example, you can implement a timeout using a Dispatch timer source rather than

NSTimer
.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

thanks for you reply, it works this way:

self.expireTimer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(expireTime) userInfo:nil repeats:YES];

[[NSRunLoop mainRunLoop] addTimer:self.expireTimer forMode:NSRunLoopCommonModes];


I have another question:

PacketTunnelProvider's - stopTunnelWithReason: completionHandler: How long can I detect other things after the method is executed?Maybe some network requests.

it works this way

Right, but I recommend you avoid this approach for two reasons:

  • It assumes that the main thread runs its run loop, which is not guaranteed by the NE provider API. An NE provider’s main thread could just as easily call

    dispatchMain
    , which would cause the main queue to run but not the main thread’s run loop.
  • It gets you into the business of cross run loop scheduling and unscheduling. While this is supported, in my experience it’s problematic for two reasons:

    • I’ve seen cases while cross run loop schedule exposes obscure bugs in the OS.

    • Even if the OS side of this works perfectly, it’s easy to fall foul of race conditions in your own code.

I have another question:

Probably best to put that in a new thread but I’m still ‘litigating’ this issue (-:

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

ok,ty.