3 ms·
> using atexit from libraries intended to be dlclose’d is fundamentally non-portable Problem is, whether or not a library can be dlclose'd is not usually up to
by zbentley 7y ago
> using atexit from libraries intended to be dlclose’d is fundamentally non-portable
Problem is, whether or not a library can be dlclose'd is not usually up to that library. Some things are explicitly intended as runtime-loadable plugins; others are dependencies of those plugins, and so on. Additionally, the person packaging a library for runtime linking may not be the library author.
The upshot of this is basically an ass-backwards obligation (not an unfamiliar situation when working in this area): atexit is only valid for use if you know exactly how code containing it will be linked, which is impossible for a lot of code. The failure mode is worse than other similar situations (e.g. 'a library I linked clobbered my signal handler' is easier to debug and mitigate).
All of that is a bit beside the point, though. We do know (and test for, and are careful about) the implementation details of atexit, and code around them, with all the benefits and drawbacks that entails. That's what this thread is about. "POSIX technically allows this function to blow up the universe when you call it" is a good thing to be mindful of, though.
- kazinator 7y agoatexit is really just for small programs; it should really be called only from the same source file where main is located. The code smell increases with increasing distance from main. If it's in an adjacent source file which is an inalienable part of that program, it's still okay.
- comex 7y ago> The upshot of this is basically an ass-backwards obligation (not an unfamiliar situation when working in this area): atexit is only valid for use if you know exactly how code containing it will be linked, which is impossible for a lot of code. True. The logical conclusion is that in most cases you just shouldn't use atexit from libraries, which... seems fine to me. In my opinion, a well-behaved library should endeavor to be cleanly unloadable without leaking any memory. (That includes using __attribute__((destructor)) if necessary; it's non-portable but without the blow-up-the-universe potential. Or just link in a C++ source file if you want portability.) That way, you can repeatedly load and unload different versions of the same library into the same process, without accumulating wasted memory over time. At least, that's useful in some cases for runtime-loadable plugins, and as you noted, other libraries may end up being loaded and unloaded as dependencies of those plugins. In fact, I'd argue that this rule of etiquette is least applicable when your library is a runtime-loadable plugin itself, but for a host that is known to never unload plugins. Admittedly, unloading libraries is fairly rare in practice, so I can forgive a library for not bothering with global destructors. But I don't think it's good practice to go out of your way to make a library not unloadable, at least without a good reason. Indeed, I'm curious what exactly the author's use case is. They say they "need to have a dependable form of post-process destruction for my tests"... what tests? Why unload libraries in a test program?