When using SDL/OpenGL, it is common practice to use code like the following rough sketch.
... more initialization ...
... more initialization ...
... mainloop ...
Shutting down SDL is taken care of via atexit.
When the mainloop/more initialization part contains any code that has to do with ARB_shader_objects & co., a crash will occur inside the driver when exit() is called. I have verified this with driver versions 8.16.20 and 8.14.13. This crash can sometimes take the entire X server with it - apparently, some lock is held.
I have uploaded a very simple sample source (SDL required) that exhibits this problem to http://homepages.uni-paderborn.de/prefect/atibug.c
After some analysis, the problem appears to be that, during the call to SDL_SetVideoMode (which creates the OpenGL context etc.), the driver registers its own atexit hook, which is (by the C standard) executed before SDL_Quit. The driver atexit hook works fine, but once SDL_Quit is called by exit (remember, SDL_Quit is also hooked via atexit), some double-free like problem rears its ugly head.
As a workaround, one can call atexit(SDL_Quit) a second time, after SDL_SetVideoMode (SDL itself seems immune to multiple SDL_Quitcalls), but I really believe that this is a bug in the driver itself.
I don’t know if ATI developers frequent this board. I’ve submitted a ticket with AT, but I’ve had a very bad experience with contacting ATI via their site (I never got any response, either negative or positive, except for automatic bull****), so any pointers to how I can actually get through to driver developers would be very much appreciated.
i don’t know if this is SDL practic but normal applications should never use an exit function for cleanup as this is a special function.
in my opinion this is not ati’s fault but a misuse of a c++ facility.
The atexit() function conforms to ANSI X3.159-1989 (``ANSI C89’’).
yes it is a standard function in that sense… but i use a coder rule used around here saying ‘never ever use atexit unless you are desperate’
well, why not? it’s functionality is well defined (it executes when main() exits and before the executable’s prologue) and it’s standard. You may as well say you don’t use for() loops unless you’re desperate…?
atexit adds abstraction over third party code and that is a good thing. It shouldn’t be used for everything, sure; but it is still a useful tool.
IMHO, of course
let’s simply call it like this: avoiding ‘special’ functions like atexit made me getting rid of many finalization problems i had. in my experience, although stated as ‘standard’ atexit has been rather unpredictable in heavy library dependencies. that’s why i never use it and always provide staged cleanup code although it needs more typing. in my opinion it is worth the extra effort to avoid such functions, especially if the added ‘features’ of it you don’t really need.
that’s my experience after all. i know i’m not a conventional c coder
but it’s not really an issue of saving typing, it’s an issue of making sure resources are released.
For example, suppose I come up with some library that has static variables that need construction and destruction so the library can work. (I’m also supposing that you’re using C rather than C++, here.) You can enforce construction in a number of ways, so that’s ok, but the ONLY way to enforce destruction is with atexit.
You could tell the user of your library to call the destruction on your behalf–but that’s obvioiusly defeating the point of having atexit abstract over the destruction of its static variables. Further; what if the user forgets to call the destruction (because they didn’t RTFM) and your programme leaks resources (since not all resources are automagically freed when a programme exits: e.g. shared memory handles)?
I concede that the clean up order is not well defined and that there could be some dependency issues with multiple libraries registering atexit handles. But that is no different from multiple static objects calling their destructors when a programme exits, either: it’s just a different interface.
i said in applications using atexit is not the best thing to do. for libraries using atexit is, like mentioned in c, to be used and more than ok. i assumed that your application is not a library in the first place hence i brought it into play.
But where’s your distinction? On one hand, a library is just a part of an application that’s generally intended to be reused; but on the other hand, it’s just a collection of object files. Similarly, an application is a set of object files–except it must have a main() symbol be loadable.
Since an application may be sufficiently large to use a set of objects archived into a ‘library’, then atexit must also be useful for applications. Since;
- an application may be worked on by different, disparate groups of people across an organisation, and atexit may be a convenient way ot ensure destructor code is called; and
- there may not always be a convenient path from some abnormal termination point to clean up.
hence, atexit is just as useful in applications since, like a library, it is also a set of object files.
btw, i’m just being a d*ckhead/devil’s advocate. Some people don’t like gotos and long jumps and all manner of language quirks like multiple inherentence… and I’d argue with them, too, partly because I’m a nut, and partly just to be argumentative I still believe what I say about atexit, but I respect your views!
The problem with atexit is that you must not do anything in it that depends on a particular shutdown order. SDL_Quit on the other hand does obviously uses some resources of the driver that are no longer available in an atexit handler.
Propably that’s the reason why SDL exposes the SDL_Quit function instead of just using atexit itself…
The point is: The atexit handler is a nice feature if you know what you are doing. But you have to be aware of it’s limitations, the most serious being the availability of “foreign” resources inside an atexit handler.
my distinction is rather simple. libraries only provide functionality that is reusable. applications usually at an ‘end-point’, not designed to be reused.
also in c there are mostly only a few hard-stops that can go after you (signals to be precise). but for those we have appropriate signaling mechanics. hence with enough coding effort such situations can be handled without falling back to atexit.
i personally consider atexit a kind of safety-net that people deploy to catch all for with they did not provide proper catchups elsewhere. i am picky about catch-up code i know and my code is full of additional code to catchup everything i can imagine going wrong. but this is because i hate my code to break in an uncontrolled way. for me atexit is an uncontrolled case as it kicks in after everything else failed to swing the broom.
in the end a matter of taste i would say. but as an informatic student with heavy interest in software engineering ( and OOP ) i prefer clean designed code with appropriate catchup for ‘logical’ errors along the way
This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.