web-dev-qa-db-fra.com

Pourquoi Apple clang n'autorise-t-il pas thread_local sur C++ 11 alors que le système officiel le supporte?

Vous trouverez ci-dessous un programme simple qui teste l’utilisation d’une variable C++ 11 thread_local de type non-POD dans une bibliothèque partagée.

Si j'utilise l'homebrew clang, cela fonctionne bien:

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

Cependant, si j'essaie d'utiliser Apple Clang, un message d'erreur m'indique qu'il n'est pas pris en charge:

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

Quelqu'un peut-il expliquer pourquoi la variante clang d’Apple refuse lamentablement d’honorer thread_local, alors que le compilateur sous-jacent le supporte et que le code généré semble fonctionner?

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)
23
acm

Le compilateur Clang inclus avec Xcode 8 et 9 prend en charge le mot clé C++ 11 thread_local. Cette fonctionnalité a été ajoutée à la version bêta de Xcode 8, comme indiqué dans la vidéo "Quoi de neuf dans LLVM" de la WWDC 2016 , commençant à la marque 5:50 . ( transcription externe )

L'exemple de programme répertorié dans la question compile et s'exécute avec Xcode 8 GM sous OS X 10.11.6, ainsi qu'avec Xcode 9.3 sous macOS 10.13.4 et produit le résultat souhaité dans chaque cas.

En ce qui concerne iOS, j'ai constaté expérimentalement que thread_local est pris en charge pour iOS 9 et versions ultérieures, mais pas pour iOS 8.4 ou les versions antérieures. 


Pour Xcode 7.x et versions antérieures, voici la réponse d'un ingénieur Apple de 2014 sur l'ancien forum des développeurs Apple (qui n'est plus accessible):

Nous ne prenons pas en charge l'implémentation thread_local à partir de l'open-source Clang parce que nous pensons pouvoir fournir une performance supérieure mise en œuvre pour nos plates-formes en utilisant diverses fonctionnalités de la dynamique linker. Une telle implémentation serait ABI-incompatible avec le mise en œuvre dans le Clang open-source, donc nous ne prendrons pas en charge thread_local jusqu'à ce que nous obtenions une implémentation avec laquelle nous pouvons vivre pour le un avenir prévisible.

Une publication ultérieure confirme que thread_local n'est toujours pas pris en charge dans Xcode 6.3.

42
rsfinn

Selon http://clang.llvm.org/cxx_status.html :

le support thread_local nécessite actuellement la bibliothèque d'exécution C++ à partir de g ++ - 4.8 ou plus tard

Je pense que la version homebrew de Clang utilise un runtime C++ différent.

1
sbooth