joeycastillo / Sensor-Watch

A board replacement for the classic Casio F-91W wristwatch
Other
1.17k stars 234 forks source link

Bug: Event race condition #278

Open hchargois opened 1 year ago

hchargois commented 1 year ago

I've noticed that button presses are sometimes "skipped", i.e. I press a button and no BUTTON_UP event arrives to the face's loop function. I've also noticed that the higher the requested tick_frequency, the more often buttons presses are skipped.

I've created this PR to demonstrate the issue, with a simple face that runs at a 64 Hz tick frequency, and counts up on each press of the alarm button, or at least it should. When running in the simulator, for 10 presses of the button, the counter only goes up by around 3 to 5, so more than half of the button presses are skipped.

What I think happens, is that:

I've added a naive fix in which the tick handler avoids overwriting the event if it's not empty. This seems to fix the counter face: 10 button presses now reliably increment the counter 10 times.

I don't expect this fix to be the best way to fix the issue, so I don't expect this PR to be merged, I just wanted to demonstrate the bug and give weight to the race condition theory above with a simple fix.

I imagine a proper fix may be to add a queue of pending events and call the face's loop multiple times in a row if there are multiple pending events. Maybe the tick events also need to be handled differently than the other ones: if we are falling behind tick events, we don't want to replay them all, we usually want only the "latest" tick event.

I should also say that I don't have the actual board yet, so these observations are made with the simulator. But since I think this bug is in movement.c and not in the watch library, I expect the same bug to be exhibited by the hardware board. Unless there's something different with how interrupts are handled.

matheusmoreira commented 7 months ago

I have also noticed this problem on hardware but it's especially noticeable in the emulator.

I imagine a proper fix may be to add a queue of pending events and call the face's loop multiple times in a row if there are multiple pending events. Maybe the tick events also need to be handled differently than the other ones: if we are falling behind tick events, we don't want to replay them all, we usually want only the "latest" tick event.

Completely agree. A circular buffer seems like a good way to implement this queue.

Buffering events will add latency which could impact user experience. The target is a microcontroller running on bare metal: can we make any guarantees regarding event handling times?