`std::call_once` always segfaults on Clang 12 on Windows (when using libstdc++)
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);
}
#define call_once call_once_workaround
(at the right spot) and implement it yourself? – C.M. Apr 28 at 17:10