I have an electron.js application installed in a raspberry pi. The app displays date and time, the initial date and time is taken from a server and is incremented on the application using setInterval
with a 1000 ms timer. What's weird is I have 3 of this devices running the same application, initially all the dates and time are the same but one of the devices is now ahead of the other 2. The application has been opened for about 5 hours now and one of the device is ahead about 12 seconds.
-
Welcome -- but that the application is installed in a Raspberry Pi does not make it a Raspberry Pi question any more than installing it on a MacBook would make it a question for apple.stackexchange.com -- general programming belongs on Stack Overflow.– goldilocks ♦Commented Oct 16, 2020 at 14:32
-
1@goldilocks I see. I posted here because I was thinking nothing was wrong with the code because it works on other devices, I thought this could probably be some hardware issue or something.– crimson589Commented Oct 16, 2020 at 22:20
-
1I understand. I do the same thing on a daily basis ("My code is fine it must be a bug with ____") and I am usually wrong too ;) just I've done it enough to know not to start investigating with that premise. Even when I'm positive it isn't my own error, I'll take it to SO or somewhere and say, "This code isn't doing what I think it should, why could that be?".– goldilocks ♦Commented Oct 16, 2020 at 22:24
-
@goldilocks I think a question about a piece of code which works fine on a PC but not on a Pi is a legitimate Pi question. The fact that the root cause is in a node.js function rather than Pi hardware is not obvious until after the question is answered.– Dmitry GrigoryevCommented Oct 19, 2020 at 8:14
-
Well, the problem was solved anyway, but: "a question about a piece of code which works fine on a PC" -> I see no mention of this anywhere here. In a comment (from after it was closed), s/he does say, "works on other devices", this is a bit ambiguous since in another comment (from before it was closed), "is only happening on one device? the other 2 are fine", other here referring to the other two pis. It is a gray area and I am fine re-opening it (BTW: In future if you feel this way vote it into the re-open queue, I review all of those).– goldilocks ♦Commented Oct 19, 2020 at 14:21
2 Answers
setInterval
is inherently not accurate (as can be seen here)
Not sure what code you used, but if you are not using Date
, you might want to consider it.
i.e the following example will drift
var displayDate = 0; // the date to be displayed
setInterval(() => {
displayDate++;
}, 1000);
And this will not:
var startDate = Date.now(); // Date in MS since 1/1/1970
var displayDate = 0;
setInterval(() => {
displayDate = (Date.now() - startDate) / 1000;
}, 1000);
-
I'm using moment.js and adding 1 second every 1000 ms to a moment date object. Even so, how come this is only happening on one device? the other 2 are fine. Commented Oct 16, 2020 at 13:46
-
They are probably all drifting, but one drifted in a different pace Commented Oct 16, 2020 at 14:00
-
1As per the above, if you want an accurate clock, check it against the system occasionally (or base it completely on that). The Pi does not have an RTC but whilst running, like pretty much any microprocessor depending on a crystal oscillator for timing, they can keep it very accurately -- drift is not impossible but is will be no where near 12 seconds in 5 hours.– goldilocks ♦Commented Oct 16, 2020 at 14:38
I still wondered how that could be a code issue when the code was fine on other pi's
Did you have a look at the Stack Overflow Q&A that Tomir linked? Read in particular the accepted answer and the other links provided therein. The basic issue is that javascript/node.js is not the operating system, and it is the operating system it must depend on, but modern multi-tasking OS's do not actually provide the kind of guarantees perfect accuracy would require here. A couple of points about this:
That something like this usually or mostly works does not mean that this is because there some guarantee that it will be perfectly accurate. At the same time there is no guarantee, there is a pretty good chance that it will, and in that sense, 12 seconds of drift in 5 hours is actually not bad. It is totally sufficient for the kinds of things
setInterval
would normally be used for, short durations in timed UI events.The reason the OS does not guarantee perfect timing of this sort is that there is a degree of indeterminacy in the operation of a contemporary, multi-tasking operating system. This is because, even when you have a multi-core system, they must time slice tasks because there are almost certainly too many of them running at any given point to devote a core to each of them for as long as they like -- that would be a very much more specialized environment.
"Time slicing" does not mean I let process X run for exactly and precisely 1512 ms, etc. It means I let it run until I want to interrupt it, and at that point there are rules -- conventions, and ones that derive from the physics of electronics -- about exactly how it can be interrupted. You cannot use some arbitrary wall clock nanosecond. In essence, it happens when the process can be interrupted. Or you might look at in terms of how long it takes to interrupt it, which is indeterminate within certain bounds (it isn't going to be minutes or hours or generally even seconds, but it will not be precisely 150 ns every time, etc).
Hence, userspace operations (that's non-kernel code) are considered to have a certain latency. For example, on standard linux as runs on the pi, if you want to do extremely high frequency things, the latency/precision is probably going to average 20-25 ms under the best of normal circumstances. Things that require better precision than this have some implementation in hardware, meaning they have some direct wiring to a clock. Buses like USB (and SPI, and I2C, and UART) work this way.
What that latency translates to, if you are trying to measure time very precisely, is drift.
For things that need to be timed over a long period, or stay in step with a wall clock, the normal approach (and the one Tomir recommends), is to use the system time. Think of it like this: You and I are sitting somewhere, and I have a watch, but you do not. You need to do something in exactly ten minutes. However, I am not going to time it for you because I am busy. What I will do is tell you the exact time when you ask for it.
Asking every five seconds is going to turn into a lot of wasted time for everyone. So, you could count in your head two minutes and then check to see how accurate you were by asking me. If the time I give you is 135 seconds after the last one, you know that more than two minutes has passed and you know exactly how much more, so some simple arithmetic is all you need.1 This isn't a perfect analogy (setInterval()
is not mouthing numbers in the background), but hopefully the point is clear.
The system time actually can drift too, which is why online computers (including pis) now normally periodically sync with a timeserver; network timeservers eventually all converge on the best ones, which will use atomic clocks in some facility somewhere (which use cesium instead of quartz crystal oscillators). Sans that, this amount of drift is generally pretty small, however -- fractions of a second a day.
- So why doesn't
setInterval()
do this under-the-hood? Because while that approach might work when the time is less than requested, the callback does not include a parameter to indicate how much it went over by, if it went over. But of course, you can check directly by consulting the system time in the callback...