[cfe-dev] [RFC] Suppress C++ static destructor registration

Greg Parker via cfe-dev cfe-dev at lists.llvm.org
Mon Jul 18 15:13:46 PDT 2016


> On Jul 18, 2016, at 2:08 PM, Richard Smith via cfe-dev <cfe-dev at lists.llvm.org> wrote:
> 
> On Mon, Jul 18, 2016 at 1:39 PM, Bruno Cardoso Lopes via cfe-dev <cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>> wrote:
> Hi,
> 
> C++ static destructors can be problematic in multi-threaded
> environment. Some of the issues users often complain about include:
> 1. Teardown ordering: crashes when one thread is exiting the process
> and calling destructors while another thread is still running and
> accessing the destructing variables
> 2. Shared code that is compiled both as an application and as a
> library. When library mode is chosen, goto (1).
> 3. Some projects currently override __cxa_atexit to avoid the behavior
> in question.
> 
> To get around that, I propose we add a compiler option (e.g.
> -fno-cxx-static-destructors) to allow clang to suppress destructor
> registration (currently done via __cxa_atexit, atexit):
> https://reviews.llvm.org/D22474 <https://reviews.llvm.org/D22474>
> 
> I'm opening this discussion here on cfe-dev to get some feedback on the matter
> 
> One can argue that dealing with C++ static destructors in
> multi-threaded environment is solely the responsibility of the
> developer, however since (AFAIK) we don't have any standard guaranteed
> semantic for "global destruction vs. threads", it seems fair to me
> that we could give developers some option.
> 
> They already have options. They can use std::quick_exit, which was added specifically to address this problem, if they don't want destructors to be run at all.

std::quick_exit() does not help. The destructor is in a library. The library author has no control over how other code in the process calls exit(). The authors of the app and other libraries are unaware that exit() is dangerous.


> There are standard techniques to avoid destructors being run for specific objects:
> 
>   template<typename T> union not_destroyed {
>     T value;
>     template<typename ...U> constexpr not_destroyed(U &&...u) : value(std::forward<U>(u)...) {}
>     ~not_destroyed() {} 
>   };
>   not_destroyed<std::string> my_str("foo"); // technically has object lifetime issues

This is fragile. It's easy to accidentally define a static variable that does not have this template, thereby breaking exit() again.

-Werror=exit-time-destructors complains about ~not_destroyed(), so it can't help. Adding #pragma diagnostic around every use of not_destroyed would fix that and not be fragile, but it would be awfully ugly.

A post-link test for references to symbol __cxa_atexit might help, but only if there are no intentional static destructors anywhere and only for optimized builds. 


>   std::string &&s = *new std::string("foo");

This didn't compile.

    test.cxx:12:26: error: rvalue reference to type 'basic_string<[3 * ...]>' cannot bind to lvalue of type 'basic_string<[3 * ...]>'


-- 
Greg Parker     gparker at apple.com <mailto:gparker at apple.com>     Runtime Wrangler


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20160718/9e846fd9/attachment.html>


More information about the cfe-dev mailing list