Std:: call_once() зависает во втором вызове после вызова вызываемого при первом вызове

Учитывая этот фрагмент кода:

#include <mutex>
#include <iostream>

void f(bool doThrow) {
    if (doThrow) {
        std::cout << "Throwing" << std::endl;
        throw 42;
    }
    std::cout << "Not throwing" << std::endl;
}

std::once_flag flag;

void g(bool doThrow) {
    try {
        std::call_once(flag, f, doThrow);
        std::cout << "Returning" << std::endl;
    } catch (int i) {
        std::cout << "Caught " << i << std::endl;
    }
}

int main() {
    std::once_flag flag;
    g(true);
    g(true);
    g(false);
    g(true);
    g(false);
}

Когда скомпилирован с g++ -std=c++11 -pthread -ggdb, я получаю вывод:

Throwing
Caught 42

после чего процесс зависает:

#0  0x000003fff7277abf in futex_wait (private=0, expected=1, futex_word=0x2aaaacad144 <flag>) at ../sysdeps/unix/sysv/linux/futex-internal.h:61
#1  futex_wait_simple (private=0, expected=1, futex_word=0x2aaaacad144 <flag>) at ../sysdeps/nptl/futex-internal.h:135
#2  __pthread_once_slow (once_control=0x2aaaacad144 <flag>, init_routine=0x3fff7a8d870 <std::__once_proxy()>) at pthread_once.c:105
#3  0x000002aaaaaab06f in __gthread_once (__once=0x2aaaacad144 <flag>, __func=0x3fff7a8d870 <std::__once_proxy()>) at /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/include/g++-v5/x86_64-pc-linux-gnu/bits/gthr-default.h:699
#4  0x000002aaaaaab6c8 in std::call_once<void (&)(bool), bool&> (__once=..., [email protected]: {void (bool)} 0x2aaaaaab08c <f(bool)>) at /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/include/g++-v5/mutex:738
#5  0x000002aaaaaab192 in g (doThrow=true) at test.cpp:17
#6  0x000002aaaaaab287 in main () at test.cpp:27

Но когда скомпилирован с clang++ -std=c++11 -pthread -ggdb, я получаю:

Throwing
Caught 42
Throwing
Caught 42
Not throwing
Returning
Returning
Returning

Насколько я знаю, это похоже на правильное поведение.

Является ли это ошибкой GCC, только меня путают по семантике std::call_once или неправильный код?

Ответ 1

Это выглядит как ошибка в библиотеке GNU С++.


Конечно, это ошибка, поскольку даже пример std::call_once из cppreference будет зависать, если вы попытаетесь использовать предоставленный онлайн-компилятор (Coliru):)

Ошибка происходит в g ​​++ linux-реализации, которая использует pthreads. Меня озадачило то, что этот пример Wandbox работает отлично. Я проверил версии libstdc++:

  • Wandbox: GLIBCXX: 20130411
  • Coliru: GLIBCXX: 20161221

Поэтому я считаю, что это ошибка libstdc++, возможно этот, а точнее этот.