Опасно ли полагаться на переполнение?

0

Ниже приведена функция задержки, которую я нашел в нашей прошивке. Это выглядит немного опасно или, по крайней мере, смущает читателя.

Глобальная переменная

static int32u masterTimerCounter; // 32-bit unsigned timer counter

Обработчик прерываний системного тика

/* Called Every Millisecond */
void sysTickIrqHandler(void)
{
    masterTimerCounter++;
}

Установка срока действия таймера

void setTimerExpiration(int32u *timerExpiration, int32u delay)
{
    *timerExpiration = masterTimerCounter + delay;
}

Проверьте, не истечет ли срок действия таймера

boolean timerExpired(int32u timerExpiration, int32u delay)
{
    if((masterTimerCounter - timerExpiration) < delay)
        return TRUE; // Timer has expired
    else
        return FALSE; // Timer still active
}

Устанавливать выключение и блокировку таймера до истечения срока действия таймера

int32u timerExpiration;
setTimerExpiration(&timerExpiration, 15); // Set expiration timer to 15 milliseconds
while(!timerExpired(timerExpiration, 15) // Block until timer has expired
    continue; 

Вопрос

Как вы можете видеть в timerExpired(), masterTimerCounter вычитается timerExpiration. Если таймер еще не истек, вычисление приведет к очень большому числу (поскольку оба операнда представляют собой непознанные числа). Когда таймер истек, вычисление приведет к значению, меньшему, чем сумма задержки.

Хотя кажется, что это работает нормально, похоже, что это может быть опасно или, по крайней мере, ввести в заблуждение читателя (я должен был прочитать его несколько раз, чтобы понять первоначальное намерение программиста).

Если бы мне пришлось написать что-то похожее на это, я бы определил функцию timerExpired следующим образом:

boolean timerExpired(int32u timerExpiration)
{
    if(timerExpiration > masterTimerCounter)
        return FALSE; // Timer still active
    else
        return TRUE; // Timer has expired
}

Должен ли я переопределить "timerExpired()"?

Примечание. Имена функций и переменных были изменены для защиты невинных.

  • 0
    Ты в порядке с игнорированием delay тогда?
  • 2
    timerExpired с задержкой в качестве параметра здесь не имеет смысла.
Показать ещё 6 комментариев
Теги:
timer
counter
delay

3 ответа

1
Лучший ответ

Проблема с вашим путем заключается в том, что если masterTimerCounter + delay вызывает опрокидывание 32-битного int, то тест timerExpired проходит сразу.

Я думаю, что самый простой способ делать целочисленные таймеры при наличии возможного опрокидывания выглядит так:

void startTimer(int32u *timerValue)
{
    *timerValue = masterTimerCounter;
}

Проверьте, не истечет ли срок действия таймера

boolean timerExpired(int32u timerVal, int32u delay)
{
    if ((masterTimerCounter - timerVal) >= delay)
        return TRUE; // Timer has expired
    else
        return FALSE; // Timer still active
}

Применение:

int32u timer;
startTimer(&timer); // Start timing
while(!timerExpired(timer, 15) // Block for 15 ticks
    continue;

Даже если вычитание в timerExpired возвращает правильные результаты.

  • 0
    Наличие неподписанного int-ролловера - это не мой путь, а тот, который есть сейчас. Мой вопрос, это опасно, и если да, я должен изменить это. То, как я думал, что я должен изменить это, находится на дне моего вопроса.
  • 0
    Представьте, что masterTimer равен 2 ^ 16-10, и вы вызываете его с задержкой 15. Можете ли вы гарантировать, что ваша система не будет работать так много миллисекунд непрерывно?
Показать ещё 2 комментария
5

Обратите внимание, что исходная логика была чем-то вроде: это абсолютное время истечения в прошлом, но меньше, чем полная задержка. Может быть, мы могли бы выразить это свободно, как недавно этот огонь по таймеру.

Ваша модифицированная логика - это просто абсолютное время истечения в прошлом, которое отличается.


Вы можете тривиально избегать риска timerExpiration просто добавляя timerExpiration к каждой стороне неравенства:

boolean timerExpired(int32u timerExpiration, int32u delay)
{
// WAS: (masterTimerCounter - timerExpiration) < delay
    if(masterTimerCounter < timerExpiration + delay)
        return TRUE; // Timer has expired
    else
        return FALSE; // Timer still active
}

но это изменяет поведение, так как вы говорите, что оригинал всегда будет ложным, если masterTimerCounter < timerExpiration. Вы можете получить исходное поведение без путаного нижнего потока, явно указав это:

boolean timerExpired(int32u timerExpiration, int32u delay)
{
    if(masterTimerCounter > timerExpiration &&       // did it expire ...
       masterTimerCounter < timerExpiration + delay) // ... recently?
        return TRUE; // Timer has expired
    else
        return FALSE; // Timer still active
}
  • 0
    +1 за слово логика .
2

Этот код прошивки не имеет смысла.

int32u expire;
setTimerExpiration(&expire, 0);
timerExpired(expire, 0); // is always false, unless the timer overflows 
  • 0
    Это никогда не может быть верно с задержкой 0, но это может быть верно с задержкой 15, как в вопросе

Ещё вопросы

Сообщество Overcoder
Наверх
Меню