Nested millis timers crashes my sketch?

I have a pair of nested millis timers that is producing a stack crash when it executes. I have a workaround that works, but I am curious why nested millis timers should crash like this. My sketch is approaching over 1,000 lines of code, so is this a warning that my stack is running out of space??

Here's the code segment that crashes:

// Loop here for i*1000 ms, checking offHook every 500 ms.
// If the user hung up the handset, exit.
long j;
while (millis() < ringbackStartTime + (i * 1000)) {    // Waiting for timeout
  j = millis();
  if (millis() > j + 500) {
    if (!offHook()) return;                            // Abort if the user hung up the handset.
    }
  }
}

And, here is my workaround that doesn't crash. Blocking code is OK here:

// Loop here for i*1000 ms, checking offHook every 500 ms.
// If the user hung up the handset, exit.
while (millis() < ringStartTime + (i * 1000)) {     // Waiting for timeout
  if (!offHook()) return;                           // Abort if the user hung up the handset.
  delay(500);
}

Here is the crash, truncated:

Soft WDT reset

>>>stack>>>

ctx: cont
sp: 3ffffd30 end: 3fffffc0 offset: 01b0
3ffffee0:  00000000 00000000 4bc6a7f0 00000000  
3ffffef0:  3ffee716 3ffee9c0 40100aac 00003f7c  
3fffff00:  00000000 00000020 3ffefd64 40204399  
3fffff10:  00496836 0000000d 3ffee9c0 40204424

Not sure why you're crashing, but I do know for certain that this line has a bug:

while (millis() < ringbackStartTime + (i * 1000)) {

Will crash at millis rollover. It should be written as:

while (millis() - ringbackStartTime < (i * 1000))

timing should always be handled with subtraction to find intervals, not addition to predict future times. The former is safe. The latter is bugged.

j = millis();
  if (millis() > j + 500) {

Think about this for just a second. If j is set equal to millis, then what are the chances that one instruction later it is suddenly 500ms later? That if will NEVER be true.

Delta_G:
timing should always be handled with subtraction to find intervals, not addition to predict future times. The former is safe. The latter is bugged.

Thanks. That's good to know. am I understanding correctly that millis() may take an awful long time to be less than startTime + interval if the addition would go over the long rollover?

I have an awful lot of millis timers in my sketch to fix because I am trying real hard to avoid blocking code. But since this program won't be running more than a day, this is unlikely to trip me.

Delta_G:

j = millis();

if (millis() > j + 500) {




Think about this for just a second. If j is set equal to millis, then what are the chances that one instruction later it is suddenly 500ms later? That if will NEVER be true.

You're right- that inner loop should be a do-while instead of an if-then.

long j;
while (millis() - ringbackStartTime < (i * 1000)) { 
  j = millis();
  do {
    if (!offHook()){
      return;
    }
    while (millis() -j < 500) 
}

Where are you getting that crash report from? It has Soft WDT Reset right at the top of it, that doesn't sound like a stack overflow crash to me. Are you enabling the watchdog timer anywhere in your sketch? If so the problem isn't your stack crashing, but you're getting stuck in an infinite loop and not resetting the watchdog timer.

It doesn't even need to be an infinite loop, it only needs to be some code that blocks loop longer than the watchdog timer timeout duration. If that's the case, you can work around it by calling yield(), which resets the watchdog timer. But it's best to avoid blocking code if possible.