digitalmars.D.learn - Bug or feature? std.c.stdlib.exit() breaks RAII
- Ashish Myles (20/20) Dec 29 2011 std.c.stdlib.exit() seems to break RAII. The code below tests this
- Jakob Ovrum (8/31) Dec 29 2011 The C runtime is beyond D's immediate control. You would have to
- Jakob Ovrum (2/4) Dec 29 2011 I mean destructors*.
- Ashish Myles (6/37) Dec 29 2011 t.
- Andrej Mitrovic (3/3) Dec 29 2011 Probably the easiest thing to do is to throw a custom exception and
- Ashish Myles (9/12) Dec 29 2011 Thanks, Andrej. That option had occurred to me, but I figured that
- AaronP (3/15) Dec 29 2011 Yeah, really. I'd been using the C exit() as well. Seems like a pretty
- Jacob Carlborg (5/36) Dec 29 2011 Could druntime hook up on the atexit function to run destructors and
- Stewart Gordon (8/10) Jan 06 2012 I'm not sure. Maybe it could be called upon to run static destructors a...
- Andrej Mitrovic (5/5) Jan 06 2012 Just implement your own exception type, e.g. ExitException, and then use...
- Stewart Gordon (7/10) Jan 06 2012 That's more or less what people have already said. What's more, Ashish ...
- Andrej Mitrovic (1/1) Jan 06 2012 That should have been int main.
- Jonathan M Davis (19/33) Dec 29 2011 A D exit function would have to do essentially the same thing as throw a...
- Ashish Myles (43/61) Dec 29 2011 Hm...embarassingly, it didn't occur to me that C++ didn't clean up
- Jonathan M Davis (39/44) Dec 30 2011 And how would it do that? The only way in the language to properly unwin...
- Ashish Myles (36/65) Dec 30 2011 the
- Jonathan M Davis (73/95) Dec 30 2011 It's more complicate than that. The base class of all throwable objects ...
- Ashish Myles (48/143) Dec 30 2011 Thanks, Jonathan, for your detailed answer.
std.c.stdlib.exit() seems to break RAII. The code below tests this both using a struct destructor and an explicit scope(exit) {}. Is this an intentional feature or a bug? import std.stdio; import std.c.stdlib; void main() { struct SafeExit { ~this() { writeln("Safely exit with destructor."); } } SafeExit safeExit; scope(exit) { writeln("Safely exit with scope(exit)."); } scope(failure) { writeln("Safely exit with scope(failure)."); } writeln("Test if std.c.stdlib.exit() breaks RAII."); writeln("Pre-exit!"); std.c.stdlib.exit(0); writeln("Post-exit! Should not get here!"); }
Dec 29 2011
On Thursday, 29 December 2011 at 16:27:33 UTC, Ashish Myles wrote:std.c.stdlib.exit() seems to break RAII. The code below tests this both using a struct destructor and an explicit scope(exit) {}. Is this an intentional feature or a bug? import std.stdio; import std.c.stdlib; void main() { struct SafeExit { ~this() { writeln("Safely exit with destructor."); } } SafeExit safeExit; scope(exit) { writeln("Safely exit with scope(exit)."); } scope(failure) { writeln("Safely exit with scope(failure)."); } writeln("Test if std.c.stdlib.exit() breaks RAII."); writeln("Pre-exit!"); std.c.stdlib.exit(0); writeln("Post-exit! Should not get here!"); }The C runtime is beyond D's immediate control. You would have to replace C's exit function with a custom one or make the compiler recognize calls to it. Calling 'exit' doesn't properly shut down the D runtime either, it's not just constructors. It's neither a bug or a feature. The bug is arguably in your program.
Dec 29 2011
On Thursday, 29 December 2011 at 17:22:33 UTC, Jakob Ovrum wrote:Calling 'exit' doesn't properly shut down the D runtime either, it's not just constructors.I mean destructors*.
Dec 29 2011
On Thu, Dec 29, 2011 at 12:22 PM, Jakob Ovrum <jakobovrum gmail.com> wrote:On Thursday, 29 December 2011 at 16:27:33 UTC, Ashish Myles wrote:C'sstd.c.stdlib.exit() seems to break RAII. The code below tests this both using a struct destructor and an explicit scope(exit) {}. =A0Is this an intentional feature or a bug? import std.stdio; import std.c.stdlib; void main() { =A0struct SafeExit { =A0 =A0 =A0~this() { =A0 =A0 =A0 =A0 =A0writeln("Safely exit with destructor."); =A0 =A0 =A0} =A0} =A0SafeExit safeExit; =A0scope(exit) { writeln("Safely exit with scope(exit)."); } =A0scope(failure) { writeln("Safely exit with scope(failure)."); } =A0writeln("Test if std.c.stdlib.exit() breaks RAII."); =A0writeln("Pre-exit!"); =A0std.c.stdlib.exit(0); =A0writeln("Post-exit! Should not get here!"); }The C runtime is beyond D's immediate control. You would have to replace =exit function with a custom one or make the compiler recognize calls to i=t.Calling 'exit' doesn't properly shut down the D runtime either, it's not just constructors. It's neither a bug or a feature. The bug is arguably in your program.In that case, what is the recommended way to exit a program from a deeply nested level and also possibly specify a return value? Ashish
Dec 29 2011
Probably the easiest thing to do is to throw a custom exception and catch it somewhere in main() to return your status code. Unlike exit(), throwing will take care of RAII stuff.
Dec 29 2011
On Thu, Dec 29, 2011 at 1:26 PM, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:Probably the easiest thing to do is to throw a custom exception and catch it somewhere in main() to return your status code. Unlike exit(), throwing will take care of RAII stuff.Thanks, Andrej. That option had occurred to me, but I figured that shouldn't be the way to do things given that most other languages have a native exit function. Given that this code transformation isn't particularly difficult (put main in a try/catch, have a custom exception storing exit code, return the exit code in the catch block), would it be reasonable to do a feature request for a D-language-supported exit()?
Dec 29 2011
On 12/29/2011 12:43 PM, Ashish Myles wrote:On Thu, Dec 29, 2011 at 1:26 PM, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:Yeah, really. I'd been using the C exit() as well. Seems like a pretty fundamental feature. :OProbably the easiest thing to do is to throw a custom exception and catch it somewhere in main() to return your status code. Unlike exit(), throwing will take care of RAII stuff.Thanks, Andrej. That option had occurred to me, but I figured that shouldn't be the way to do things given that most other languages have a native exit function. Given that this code transformation isn't particularly difficult (put main in a try/catch, have a custom exception storing exit code, return the exit code in the catch block), would it be reasonable to do a feature request for a D-language-supported exit()?
Dec 29 2011
On 2011-12-29 18:22, Jakob Ovrum wrote:On Thursday, 29 December 2011 at 16:27:33 UTC, Ashish Myles wrote:Could druntime hook up on the atexit function to run destructors and similar when the program exits? -- /Jacob Carlborgstd.c.stdlib.exit() seems to break RAII. The code below tests this both using a struct destructor and an explicit scope(exit) {}. Is this an intentional feature or a bug? import std.stdio; import std.c.stdlib; void main() { struct SafeExit { ~this() { writeln("Safely exit with destructor."); } } SafeExit safeExit; scope(exit) { writeln("Safely exit with scope(exit)."); } scope(failure) { writeln("Safely exit with scope(failure)."); } writeln("Test if std.c.stdlib.exit() breaks RAII."); writeln("Pre-exit!"); std.c.stdlib.exit(0); writeln("Post-exit! Should not get here!"); }The C runtime is beyond D's immediate control. You would have to replace C's exit function with a custom one or make the compiler recognize calls to it. Calling 'exit' doesn't properly shut down the D runtime either, it's not just constructors. It's neither a bug or a feature. The bug is arguably in your program.
Dec 29 2011
On 29/12/2011 19:09, Jacob Carlborg wrote: <snip excessive quote>Could druntime hook up on the atexit function to run destructors and similar when the program exits?I'm not sure. Maybe it could be called upon to run static destructors and destruct heap-allocated objects. But in order to call scope guards and RAII, it would need to unwind the call stack, which could get complicated if you're trying to do it from within a function. It's much simpler not to use exit() and throw a custom exception instead. Stewart.
Jan 06 2012
Just implement your own exception type, e.g. ExitException, and then use: void main() { try { realMain(); } catch (ExitException e) { return 0; } void exit() { throw ExitException(); } where realMain is the actual main function. Then just call exit() whenever you want to.
Jan 06 2012
On 06/01/2012 14:44, Andrej Mitrovic wrote:Just implement your own exception type, e.g. ExitException, and then use:That's more or less what people have already said. What's more, Ashish has already suggested a further improvement whereby the custom exception carries an exit code.void main() { try { realMain(); } catch (ExitException e) { return 0; }<snip>That should have been int main.That's not the only typo - you've forgotten to finish that function with a return for if ExitException is never thrown and a closing }. Stewart.
Jan 06 2012
On Thursday, December 29, 2011 13:43:36 Ashish Myles wrote:On Thu, Dec 29, 2011 at 1:26 PM, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:A D exit function would have to do essentially the same thing as throw an exception and catch it in main anyway. The only way that the stack is going to be unwound properly is if you actually unwind it. The only way in the language to do that without actually returning from each and every function on the stack is to throw an exception. How many modern languages do you know of with an exit function that cleans everything up properly? C++ won't for the same reasons that D won't. Java gets away with it because it doesn't have destructors or scope statements or anything else that would actually require unwinding the stack. All a D exit function _could_ do would be to throw an exception and then have the runtime catch it - which still wouldn't work if the programmer was foolish enough to do something like catch(Exception) {} in their code. So, throwing an exception and catching it _is_ the way to do it, and it really makes more sense if you're doing it yourself, since then you're less likely to make that mistake and catch all Exceptions somewhere in your code and eat the one which is supposed to exit the program. - Jonathan M DavisProbably the easiest thing to do is to throw a custom exception and catch it somewhere in main() to return your status code. Unlike exit(), throwing will take care of RAII stuff.Thanks, Andrej. That option had occurred to me, but I figured that shouldn't be the way to do things given that most other languages have a native exit function. Given that this code transformation isn't particularly difficult (put main in a try/catch, have a custom exception storing exit code, return the exit code in the catch block), would it be reasonable to do a feature request for a D-language-supported exit()?
Dec 29 2011
On Thu, Dec 29, 2011 at 7:16 PM, Jonathan M Davis <jmdavisProg gmx.com> wrote:A D exit function would have to do essentially the same thing as throw an exception and catch it in main anyway. The only way that the stack is going to be unwound properly is if you actually unwind it. The only way in the language to do that without actually returning from each and every function on the stack is to throw an exception. How many modern languages do you know of with an exit function that cleans everything up properly? C++ won't for the same reasons that D won't. Java gets away with it because it doesn't have destructors or scope statements or anything else that would actually require unwinding the stack. All a D exit function _could_ do would be to throw an exception and then have the runtime catch it - which still wouldn't work if the programmer was foolish enough to do something like catch(Exception) {} in their code. So, throwing an exception and catching it _is_ the way to do it, and it really makes more sense if you're doing it yourself, since then you're less likely to make that mistake and catch all Exceptions somewhere in your code and eat the one which is supposed to exit the program. - Jonathan M DavisHm...embarassingly, it didn't occur to me that C++ didn't clean up either; but sure enough, the following code shows that exit() breaks C++ RAII. #include <iostream> #include <cstdlib> struct SafeExit { ~SafeExit() { std::cout << "Safely exit with destructor." << std::endl; } }; int main(int argc, char** argv) { SafeExit safeExit; std::cout << "Test if std.c.stdlib.exit() breaks RAII." << std::endl; std::cout << "Pre-exit!" << std::endl; exit(0); std::cout << "Post-exit! Should not get here!" << std::endl; return 0; } On the other hand, ruby happily *does* unwind the stack properly on exit(). def safe_exit begin yield ensure puts "Safely exit with ensure." end end safe_exit do puts "Test if std.c.stdlib.exit() breaks RAII." puts "Pre-exit!" exit(0); puts "Post-exit! Should not get here!" end Honestly, I would rather have the latter robustness. While I have always thought of abort() as being a dirty exit, I had, until now, always thought of exit() as being very safe. Violating RAII on a safely-intended exit() is a really Bad Thing, I would think. Since D could conceivably implement a very safe exit() without an explicit use of Exceptions to get around the "catch Exception() {}" problem you mentioned above, does it make sense to request a safer exit() feature for D? Ashish
Dec 29 2011
On Thursday, December 29, 2011 23:03:23 Ashish Myles wrote:Since D could conceivably implement a very safe exit() without an explicit use of Exceptions to get around the "catch Exception() {}" problem you mentioned above, does it make sense to request a safer exit() feature for D?And how would it do that? The only way in the language to properly unwind the stack without returning from each and every function is to throw an Exception. If you wanted to do an exit function, it would somehow have to do the exact same thing that happens when you throw an Exception except that it's not an Exception and isn't caught by catch(Exception) {}. That may not be impossible, but I expect that it would complicate things quite a bit. And scope statements are designed around exceptions such that if you didn't throw an Exception, they wouldn't work properly. The same goes for finally blocks. Also, what is the correct thing to do in a situation like this try { //code } catch(Exception e) { //do stuff } The code in the catch block assumes that it's always going to be run when the code in the try block is not properly completed. If an exit call were made from within the try block (be it directly in it or in a function that was called inside it), how would the catch block be handled? Without an Exception, it would be skipped, what's in that catch block wouldn't be run, and there would be no proper cleanup. The very concept of exit violates how the language functions with regards to stack unwinding. Stack unwinding is built around how exceptions function. exit, on the other hand, tries to avoid the whole exception thing and just kill your program. But ultimately, you _can't_ ignore the fact that in order to ensure proper stack unwinding, you either need to return from each function on the stack, or throw an Exception from them. Anything else is going to fail to unwind the stack properly. And honestly, I would generally consider it bad practice to use an exit function. It violates the proper flow of the program - as the issues with stack unwinding illustrate. If you want to do the equivalent of an exit function and have proper cleanup occur, you really need to be throw an Exception designated for that and have your code let it pass all the way through to main so that it can exit properly after having unwound the stack. - Jonathan M Davis
Dec 30 2011
On Fri, Dec 30, 2011 at 5:43 AM, Jonathan M Davis <jmdavisProg gmx.com> wro= te:On Thursday, December 29, 2011 23:03:23 Ashish Myles wrote:theSince D could conceivably implement a very safe exit() without an explicit use of Exceptions to get around the "catch Exception() {}" problem you mentioned above, does it make sense to request a safer exit() feature for D?And how would it do that? The only way in the language to properly unwind=stack without returning from each and every function is to throw an Excep=tion.If you wanted to do an exit function, it would somehow have to do the exa=ctsame thing that happens when you throw an Exception except that it's not =anException and isn't caught by catch(Exception) {}. That may not be imposs=ible,but I expect that it would complicate things quite a bit. And scope state=mentsare designed around exceptions such that if you didn't throw an Exception=,they wouldn't work properly. The same goes for finally blocks. Also, what=isthe correct thing to do in a situation like thisOk, 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.try { =A0 =A0//code } catch(Exception e) { =A0 =A0//do stuff } The code in the catch =A0block assumes that it's always going to be run w=hen thecode in the try block is not properly completed. If an exit call were mad=efrom within the try block (be it directly in it or in a function that was called inside it), how would the catch block be handled? Without an Excep=tion,it would be skipped, what's in that catch block wouldn't be run, and ther=ewould be no proper cleanup.For cleanup that needs to be done no matter what the exception, I would just use a finally{} block. 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?
Dec 30 2011
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 } 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
Dec 30 2011
Thanks, Jonathan, for your detailed answer. Ashish On Fri, Dec 30, 2011 at 1:41 PM, Jonathan M Davis <jmdavisProg gmx.com> wro= te:On Friday, December 30, 2011 10:45:43 Ashish Myles wrote:sOk, 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. =A0__ExitException could then derive from __SystemException and store the exit value. =A0But 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 i=Throwable. Error and Exception are derived from Throwable. Destructors, f=inallyblocks, and scope statements are all skipped when a Throwable is thrown u=nlessit is derived from Exception. So, there is no proper cleanup unless an Exception is thrown. Right now, the compiler, the runtime, and programmer=s canall assume that try { =A0 =A0//code =A0 =A0//1 } catch(Exception e) { =A0 =A0//2 }nfact nothrow relies on this. If you wrap a function call in a try-catch b=lockwhich catches Exception, then the function it's called in can be nothrow =evenif the function being called throws an exception. If we tried to have ano=therexception type which was for exiting, then you'd =A0get this weird situat=ionwhere 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 fun=ctionand have proper cleanup occur is to either return from it or have an Exce=ptionthrown from it. You're trying to have another way added. It's not that it='snecessarily impossible, but it would likely require the redesign of sever=alfeatures and would break the assumptions made by a lot of code.ock isFor 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 bl=exited normally, but it isn't run when a non-Exception Throwable (general=ly anError) is thrown, so it gurantees nothing on unsafe shutdown. And if you =wereusing exit, what would be the proper behavior? Neither the remainder of t=hetry block nor the catch block would be run (since exit would skip the res=t ofthe try block and skip the catch block entirely), which would likely brea=k theassumptions made by a lot of code. It would certainly break scope. All of=asudden, you have something other than Error which won't hit scope(success=) orscope(failure) but _will_ hit scope(exit), and that something is trying t=o beexiting _cleanly_ - unlike Error.ellUTILITY: 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. =A0Is 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 t=them to terminate themselves or to kill them. There is no way that I'm aw=areof built into threads to tell them that it's time to shutdown and then le=tthem do it cleanly (which is what you'd need for a clean shutdown). You c=oulduse std.concurrency to inform them to shutdown or have a shared flag whic=hindicates that it's time for all threads to shutdown, but you couldn't us=epthreads or the Windows equivalent to tell a thread to shutdown cleanly. =So,the only means generally available to terminate a thread is to forcibly k=illit (as C's exit does), making automatic cleanup impossible. _Some_ cleanup can be done when exit is called using atexit and on_exit, =butthe stack won't be unwound properly, so RAII, scope statements, and final=lyblocks 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 e=verythread. 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 aflag or whatever) which isn't going to shutdown on its own (e.g. after fi=nishingsome calculation) that it needs to shutdown, wait for all threads to shut=down,and then terminate the program by exiting main. Whether that's always pos=siblewhen dealing with C libraries, I don't know, but I believe that that's re=allythe 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 shu=tdownmost of the program and then be forced to use exit due to C stuff that yo=u haveno control over, but without at least shutting down the parts that you ca=ncleanly, 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=yoursystem or whatnot), but that depends on what your program is doing and wh=atclean 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=waythat you can do so 100% cleanly. - Jonathan M Davis
Dec 30 2011