std::atomic trivially copyable structs

徘徊边缘 提交于 2019-12-08 05:55:22

问题


C++ reference says: http://en.cppreference.com/w/cpp/atomic/atomic

std::atomic may be instantiated with any TriviallyCopyable type T

However following example does not work under g++ 6.2.0

#include <atomic>
#include <functional>

struct Test11 {
    int x;
};
struct Test12 {
    char x;
};
struct Test13 {
    long x;
};
struct Test2 {
    char x;
    int y;
};
struct Test3 {
    int y;
    long x;
};

template<typename T, typename... ARGS>
void test(ARGS&& ... args) {
    static_assert(std::is_trivially_copyable<T>::value);

    std::atomic<T> a;
    a.store(T{std::forward<ARGS>(args)...});
}

int main() {
    test<Test11>(1);
    test<Test12>('\1');
    test<Test13>(1L);
    test<Test2>('\1',2);
    test<Test3>(1,2L);
    return 0;
}

Compile: g++-6 -std=c++14 -latomic test.cpp

/tmp/cchademz.o: In function std::atomic<Test3>::store(Test3, std::memory_order): test.cpp:(.text._ZNSt6atomicI5Test3E5storeES0_St12memory_order[_ZNSt6atomicI5Test3E5storeES0_St12memory_order]+0x3e): undefined reference to __atomic_store_16 collect2: error: ld returned 1 exit status

g++-6 --version

g++ (Ubuntu 6.2.0-7ubuntu11) 6.2.0 20161018

Especially I do not understand why Test2 works but Test3 does not.

Any ideas?

EDIT: added -latomic flag and g++ version


回答1:


As mentioned by @TartanLlama in its now-deleted answer, you need to link against libatomic:

g++-6 -std=c++14 test.cpp -latomic

You need to add -latomic at the end of the compilation line. Some compilers (linkers) may work correctly if you put -latomic before test.cpp (e.g. g++ on Coliru), but some won't (see Why does the order in which libraries are linked sometimes cause errors in GCC?).

Disclaimer: I am not an expert in linkage, so I cannot provide a detailed explanation as to why it works with -latomic before on some platform and not on other (I am guessing the linkers are different, but... ).


As to why your code compiles if you remove Test3, this is compiler and architecture dependent. If you look at the generated ASM with -O2 and g++6.2 on godbolt:

sub     rsp, 24
movabs  rax, 8589934593
mov     ecx, 5
mov     DWORD PTR [rsp], 1
mov     rdi, rsp
mov     esi, 1
mov     edx, 2
mfence
mov     BYTE PTR [rsp], 1
mfence
mov     QWORD PTR [rsp], 1
mfence
mov     QWORD PTR [rsp], rax
mfence
call    __atomic_store_16

You see that for structure that takes less than 8 bytes (Test1X, Test2), the compiler can use the mov QWORD instruction (a qword is usually 8 bytes long on nowadays architectures), but it cannot generate a single instruction to handle the cases where the size is strictly greater than 8 (sizeof(Test3) will usually be 16).

Basically, there is probably a specialization of std::atomic<T> (or of some operations of std::atomic<T>) in g++1 when T is "small", and the definition of "small" is probably architecture dependent.

Disclaimer: Again, I am not an expert regarding <atomic> so this is mainly from experiments regarding generated ASM on godbolt and behaviour of g++ and clang on Coliru.

1clang has a __atomic_store_8 procedure and a __atomic_store procedure, and without -latomic it will not compile for Test2 and Test3. However it manages to compile Test13 even if sizeof(Test13) is 8 so it does not use __atomic_store_8 for some structures. icc has a completely different behavior and does not generate any call (cannot test on Coliru, but you can look it up on godbolt).



来源:https://stackoverflow.com/questions/40739306/stdatomic-trivially-copyable-structs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!