About Delay issues with iPhone VoIP applications

We are encountering the following issue with our VoIP application for iPhone, published on the App Store, and would appreciate your guidance on possible countermeasures.

The VoIP application (callee side) utilizes a Wi-Fi network. The sequence leading to the issue is as follows:

  1. VoIP App (callee): Launches
  2. iPhone (callee): Locks (e.g., by short-pressing the power button)
  3. VoIP App (callee): Transitions to a suspended state
  4. VoIP App (caller): Initiates a VoIP call
  5. VoIP App (callee): Receives a local push notification
  6. VoIP App (callee): Answers the incoming call
  7. VoIP App (callee): Executes performAnswerCallAction()

After this, the VoIP App (callee) uses "NSTimer scheduledTimerWithTimeInterval" to manage internal processing timing. However, the processing sometimes takes longer than the specified waiting time. Specifically, delays of several seconds can occur.

We understood that if the user is interacting with the screen and both the iPhone and the VoIP app are in an active state, the VoIP app's processing would not be delayed. However, can significant delays (several seconds) in application processing still occur even when the iPhone is in an active state (i.e., the user is interacting with the screen)?"

We understood that if the user is interacting with the screen and both the iPhone and the VoIP app are in an active state, the VoIP app's processing would not be delayed. However, can significant delays (several seconds) in application processing still occur even when the iPhone is in an active state (i.e., the user is interacting with the screen)?"

Two questions:

  1. What thread does the timer target? The simplest way to delay timer firing is to block the runloop the timer is targeting.

  2. Have you set NSTimer.tolerance? While the documentation says that it defaults to "0", I'd suggest setting it to "0" yourself (assuming that what you want).

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

What thread does the timer target? The simplest way to delay timer firing is to block the runloop the timer is targeting.

Sometimes this delays occur, and sometimes they don't. Think of timers as targeting a dedicated call-controll processing thread and not blocking the run loop.

Have you set NSTimer.tolerance? While the documentation says that it defaults to "0", I'd suggest setting it to "0" yourself (assuming that what you want).

I also considered NSTimer.tolerance and, as per your suggestion, explicitly set it to 0. However, this had no discernible effect, likely because its default value is already 0. Given this, do you have any other suggestions for ensuring reliable timer firing?

I also considered NSTimer.tolerance and, as per your suggestion, explicitly set it to 0. However, this had no discernible effect, likely because its default value is already 0.

OK. The delays you're talking about are long enough that I was never all that confident that this was the issue, but it's an easy enough "tweak" that I thought it was worth trying.

Moving to here:

Given this, do you have any other suggestions for ensuring reliable timer firing?

So, let me start with this:

Think of timers as targeting a dedicated call-controlled processing thread and not blocking the run loop.

How does this actually work? Are you simply running NSRunLoop or CFRunLoop on a dedicated thread? Or are you doing something more complicated? Critically, is this a dedicated thread your app uses "forever" or are you creating/destroying it? FYI, my recommendation here would be that you create the thread once and use it forever.

Moving to here:

Specifically, delays of several seconds can occur.

Given the length of the delay you're talking about, the most likely explanation is that the timer thread was busy doing something else.

This is by far the most common issue and, unfortunately, it can be tricky to debug since you don't know there is "a problem" until AFTER the problem has happened. If you're able to reproduce this in a controlled/testing environment (even intermittently), one "trick" I've had success with is to do something like the following:

  1. Create another dedicated thread with a single timer attached to it.

  2. That timer is configured so that it calls "abort()" if it ever fires.

  3. Every time your primary timer (the one that's being delayed) fires, it uses NSTimer.fireDate to push the fire time of #2 "further" into the future, past the time of the primary timers next expected firing.

  4. If the timer ever fires, it calls abort() to intentionally crash your app.

Putting that in more concrete terms, if your timer is supposed to fire every "3s", then the first thing it does every time it runs is to set the fireDate for #3 to "now+5s". If everything is working properly, then that keeps pushing the timer into the future, preventing it from ever firing. However, if your primary timer is ever delayed for more than 2s, then the timer fires, crashing your app and handing you a crash log showing exactly what the timer thread was doing when it "should" have been running the timer.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thank you for your advice.

In the end, we decided to address the issue by changing the timer setting to a value that took into account the delays we had observed so far.

This issue can be closed.

In the end, we decided to address the issue by changing the timer setting to a value that took into account the delays we had observed so far.

Fair enough, that works too!

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

About Delay issues with iPhone VoIP applications
 
 
Q