Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alignment of atomic variables

Tags:

c++

c++11

atomic

Naturally, in order for a typical modern processor architecture (like x86_64) to perform an atomic load or store, the data to be read/written needs to be aligned.

But how is this requirement actually realized/enforced via C++11 <atomic> variables?

Suppose I have an architecture that supports 16-byte compare and swap (double-word CAS), so it can atomically read/write 16-byte values, and I define a 16-byte type:

struct double_word
{
    std::uint64_t x;
    std::uint64_t y;
};

Now, suppose I include an std::atomic<double_word> as a member field of a class:

class foo
{
    public:

    std::atomic<double_word> dword;
};

How do I know foo::dword is actually aligned on a 16-byte boundary? How do I know a call to dword.load() would actually be atomic?

Actually, I originally starting asking this question because of an odd thing that happened when I added another data member before foo::dword. I defined foo as:

class foo
{
    public:

    std::uint64_t x;
    std::atomic<double_word> dword; 
};

When I actually perform an atomic load on foo::dword, and compile and run using GCC 4.7.2 on an x86_64 machine running Debian Linux, it actually gives me a Segmentation Fault!

Full program:

#include <atomic>
#include <cstdint>

    struct double_word
    {
        std::uint64_t x;
        std::uint64_t y;
    };

    class foo
    {
        public:

        std::uint64_t x;
        std::atomic<double_word> dword; // <-- not aligned on 16-byte boundary
    };

    int main()
    {
        foo f;
        double_word d = f.dword.load(); // <-- segfaults with GCC 4.7.2 !!
    }

This actually segfaults on f.dword.load(). At first I didn't understand why, but then I realized that dword is not aligned on a 16-byte boundary. So, this leads to a lot of questions like: what should the compiler do if an atomic variable is not aligned and we try to atomically load it? Is it undefined behavior? Why did the program simply segfault?

Secondly, what does the C++11 standard say about this? Should the compiler make sure that double_word is automatically aligned on a 16-byte boundary? If so, does that mean GCC is simply buggy here? If not - it would seem it is up to the user to ensure alignment, in which case any time we use an std::atomic<T> larger than one byte, it would seem we'd have to use std::aligned_storage to ensure it is properly aligned, which (A) seems cumbersome, and (B) is something I've never actually seen done in practice or in any examples/tutorials.

So, how should a programmer using C++11 <atomic> handle alignment issues like this?

like image 495
Siler Avatar asked Nov 02 '14 19:11

Siler


1 Answers

It is a GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65147 Just adding alignas(16) is fixing the problem.

#include <atomic>
#include <cstdint>

struct double_word
{
    std::uint64_t x;
    std::uint64_t y;
};

class foo
{
    public:

    std::uint64_t x;
    alignas(16) std::atomic<double_word> dword; // <-- not aligned on 16-byte boundary
};

int main()
{
    foo f;
    double_word d = f.dword.load(); // <-- segfaults with GCC 4.7.2 !!
}
like image 168
Denis Zaikin Avatar answered Nov 12 '22 00:11

Denis Zaikin