4 ms·
Great write-up! I was just spelunking in this part of the language recently. Worth noting that global constructors and destructors are generally sucky for a bu
by pizlonator 3y ago
Great write-up! I was just spelunking in this part of the language recently.
Worth noting that global constructors and destructors are generally sucky for a bunch of reasons. Don’t use them unless you really have to. Avoid them in libraries.
Reason to avoid global constructors: there’s no laziness to them; they definitely run on program start even if the global is never used. For small projects, no big deal. For large projects it means a large startup time penalty. Successful projects get big eventually, so if you’re playing the long game you want to assume you’re a big project. If you’re a library writer, it’s probable that you’ll be linked into something big. If you don’t have global ctors then your clients will thank you.
Atexit anything is just pointless, IMO. The best way for a process to exit is to just exit. Trying to “destruct” your state at exit risks race conditions and weird bugs but buys you very little. Even things like flushing or syncing a file probably don’t belong in atexit since you should assume your program will run a long time so you want important stuff flushed and synced way before the exit happens. Also, global dtors are implemented by having a global ctor that does __cxa_atexit, so see previous paragraph.
And as with all programming advice, your mileage may vary and some advice is best ignored. If global ctors/dtors achieve what you want and/or startup time isn’t an issue, then do what you feel, bro
- KerrAvon 3y agoYes. If you have an atexit or global destructor on a program expected to run on anything Unixlike or Windows >= NT, you’re just making things slower for the user. Unless you’re on AmigaOS, let the kernel do its job; it can release your resources much faster than you can.
- dataflow 3y ago> If you have an atexit or global destructor on a program expected to run on anything Unixlike or Windows >= NT, you’re just making things slower for the user. Unless you’re on AmigaOS, let the kernel do its job; it can release your resources much faster than you can. This is a pretty myopic view of things? There are lots of other things you might wish to do when the program terminates besides releasing resources...
- pizlonator 3y agoLike what?
- dataflow 3y agoLike https://stackoverflow.com/a/9334778 https://stackoverflow.com/a/9334778
- masklinn 3y agoOr signaling external systems, or removing lockfiles, … Anything the kernel does not do on program exit, which is most things.
- gsliepen 3y agoAt a previous job I was using destructors to ensure motors and other actuators in a telescope would shut down properly, and in the right order. This was very important, because incorrect shutdown could cause damage to equipment and potentially people. This was actually my main reason for using C++ to program the control software: the fact that constructors and destructors run deterministically, especially when something bad happens and an exception is thrown. And while the language doesn't specify in which order they run for global variables in different compilation units, you still know how they are ordered with respect to `main()`, and if you pay attention to how the linker works, you can actually know how they will all be ordered.
- pizlonator 3y agoOk but couldn’t you have done that prior to calling exit?
- dataflow 3y agoWhat problem would that have solved? Also... can't you make that argument for anything? "Avoid unique_ptr -- can't you just free the memory before exiting?"
- nmcveity 3y agoBut sometimes not fast enough. IIRC, you can wait for a HPROCESS in Win32 with the WaitForSingleObject call but that is signaled when the application code finishes _not_ when the OS has finished it's clean up. So if the process you were waiting on was an application that would write, say, "a.txt" and you wanted to wait until that process was done so that you can read "a.txt", it is possible that you fail to open that file because the OS had not released the file resource. Of course, I wouldn't put that clean up in an atexit.
- akoboldfrying 3y ago>Even things like flushing or syncing a file probably don’t belong in atexit since you should assume your program will run a long time so you want important stuff flushed and synced way before the exit happens. I definitely want buffers to be flushed to disk if the user Ctrl-C's the program. The OS only ensures that the resources are released at the OS level, but does nothing to prevent the persistent application state becoming inconsistent at the application level. The really hardcore way to do this is to structure your whole program so that all state is recorded as deltas appended to a WAL, with a background thread/process periodically merging this into "current state" -- in that case, even pulling the power cord can't leave your application in an inconsistent state. But that's a whole lot of work, and just best-effort flushing buffers on exit gets you 80% of the way there.
- gpderetta 3y agoOr you do the easy mode and use sqlite for your file format.
- gpderetta 3y agoNot much to add. Strongly agree. Destructors are to preserve internal program invariant. External invariants need transactional guaratees.