Control exact polling rates using uasyncio

Discussion about programs, libraries and tools that work with MicroPython. Mostly these are provided by a third party.
Target audience: All users and developers of MicroPython.
ltmerlin
Posts: 32
Joined: Fri Jun 28, 2019 12:34 pm

Re: Control exact polling rates using uasyncio

Post by ltmerlin » Thu Jan 30, 2020 9:49 pm

Thank you Kevin for this info!
I will look into the uart implementation in the firmware and try to find the size.
A ring-buffer seems a good choice, but I’m not sure how I still can be certain that we won’t access fragments of the buffer at the same time. The piece of memory holding the pointer is still vulnerable for that or do I see this wrong?
I will implement and test it of with all the suggestions and tips. The thread-safety is a difficult one to test thoroughly...

kevinkk525
Posts: 727
Joined: Sat Feb 03, 2018 7:02 pm

Re: Control exact polling rates using uasyncio

Post by kevinkk525 » Thu Jan 30, 2020 10:02 pm

no we are not using threads. in threads you would have the problem that the control could be taken away from both threads.
In this case the control can only be taken from the main loop. However, the main loop only accesses the pointer but doesn't modify it. The interrupt always runs to end and therefore always modifies the pointer correctly.
However, you are correct, there is a chance the the interrupt is modifying the pointer integer at the same time the main loop is accessing it. And since an integer is multiple bytes there is a chance that the main loop only read the first byte and then the interrupt modifies the pointer and suddenly the main loop has read a different integer than it should have.
This is hardly avoidable though because there's nothing you can do inside the interrupt to make the main loop read the correct value if it is already accessing the pointer.
When using an additional boolean lock you would then know if they try to access it at the same time but you'd still not be able to do anything about it.

However, if the pointer stays within the range of 1 Byte (e.g. <100) this should not be an issue because since only ever 1 Byte of that integer gets changed, therefore the whole integer can't be changed during the main loop accesses it and therefore the main loop always reads a correct value.
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

User avatar
dhylands
Posts: 3385
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Control exact polling rates using uasyncio

Post by dhylands » Thu Jan 30, 2020 10:47 pm

On most 32-bit architectures (and definitely on the ARM), retrieving or storing a 32-bit value is an atomic operation. You'll never get 3 bytes from one time and an additional byte from an interrupt handler. You still have the potential problem that the main loop may have just retrieved the value and the interrupt handler changes the value and the main loop is now using the old value.

Of course, loading a value, modifying it and saving it is definitely not atomic (i.e. incrementing a value)

User avatar
pythoncoder
Posts: 4232
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Control exact polling rates using uasyncio

Post by pythoncoder » Fri Jan 31, 2020 8:23 am

Further than that, we are discussing Python not machine code.

As I understand it, MicroPython interrupts cannot interrupt a Python instruction. Python code which modifies a Python object will produce multiple bytecodes. The VM will execute even more machine opcodes to process the bytecodes. The interrupt must be delayed until that sequence is complete for consistency to be maintained at the Python level.
Peter Hinch

kevinkk525
Posts: 727
Joined: Sat Feb 03, 2018 7:02 pm

Re: Control exact polling rates using uasyncio

Post by kevinkk525 » Fri Jan 31, 2020 8:28 am

On most 32-bit architectures (and definitely on the ARM), retrieving or storing a 32-bit value is an atomic operation. You'll never get 3 bytes from one time and an additional byte from an interrupt handler.
I always thought the interrupt can interrupt while another operation is reading an object and that only a 1Byte operation is atomic, but I might be wrong.
Of course, loading a value, modifying it and saving it is definitely not atomic (i.e. incrementing a value)
Since only the interrupt is modifying the integer that is not a problem.
You still have the potential problem that the main loop may have just retrieved the value and the interrupt handler changes the value and the main loop is now using the old value.
That is actually no problem in a ring buffer because the next iteration of the main loop will just continue from where it last left. If the interrupt modifies the pointer right after the main loop read it, that next value will just be part of the next loop iteration.
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

User avatar
pythoncoder
Posts: 4232
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Control exact polling rates using uasyncio

Post by pythoncoder » Fri Jan 31, 2020 9:12 am

kevinkk525 wrote:
Fri Jan 31, 2020 8:28 am
...
I always thought the interrupt can interrupt while another operation is reading an object and that only a 1Byte operation is atomic, but I might be wrong.
As Dave says this is arch dependent. For example an N byte wide bus can enforce atomic N byte access at the hardware level.
That is actually no problem in a ring buffer
This is correct. Ring buffers are thread-safe because one process controls the write pointer and the other controls the read pointer. If either process reads the pointer which it doesn't control, and gets data which is out of date, the outcome is safe. For example the read process might get one byte fewer than have arrived at that moment in time.
Peter Hinch

kevinkk525
Posts: 727
Joined: Sat Feb 03, 2018 7:02 pm

Re: Control exact polling rates using uasyncio

Post by kevinkk525 » Fri Jan 31, 2020 10:56 am

pythoncoder wrote:
Fri Jan 31, 2020 9:12 am
kevinkk525 wrote:
Fri Jan 31, 2020 8:28 am
...
I always thought the interrupt can interrupt while another operation is reading an object and that only a 1Byte operation is atomic, but I might be wrong.
As Dave says this is arch dependent. For example an N byte wide bus can enforce atomic N byte access at the hardware level.
How big is N for esp8266, esp32, Pyboard?
I was always thinking from esp8266 since I use that all the time, my mistake :D Of course there's a big difference between architectures and hardware.
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

Post Reply