c++ c++11 global-variables destructor thread-local-storage

c++ - ¿Por qué `std:: exit` no activa los destructores como se esperaba?



c++11 global-variables (2)

El problema aquí es que al salir del proceso, el hilo será (en la mayoría de los sistemas operativos multitarea modernos) matado por la fuerza. Esta muerte del hilo ocurre a nivel del sistema operativo, y el sistema operativo no sabe nada acerca de los objetos o destructores.

#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); }

Mi compilador es el -std=c++1z clang 5.0 con -std=c++1z .

La salida es la siguiente:

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

Tenga en cuenta que no hay ~A:3 , lo que significa que el objeto A a3 no se destruyó.

Sin embargo, según cppref :

std::exit causa la terminación normal del programa. Se realizan varios pasos de limpieza:

Los destructores de objetos con duración de almacenamiento local de hilos ... están garantizados para ser llamados.


Los objetos con duración de almacenamiento de subprocesos están garantizados para ser destruidos solo para el subproceso que exit llamadas. Citando C ++ 14 (N4140), [support.start.term] 18.5 / 8 (énfasis mío):

[[noreturn]] void exit(int status)

La función exit() tiene un comportamiento adicional en esta Norma Internacional:

  • Primero, se destruyen los objetos con duración de almacenamiento de hilos y asociados con el hilo actual . A continuación, los objetos con duración de almacenamiento estático se destruyen y se llaman las funciones registradas mediante la llamada atexit. Ver 3.6.3 para el orden de destrucciones y llamadas. (Los objetos automáticos no se destruyen como resultado de llamar a exit() .) Si el control deja una función registrada llamada por exit porque la función no proporciona un controlador para una excepción lanzada, se llamará a std::terminate() (15.5. 1).
  • A continuación, todas las secuencias C abiertas (como mediadas por las firmas de función declaradas en <cstdio> ) con datos no tmpfile() se eliminan, todas las secuencias C abiertas se cierran, y se eliminan todos los archivos creados al llamar a tmpfile() .
  • Finalmente, el control se devuelve al entorno host. Si el estado es cero o EXIT_SUCCESS , se EXIT_SUCCESS una forma definida por la implementación de la terminación exitosa del estado. Si el estado es EXIT_FAILURE , se EXIT_FAILURE una forma definida por la implementación de la terminación EXIT_FAILURE del estado. De lo contrario, el estado devuelto está definido por la implementación.

Por lo tanto, el estándar no garantiza la destrucción de objetos con una duración de almacenamiento de subprocesos asociada con otros subprocesos que no sea el que llama a exit .