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

Aaron Ballman via cfe-dev cfe-dev at lists.llvm.org
Wed Jul 18 12:33:21 PDT 2018


On Tue, Jul 17, 2018 at 5:06 PM, JF Bastien via cfe-dev
<cfe-dev at lists.llvm.org> wrote:
> Replying to everyone so far. Thanks for the input!
>
> Based on feedback reveiced I think it makes sense to have an attribute, and
> I’m happy to implement it. I also think a flag makes sense based on
> developer feedback, and based on Ben’s reading of the standard for
> freestanding. I don’t think we want to change the freestanding behavior (at
> least not before C++20 cleans it up), but having a flag to control this
> behavior makes sense.
>
> I therefore think we should pursue both.
>
> On attributes: I like this attribute for globals, function statics, and TLS.
> I don’t like it at all for automatic variables, because RAII is fundamental
> to C++. I think we should limit where the attribute can be used.
>
> Attribute name bikeshed: anyone care for one of
>
> [[no_destroy]]
> [[indestructible]]
> [[forever]]
> [[undying]]
> [[RAINI]] (Resource Acquisition Is Not Initialization)
>
> ?

Out of that list, I think [[no_destroy]] resonates with me the most,
but I don't have a strong preference. Other alternatives:

[[destructor_not_run]]
[[not_destroyed]]
[[no_cleanup]]

One question I have about any of these names is: can we check some
very large bodies of code to ensure there are no use macros with the
same spelling as whatever we seriously consider?

> Compiler flag name bikeshed: I’m happy with -fno-c++-static-destructors as

I'd be happy with that flag name.

~Aaron

> Richard suggests.
>
>
> Individual replies:
>
> Richard said:
>
> Conversely, code (I'm also thinking particularly of third-party code) not
> written to deal with no cleanup on termination may fail to flush buffers or
> save changes to its configuration or the like, resulting in data loss. So
> the user needs to do some work at some level -- either to verify it's safe
> to turn this option on, or to change the code to avoid non-trivial
> destruction -- and that work scales with the quantity of pre-existing
> non-trivial destructors. Nonetheless, this flag may still be the most
> pragmatic way to address the problems in question after determining that
> it's safe to use it.
>
>
> Agreed. Luckily some platforms already terminate program abruptly. These
> applications already deal with those issues :-)
>
> On the standardization front, I think there is a path to changing ISO C++
> that has at least some chance of success:
> 1) Add an annotation mechanism to say "this variable intentionally has a
> non-trivial destructor that must be run at shutdown" and another to say
> "this variable should not have its destructor run at shutdown even though
> it's non-trivial"
> 2) Deprecate variables with a non-trivial destructor without one of those
> annotations
> 3) (After some time has passed) switch the default from running destructors
> to not running destructors
>
>
> This path is interesting. I’ll write a paper for the next mailing, or during
> the meeting, based on clang implementation experience and any usage I can
> get between now and then. I’ll make sure to mention your suggestion.
>
> David said:
>
> I would; however, point out that exit() is not the only time that static
> destructors are run.  The C++ standard pretends that shared libraries don’t
> exist (and added thread-local variables with nontrivial destructors in such
> a way that gives implementers two possible bad choices if they have to
> support library unloading), but in the real world they do.  It’s difficult
> to see how such a mode would coexist with a world that supports loading and
> unloading of shared libraries.  Perhaps a sanitiser could check that the
> objects have been destroyed when the library is unloaded?
>
>
> I’d like to keep shared libraries out of scope of this discussion.
>
>
>
> On Jul 16, 2018, at 2:55 PM, JF Bastien via cfe-dev <cfe-dev at lists.llvm.org>
> wrote:
>
> Hi folks,
>
> I’d like to add a flag in clang, -fno-cxx-static-destructors,  which allows
> developers to demand that no static destructors be emitted. Bruno has a
> sample implementation. We’ve discussed this previously but I’d like to
> re-open the discussion and make a different case for it because we’ve
> received more requests for such a feature.
>
>
> Why is this desirable?
>
> In low-memory circumstances it’s often the case that we know that static
> global destructors are never called. It would be useful to avoid emitting
> them entirely to save memory. We can’t necessarily make the types themselves
> trivially destructible (e.g. a std::map, or a type that’s used both as a
> global and as an automatic variable for which the destructor is only
> meaningful when automatic, or coming from 3rd party library such as boost),
> and using a NeverDestroyed<T> class or global pointer only (std::string& foo
> = *new std::string(“derp");) prevents constexpr and is annoying boilerplate
> (and again, 3rd party code breaks that party).
>
> This is also useful for some thread-related use cases: we have empirical
> evidence that threads using globals cause crashes if these globals are being
> destroyed.
>
> Thread-local storage is similarly painful for different reasons. I’m not
> proposing that anything be done yet, but let’s keep it in mind.
>
>
> Developers want this?
>
> Yes, we’ve received numerous requests for this. Developers are worried about
> code footprint, and have numerous crash reports they’d like to get rid of.
> Developers tell us they’d really rather not pay for this feature, because
> they don’t want to use it yet are stuck with it (and C++ is a “don’t pay for
> what you don’t use” language).
> Interesting note: developers are used to having no cleanup on termination on
> platforms where applications can get terminated when e.g. they’re sent to
> the background by user action.
>
>
> Concrete example
>
> Greg Parker provided this example of thread-related issues in the previous
> discussion:
>
> The Objective-C runtime has a global table that stores retain counts. Pretty
> much all Objective-C code in the process uses this table. With global
> destructors in place this table is destroyed during exit(). If any other
> thread is still running Objective-C code then it will crash.
>
> Currently the Objective-C runtime avoids the destructor by initializing this
> table using placement new into an aligned static char buffer.
>
>
> I’m assuming that the embedded usecase is obvious enough to everyone to not
> need an example. Let me know if that’s not the case.
>
>
> What about standardization?
>
> If this works out I'll discuss standardization options through SG14 (and
> then WG21). The type of developer who’s asked for this are typical SG14
> targets (embedding, gaming, etc). This might fit in with “freestanding” or
> other similar SG14 efforts, but we need experience to guide the proposal.
> Maybe EWG will be interested as well? 🤷‍♂️
>
>
> Thanks,
>
> JF
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>
>
>
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>



More information about the cfe-dev mailing list