Почему `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
,
Проблема здесь в том, что при выходе из процесса поток (в большинстве современных многозадачных операционных систем) будет принудительно уничтожен. Это уничтожение потока происходит на уровне ОС, и ОС ничего не знает об объектах или деструкторах.