Подтвердить что ты не робот

Почему Apple clang запрещает С++ 11 thread_local, когда "официальный" clang поддерживает его

Ниже приведена простая программа, которая тестирует с использованием переменной потока С++ 11 thread_local типа non-POD в общей библиотеке.

Если я использую домодельный clang, это отлично работает:

> /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

Однако, если я пытаюсь использовать 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.

Может ли кто-нибудь дать понять, почему вариант Apple clang трусливо отказывается соблюдать thread_local, несмотря на то, что базовый компилятор поддерживает его, и сгенерированный код работает?

lib.h:

#pragma once

int doit(int) __attribute__((__visibility__("default")));

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);
}

main.cpp:

#include "lib.h"

int main(int argc, char* argv[]) {
    return doit(argc);
}

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)
4b9b3361

Ответ 1

Компилятор clang, включенный в Xcode 8 и позже, поддерживает ключевое слово thread_local С++ 11. Эта функциональность была добавлена в бета-версию Xcode 8, как обсуждалось в видео WWDC 2016 "Что нового в LLVM", начиная с отметки 5:50. (внешняя стенограмма)

Образец программы, перечисленной в вопросе, компилируется и запускается с Xcode 8 GM под OS X 10.11.6 и выдает намеченный результат. Впоследствии он был повторно протестирован с Xcode 9.3 под macOS 10.13.4 и с Xcode 10.2.1 под macOS 10.14.4 и продолжает работать как задумано.

Что касается iOS, я экспериментально обнаружил, что thread_local поддерживается для iOS 9 и более поздних thread_local, но не для iOS 8.4 или более ранних.


Для Xcode 7.x и более ранних, вот ответ от 2014 года от инженера Apple на старом форуме разработчиков Apple (больше не доступен):

Мы не поддерживаем реализацию thread_local из Clang с открытым исходным кодом, потому что мы считаем, что можем обеспечить более высокопроизводительную реализацию для наших платформ, используя различные функции в динамическом компоновщике. Такая реализация была бы ABI-несовместимой с реализацией в Clang с открытым исходным кодом, поэтому мы не будем поддерживать thread_local, пока не получим реализацию, с которой мы можем жить в обозримом будущем.

Последующее сообщение подтверждает, что thread_local все еще не поддерживается в Xcode 6.3.

Ответ 2

Согласно http://clang.llvm.org/cxx_status.html:

Поддержка thread_local в настоящее время требует библиотеки времени выполнения С++ из g++ - 4.8 или новее

Я считаю, что в домашней версии clang используется другая среда выполнения С++.