Incorrect use of millis() for timeout function
SodaqMoja opened this issue · comments
In Sd2Card.cpp there are a few places where a timeout function is implemented. However, in each of these places the value returend by millis() is truncated to 16 bits. That can't be correct.
It's hard to actually show the effect of this flaw, but I assume that every now and then the timeout takes way too long. I'm guessing the timeout in that case will be 64 seconds instead of the intended 300, 2000.
It's hard to actually show the effect of this flaw, but I assume that every now and then the timeout takes way too long. I'm guessing the timeout in that case will be 64 seconds instead of the intended 300, 2000.
When you compute the time difference, the unsigned subtraction is not affected by a rollover:
uint16_t t0 = (uint16_t)millis();
...
if (((uint16_t)(millis() - t0)) > SD_INIT_TIMEOUT) {
you can easily verify that even if a rollover happens in the delta is always correct, for instance:
millis() - t0 == 0x00000010 - 0xFFFFFFFE == 0x00000012
but since the upper bits are discarded anyway, you can safely ignore them if you don't need them and avoid the overhead of 32 bit operations on AVR.
There are four occurrences where millis() is truncated. Two of them is missing an essential set of parenthesis, in waitNotBusy and waitStartBlock.
Here is a test sketch that shows what can happen with waitNotBusy.
#include "Arduino.h"
#define MYSERIAL Serial
void waitNotBusy(uint16_t timeoutMillis)
{
uint16_t t0 = millis();
do {
// Do nothing to make sure there is a timeout
} while (((uint16_t)millis() - t0) < timeoutMillis);
}
void setup()
{
MYSERIAL.begin(115200);
delay(1000);
}
void loop()
{
waitNotBusy(2000);
MYSERIAL.println(millis());
}
It prints
3001
5001
...
63001
65001
And then hangs.
Two of them is missing an essential set of parenthesis, in waitNotBusy and waitStartBlock.
I see, it seems that you have a point here, thanks for digging it out!
I'm preparing a patch to fix this in a minute.