Is it safe to use Atomic<Range<float>> in various threads?

Hello,
I am sorry, I am quite noob in Atomic subjects. I was always sure that juce::Atomic (or std::atomic) work properly only on basic primitives like int, bool, float etc. But I’ve just found out the line:

static_assert (std::atomic<Range<float>>::is_always_lock_free);

is compilable. And as far as I understand it, if std::atomic<T>::is_always_lock_free returns true it means that type T can be used as an atomic variable. But juce::Range<float> seems to be much more complex object than basic float.

So I would like to ask if it is safe if in DSP thread I use (read) object of:

Atomic<Range<float>> myAtomicRange;

which can be set in message thread?

To be more detailed, in DSP thread I want to use myRange by calling:

myAtomicRange.get().getStart();
myAtomicRange.get().getEnd();

And in message thread I set it be calling:

myAtomicRange.get.set({newStart, newEnd});

For any help great thanks in advance.

Looking at the code shows that the only two members that need to be copied by the default implementation is assigning two floats:

That means it is 2 * 32 bit, which fits into a single operation on a 64 bit architecture.

The methods don’t add to the size of the Range. Those trivial methods will be inlined most likely anyway.

So I think the compiler is right :slight_smile:

Great thanks for your answer.
I would understand your answer, but I’ve just found out that:

static_assert (std::atomic<Rectangle<float>>::is_always_lock_free);

is compilable too. But in Rectangle<float> there are 4 floats. Two in position (Point<float>) and another two as a size (width and height).

So what about that?

hmm, interesting. Seems I didn’t fully understand it myself then. I’ll wait for somebody else to correct me :slight_smile:

1 Like

I don’t know why it works, but it seems that four floats can be atomic (at least on my computer):

#include <iostream>
#include <atomic>

int main() {
    struct myStruct {
        float a = 0, b = 0, c = 0, d = 0;
    };
    std::cout << std::atomic<myStruct>::is_always_lock_free;
}
#include <iostream>
#include <atomic>

int main() {
    struct myStruct {
        float a = 0, b = 0, c = 0, d = 0, e = 0;
    };
    std::cout << std::atomic<myStruct>::is_always_lock_free;
}

The first one outputs 1 and the second one outputs 0.


Seems to be related to CPU.