`std::call_once` always segfaults on Clang 12 on Windows (when using libstdc++)

10

I'm looking for a workaround, which will probably involve patching libstdc++ headers. Preserving binary compatibility is preferred but not obligatory, since I'm not using any precompiled C++ code except libstdc++.

I want to keep the std::call_once interface, since I'm trying to compile third-party code that uses is, which I don't want to change.


Here's my code:

#include <iostream>
#include <mutex>

int main()
{
    std::once_flag flag;
    std::call_once(flag, []{std::cout << "Once!\n";});
}

Running it causes a segmentation fault.

I cross-compile it to Windows from Ubuntu using Clang 12, using the standard library from MSYS2 GCC 10.2. Then I test the result with Wine (a quick test showed that it crashes on a VM too). But you should be able to reproduce the results by compiling on Windows natively (using the official Clang binaries + MSYS2 GCC, since MSYS2 doesn't have Clang 12 yet).

I compile it like this:

clang++-12 1.cpp --target=x86_64-w64-mingw32 --sysroot=/mingw64 -pthread -femulated-tls

If I add -g, GDB shows following:

Program received signal SIGSEGV, Segmentation fault.
0x00000001e014dc4a in ?? () from Z:\home\holyblackcat\Sandbox\2\libgcc_s_seh-1.dll
(gdb) bt
#0  0x00000001e014dc4a in ?? () from Z:\home\holyblackcat\Sandbox\2\libgcc_s_seh-1.dll
#1  0x00000000004015f3 in std::call_once<main::$_0> (__once=..., __f=...) at /mingw64/include/c++/10.2.0/mutex:721
#2  0x00000000004015b5 in main () at 1.cpp:8
(gdb) f 1
#1  0x00000000004015f3 in std::call_once<main::$_0> (__once=..., __f=...) at /mingw64/include/c++/10.2.0/mutex:721
721           __once_callable = std::__addressof(__callable);
(gdb) list
716           auto __callable = [&] {
717               std::__invoke(std::forward<_Callable>(__f),
718                             std::forward<_Args>(__args)...);
719           };
720     #ifdef _GLIBCXX_HAVE_TLS
721           __once_callable = std::__addressof(__callable);
722           __once_call = []{ (*(decltype(__callable)*)__once_callable)(); };
723     #else
724           unique_lock<mutex> __functor_lock(__get_once_mutex());
725           __once_functor = __callable;

Clang version is:

# clang++-12 --version --target=x86_64-w64-mingw32 --sysroot=/mingw64 -pthread -femulated-tls
Ubuntu clang version 12.0.1-++20210423082613+072c90a863aa-1~exp1~20210423063319.76
Target: x86_64-w64-windows-gnu
Thread model: posix

GCC version (which provides libstdc++) is:

# g++ --version
g++.exe (Rev10, Built by MSYS2 project) 10.2.0

Compiling the code with this GCC (which is native, not cross-compiling), produces a working code.

What's going on here? Are there any workarounds, or do I have to downgrade to Clang 11?


I reported a Clang bug.

This bug looks related.


Here is the current segfaulting implementation of call_once, after preprocessing:

struct once_flag
{
  private:
    typedef __gthread_once_t __native_type;
    __native_type _M_once = 0;

  public:
    constexpr once_flag() noexcept = default;
    once_flag(const once_flag &) = delete;
    once_flag &operator=(const once_flag &) = delete;
    template <typename _Callable, typename... _Args>
    friend void call_once(once_flag &__once, _Callable &&__f, _Args &&...__args);
};

extern __thread void *__once_callable;
extern __thread void (*__once_call)();
extern "C" void __once_proxy(void);

template <typename _Callable, typename... _Args>
void call_once(once_flag &__once, _Callable &&__f, _Args &&...__args)
{
    auto __callable = [&]
    {
        std::__invoke(std::forward<_Callable>(__f), std::forward<_Args>(__args)...);
    };
    __once_callable = std::__addressof(__callable);
    __once_call = []{(*(decltype(__callable) *)__once_callable)();};
    int __e = __gthread_once(&__once._M_once, &__once_proxy);
    if (__e)
        __throw_system_error(__e);
}
Share
Improve this question
12
  • Works in VS2019 v16.9.4 cl.exe (compiler) v19.28.29914. Definitely a bug. – Casey Apr 27 at 21:34
  • Did you try in an AppVeyor Windows CI instance? – Casey Apr 27 at 21:36
  • 2
    @Casey Yep, there's no doubt about it being a bug. I'm hoping for a workaround, since I don't want to roll back to Clang 11. No, I didn't try AppVeyor, but I expect the results to be the same. – HolyBlackCat Apr 27 at 21:47
  • #define call_once call_once_workaround (at the right spot) and implement it yourself? – C.M. Apr 28 at 17:10
  • @C.M. Yes, or I could patch the header itself. But I don't feel I'm knowledgeable enough to implement it. – HolyBlackCat Apr 28 at 17:13

Comments

Popular posts from this blog

Meaning of `{}` for return expression

Get current scroll position of ScrollView in React Native

flutter websocket connection issue