ARMmbed / mbed-os

Arm Mbed OS is a platform operating system designed for the internet of things
https://mbed.com
Other
4.63k stars 2.96k forks source link

mbed::CircularBuffer interrupt safe but not thread safe? #15497

Open schnoberts1 opened 4 months ago

schnoberts1 commented 4 months ago

Description of defect

The ARMbed synchronisation description is as follows:

e.g. If it's marked Interrupt safe it's also thread safe.

mbed::CircularBuffer is marked thread safe but it's incrementCounter method is not thread safe. There's also a lack of fences, perhaps this isn't needed, but if that's the case it should be commented.

I think there's either a defect in the buffer or there's a defect in tagging this method as interrupt safe when that is defined as also being safe for use on multiple threads. As far as I can tell being in a critical section is not the same as an operation being atomic. Am I wrong and a critical section acts as a global lock, it looks to me like all it does is provide re-entrant disabling of IRQs?

Target(s) affected by this defect ?

Any developer using this class to communicate between threads.

Toolchain(s) (name and version) displaying this defect ?

All

What version of Mbed-os are you using (tag or sha) ?

mbed-os-6.17.0

What version(s) of tools are you using. List all that apply (E.g. mbed-cli)

N/A

How is this defect reproduced ?

I simply read the code, but any multi-thread high throughput test case would work, as would reading the resulting assembler.

0xc0170 commented 1 month ago

`> mbed::CircularBuffer is marked thread safe but it's incrementCounter method is not thread safe. There's also a lack of fences, perhaps this isn't needed, but if that's the case it should be commented.

It's private method. Whoever changes the class implementation should follow the synchronization promise (invoking private methods that are not protected must be in the protected block otherwise its breaking its promise). Or what have I missed?

schnoberts1 commented 1 month ago

It was an example and I should have provided context.

My confusion leads from this :

void push(const T &data)
    {
        core_util_critical_section_enter();

        _buffer[_head] = data;

        _head = incrementCounter(_head);

        if (_full) {
            _tail = _head;
        } else if (_head == _tail) {
            _full = true;
        }

        core_util_critical_section_exit();
    } 

It calls incrementCounter. It’s wrapped in these critical sections calls which call hal_critical_section_enter which call __disable_irq but this isn’t a serialisation point (a lock) or a fence is it? If I am right then I expected incrementCounter to be thread safe but it doesn’t seem to be.

Sorry for the confusing problem statement.

I don’t understand how this circular buffer is thread safe. I’d like use it so if I am mistaken that’s awesome :)