为什么Apple clang在'官方'clang支持时不允许使用C++ 11 thread_local

acm*_*acm 23 xcode clang c++11

下面是一个简单的程序,它在共享库中使用非POD类型的C++ 11 thread_local变量进行测试.

如果我使用自制软件,这很好用:

> /usr/local/Cellar/llvm/3.5.0_2/bin/clang --version                                          
clang version 3.5.0 (tags/RELEASE_350/final)
Target: x86_64-apple-darwin14.0.0
Thread model: posix

> cmake .. -G Ninja -DCMAKE_C_COMPILER=/usr/local/Cellar/llvm/3.5.0_2/bin/clang -DCMAKE_CXX_COMPILER=/usr/local/Cellar/llvm/3.5.0_2/bin/clang++
-- The C compiler identification is Clang 3.5.0
-- The CXX compiler identification is Clang 3.5.0
-- Check for working C compiler using: Ninja
-- Check for working C compiler using: Ninja -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler using: Ninja
-- Check for working CXX compiler using: Ninja -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
> ninja all
...                                                                                      

>  ./main                                                                                       
XXX LifeCycle::LifeCycle 0x7fedc0c04b90
X before: -17
XXX LifeCycle::LifeCycle 0x7fedc0c04c10
X before in thread: -17
X after in thread: 2
XXX LifeCycle::~LifeCycle 0x7fedc0c04c10
X after: 1
XXX LifeCycle::~LifeCycle 0x7fedc0c04b90
Run Code Online (Sandbox Code Playgroud)

但是,如果我尝试使用Apple Clang,我会收到一条错误消息,指出它不受支持:

> /usr/bin/clang --version
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin14.0.0
Thread model: posix
> cmake .. -G Ninja -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++
-- The C compiler identification is AppleClang 6.0.0.6000056
-- The CXX compiler identification is AppleClang 6.0.0.6000056
-- Check for working C compiler using: Ninja
-- Check for working C compiler using: Ninja -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler using: Ninja
-- Check for working CXX compiler using: Ninja -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to:

> ninja all
[1/4] Building CXX object CMakeFiles/lib.dir/lib.cpp.o
FAILED: /usr/bin/clang++   -Dlib_EXPORTS -Wall -std=c++11 -mmacosx-version-min=10.7 -stdlib=libc++ -fPIC -MMD -MT CMakeFiles/lib.dir/lib.cpp.o -MF CMakeFiles/lib.dir/lib.cpp.o.d -o CMakeFiles/lib.dir/lib.cpp.o -c ../lib.cpp
../lib.cpp:23:5: error: thread-local storage is unsupported for the current target
    thread_local LifeCycle lc;
    ^
1 error generated.
ninja: build stopped: subcommand failed.
Run Code Online (Sandbox Code Playgroud)

任何人都可以提供任何洞察到为什么Apple的铿锵变种懦弱拒绝尊重thread_local,尽管底层编译器支持它,并且生成的代码似乎有效?

lib.h:

#pragma once

int doit(int) __attribute__((__visibility__("default")));
Run Code Online (Sandbox Code Playgroud)

lib.cpp:

#include "lib.h"

#include <thread>
#include <cstdlib>
#include <cstdio>

namespace {

    class LifeCycle {
    public:
        LifeCycle()
            : x(-17) {
            printf("XXX LifeCycle::LifeCycle %p\n", this);
        }

        ~LifeCycle() {
            printf("XXX LifeCycle::~LifeCycle %p\n", this);
        }

        int x;
    };

    thread_local LifeCycle lc;
} // namespace

int doit(int arg) {
    printf("X before: %d\n", lc.x);
    lc.x = arg;
    std::thread xwriter([arg]() {
            if (lc.x == arg)
                abort();
            printf("X before in thread: %d\n", lc.x);
            lc.x = arg + 1;
            printf("X after in thread: %d\n", lc.x);
        });
    xwriter.join();
    printf("X after: %d\n", lc.x);
    return (lc.x == arg ? EXIT_SUCCESS : EXIT_FAILURE);
}
Run Code Online (Sandbox Code Playgroud)

main.cpp中:

#include "lib.h"

int main(int argc, char* argv[]) {
    return doit(argc);
}
Run Code Online (Sandbox Code Playgroud)

的CMakeLists.txt:

cmake_minimum_required(VERSION 3.1)

set(CMAKE_CXX_FLAGS "-Wall -std=c++11 -mmacosx-version-min=10.7 -stdlib=libc++")

add_library(lib SHARED lib.cpp)
add_executable(main main.cpp)
target_link_libraries(main lib)
Run Code Online (Sandbox Code Playgroud)

rsf*_*inn 43

Xcode 8和9中包含的clang编译器支持C++ 11 thread_local关键字.此功能已添加到Xcode 8测试版中,如WWDC 2016视频"LLVM中的新功能"所述,从5:50开始.(外部成绩单)

问题中列出的示例程序在OS X 10.11.6下与Xcode 8 GM一起编译并运行,以及在macOS 10.13.4下与Xcode 9.3一起运行,并在每种情况下生成预期输出.

关于iOS,我通过实验发现thread_localiOS 9及更高版本支持,但iOS 8.4或更早版本不支持.


对于Xcode 7.x及更早版本,以下是2014年苹果工程师在旧Apple开发者论坛上的答案(不再可访问):

我们不支持开源Clang的thread_local实现,因为我们相信我们可以使用动态链接器中的各种功能为我们的平台提供更高性能的实现.这样的实现将与开源Clang中的实现ABI不兼容,因此我们将不会支持thread_local,直到我们得到一个我们可以在可预见的未来实现的实现.

后续帖子确认thread_localXcode 6.3中仍然不支持.

  • 他们不想破坏特定功能*的ABI兼容性*,因此他们根本不提供该功能?这是精神上的. (40认同)
  • 这真的很不幸.这意味着你*仍然*不能在便携式C++ 11中使用thread_local,即使现在VC14也支持它.如果苹果的任何工程师都在阅读这篇文章,我真的希望你能尽快修复. (8认同)
  • Apple最终实现`thread_local'的方式证明了为什么上面所有批评者都错了.他们等到他们有一个很好的实施,他们可以保证永远稳定.这种理念正是为什么libSystem(Apple的"CRT")**从未破坏二进制兼容性,而微软必须在每次编译器更改时发布新的`msvcrXYZ.dll` /`vcruntimeXYZ.dll`. (4认同)
  • 我在2016年6月29日确认@ rsfinn的注释.Xcode 8**确实支持`thread_local`,测试于2016年9月20日.我随便对@alexchandel关于他们实现它的方式的意义感兴趣,但是太多了我的盘子充分研究它. (2认同)