Почему `std::exit` не вызывает деструкторы, как ожидалось?

#include <cstdlib>
#include <thread>
#include <chrono>
#include <iostream>

using namespace std;
using namespace std::literals;

struct A
{
    int n_ = 0;
    A(int n) : n_(n) { cout << "A:" << n_ << endl; }
    ~A() { cout << "~A:" << n_ << endl; }
};

A a1(1);

int main()
{
    std::thread([]()
    {
        static A a2(2);
        thread_local A a3(3);
        std::this_thread::sleep_for(24h);
    }).detach();

    static A a4(4);
    thread_local A a5(5);

    std::this_thread::sleep_for(1s);
    std::exit(0);
}

Мой компилятор clang 5.0 с -std=c++1z,

Вывод следующий:

A:1
A:2
A:4
A:5
A:3
~A:5
~A:2
~A:4
~A:1

Обратите внимание, что нет ~A:3, что означает объект A a3 не был разрушен.

Однако, согласно cppref:

std::exit вызывает нормальное завершение программы. Выполняется несколько этапов очистки:

Деструкторы объектов с длительностью локального хранения потока гарантированно будут вызваны.

2 ответа

Решение

Объекты с длительностью хранения потока гарантированно уничтожаются только для потока, который вызывает exit, Цитирование C++14 (N4140), [support.start.term] 18.5/8 (выделено мной):

[[noreturn]] void exit(int status)

Функция exit() имеет дополнительное поведение в этом международном стандарте:

  • Сначала уничтожаются объекты с длительностью хранения потока, связанные с текущим потоком. Затем объекты со статической продолжительностью хранения уничтожаются и вызываются функции, зарегистрированные с помощью вызова atexit. См. 3.6.3 для порядка разрушений и вызовов. (Автоматические объекты не уничтожаются в результате вызова exit().) Если управление оставляет зарегистрированную функцию, вызванную exit потому что функция не предоставляет обработчик для брошенного исключения, std::terminate() должен быть назван (15.5.1).
  • Затем все открытые потоки C (как опосредованные сигнатурами функций, объявленными в <cstdio>) с неписанными буферизованными данными сбрасываются, все открытые потоки C закрываются, а все файлы создаются путем вызова tmpfile() удалены.
  • Наконец, управление возвращается в среду хоста. Если статус равен нулю или EXIT_SUCCESSвозвращается заданная реализацией форма успешного завершения статуса. Если статус EXIT_FAILUREвозвращается форма реализации неудачного завершения статуса. В противном случае возвращаемый статус определяется реализацией.

Следовательно, стандарт не гарантирует уничтожение объектов с продолжительностью хранения потоков, связанных с другими потоками, кроме вызывающего exit,

Проблема здесь в том, что при выходе из процесса поток (в большинстве современных многозадачных операционных систем) будет принудительно уничтожен. Это уничтожение потока происходит на уровне ОС, и ОС ничего не знает об объектах или деструкторах.

Другие вопросы по тегам