Bug or feature? std.c.stdlib.exit() breaks RAII

Ashish Myles marcianx at gmail.com
Fri Dec 30 14:52:06 PST 2011


Thanks, Jonathan, for your detailed answer.

Ashish


On Fri, Dec 30, 2011 at 1:41 PM, Jonathan M Davis <jmdavisProg at gmx.com> wrote:
> On Friday, December 30, 2011 10:45:43 Ashish Myles wrote:
>> Ok, now there are two issues here:
>> IMPLEMENTATION: Implementation of a safe_exit() without an explicit
>> Exception seems to be easy to do at the language level for a
>> single-threaded program -- you simply have a hidden/system class like,
>> say, __SystemException from which Exception derives that be comes the
>> base class of all throwable objects.  __ExitException could then
>> derive from __SystemException and store the exit value.  But it is not
>> clear how this would work for multithreaded programs, with which I
>> have little experience in the context of C++ exceptions. Presumably,
>> the __ExitException would have to be thrown in all threads and could
>> interrupt functions that would otherwise not throw exceptions -- I
>> can't say I understand all the implications.
>
> It's more complicate than that. The base class of all throwable objects is
> Throwable. Error and Exception are derived from Throwable. Destructors, finally
> blocks, and scope statements are all skipped when a Throwable is thrown unless
> it is derived from Exception. So, there is no proper cleanup unless an
> Exception is thrown. Right now, the compiler, the runtime, and programmers can
> all assume that
>
> try
> {
>    //code
>    //1
> }
> catch(Exception e)
> {
>    //2
> }
>
> either #1 or #2 will be hit in that code if proper cleanup is occuring. In
> fact nothrow relies on this. If you wrap a function call in a try-catch block
> which catches Exception, then the function it's called in can be nothrow even
> if the function being called throws an exception. If we tried to have another
> exception type which was for exiting, then you'd  get this weird situation
> where nothrow functions _can_ throw when the program is being shutdown
> properly, and that could be a big problem.
>
> Functions in D are set up around the idea that the only way to exit a function
> and have proper cleanup occur is to either return from it or have an Exception
> thrown from it. You're trying to have another way added. It's not that it's
> necessarily impossible, but it would likely require the redesign of several
> features and would break the assumptions made by a lot of code.
>
>> For cleanup that needs to be done no matter what the exception, I
>> would just use a finally{} block.
>
> Yes and no. finally gets hit whether an Exception is thrown or the try block is
> exited normally, but it isn't run when a non-Exception Throwable (generally an
> Error) is thrown, so it gurantees nothing on unsafe shutdown. And if you were
> using exit, what would be the proper behavior? Neither the remainder of the
> try block nor the catch block would be run (since exit would skip the rest of
> the try block and skip the catch block entirely), which would likely break the
> assumptions made by a lot of code. It would certainly break scope. All of a
> sudden, you have something other than Error which won't hit scope(success) or
> scope(failure) but _will_ hit scope(exit), and that something is trying to be
> exiting _cleanly_ - unlike Error.
>
>> UTILITY: Now, the actual utility of having a safe exit seems to be in
>> question here. A common use of this is in OpenGL programs (that may be
>> implicitly multithreaded) where the keyboard handler exit()s when I
>> hit 'q' or ESC (which is quite common). Moreover, the underlying GUI
>> framework or other APIs being used may conceivably have multiple
>> threads and abstract this out for the user.  Is this an unreasonable
>> use case for a safe exit? Or would this be too difficult to implement
>> cleanly?
>
> Exceptions only affect a single thread, so they're not going to help you
> terminate a multi-threaded program regardless. And to terminate another
> thread, you need a way to terminate it. The only ways to do that are to tell
> them to terminate themselves or to kill them. There is no way that I'm aware
> of built into threads to tell them that it's time to shutdown and then let
> them do it cleanly (which is what you'd need for a clean shutdown). You could
> use std.concurrency to inform them to shutdown or have a shared flag which
> indicates that it's time for all threads to shutdown, but you couldn't use
> pthreads or the Windows equivalent to tell a thread to shutdown cleanly. So,
> the only means generally available to terminate a thread is to forcibly kill
> it (as C's exit does), making automatic cleanup impossible.
>
> _Some_ cleanup can be done when exit is called using atexit and on_exit, but
> the stack won't be unwound properly, so RAII, scope statements, and finally
> blocks aren't going to be run properly. So, critical, global stuff can be
> potentially cleaned up, but you can't get a fully clean shutdown without
> actually returning or having an Exception thrown from every function in every
> thread.
>
> So, in general, the best way to handle taking down a multi-threaded
> application cleanly is to message each thread (be it via std.concurrency or a
> flag or whatever) which isn't going to shutdown on its own (e.g. after finishing
> some calculation) that it needs to shutdown, wait for all threads to shutdown,
> and then terminate the program by exiting main. Whether that's always possible
> when dealing with C libraries, I don't know, but I believe that that's really
> the only way to actually get a fully clean shutdown of a multi-threaded
> program in either C++ or D. In some cases, you may be able to cleanly shutdown
> most of the program and then be forced to use exit due to C stuff that you have
> no control over, but without at least shutting down the parts that you can
> cleanly, the D stuff in general isn't going to shut down cleanly.
>
> Now, it could be that your D program will be just fine even if it isn't
> shutdown cleanly (e.g. it's not like you're going to get memory lost from your
> system or whatnot), but that depends on what your program is doing and what
> clean up gets skipped when exit is called. In general though, exiting by
> returning from main is by far the best way to exit a program and the only way
> that you can do so 100% cleanly.
>
> - Jonathan M Davis


More information about the Digitalmars-d-learn mailing list