Bug 3462 – Add a clean way to exit a process.

Status
ASSIGNED
Severity
enhancement
Priority
P4
Component
druntime
Product
D
Version
D2
Platform
All
OS
All
Creation time
2009-10-31T21:46:07Z
Last change time
2024-12-07T13:30:44Z
Keywords
bootcamp, patch
Assigned to
Alexandru Razvan Caciulescu
Creator
Leandro Lucarella
Moved to GitHub: dmd#17144 →

Attachments

IDFilenameSummaryContent-TypeSize
485druntime.patchdruntime patch (against svn r185)text/plain1705
486phobos.patchphobos patch (against svn r1316)text/plain987
1442ExitError.patchanother version, slightly less sophisticatedtext/plain1392

Comments

Comment #0 by leandro.lucarella — 2009-10-31T21:46:07Z
Created attachment 485 druntime patch (against svn r185) Maybe I'm missing something, but I can't find any "standard" way to exit a program cleanly. I can't just call C's exit() function because the stack doesn't get unwinded so scope guards and other finally blocks are not executed. This is bad if you want to do some cleanup. In my particular case, I create a lock file so other instances of a program exit immediately if the lock file is present, and I want to remove the lock file as soon as the program finishes. I want to be able to call some exit() function in any part of the program for simplicity though. I hacked a solution by adding all the program inside a try block, catching an special "Exit" exception with a status attribute. If that exception is catched, the program returns the exception's status code. I think this is an useful feature that deserves being in the standard library, and for that we need runtime support (that's why I added it to the druntime component instead of Phobos, even when Phobos should be hacked too. Attached are patches for druntime and phobos with this changes: druntime: * Add a ProcessExit class to core.exception module (inherits from Object since it's not a real exception and we don't want people catching it even when doing a catch (Throwable)). * Catch the new exception in tryExec() nested funtion from dmain2.d, even when rt_trapExceptions is false (again, because is not really an exception), making the status attribute the new program's exit code. phobos: * Add a new void std.process.exit(int status). All it does is "throw new ProcessExit(status);" to hide implementation details. I don't know if std.process is the better module, maybe it should go somewhere else.
Comment #1 by leandro.lucarella — 2009-10-31T22:18:41Z
Created attachment 486 phobos patch (against svn r1316) BTW, you can apply the patches with: path/to/repo/trunk$ patch -p1 < patch in the root directory of the svn repo.
Comment #2 by leandro.lucarella — 2009-10-31T22:26:43Z
If this is accepted, I guess the discussion in this thread should be revised: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=99432 Personally I think: -------------------- try { // code } catch { // code } -------------------- Should be a shortcut to: -------------------- try { // code } catch (Exception) { // code } --------------------
Comment #3 by sean — 2009-10-31T23:15:05Z
This is tricky. If multiple threads are running then the app has to either forcibly terminate the threads or wait for them to complete. Even trickier if the ProcessExit exception isn't thrown from the main thread. One could send a signal to all executing threads, telling them to throw an exception except that it isn't legal to throw an exception from a signal handler. Sadly, I haven't come up with a way to do this that doesn't risk deadlocks or other Bad Things from happening. As far as I know, the easiest thing is still to call cstdlib exit(). If a clean, safe option presents itself I'd gladly add a Runtime.exit() routine.
Comment #4 by leandro.lucarella — 2009-11-01T10:37:39Z
Comment #5 by sean — 2010-07-20T15:03:10Z
Do you think this may have been addressed by the new messaging protocol in Phobos? I know it's not druntime-specific, but the thread ownership hierarchy seems like a potentially decent solution to this problem.
Comment #6 by leandro.lucarella — 2010-07-20T17:33:30Z
I'm not very familiar with the new threading model in D2, I'm mostly a D1 user, so I can't really say. Sorry and thanks to take the time to answer.
Comment #7 by hsteoh — 2014-09-15T23:59:04Z
This issue keeps cropping up from time to time. Is it possible to use the C library's atexit() hook to do druntime cleanups, so that people can just call std.c.stdlib.exit() and have it do the Right Thing(tm)?
Comment #8 by sean — 2014-09-16T22:58:28Z
That would work fine for a single-threaded program. But when you call exit(0) in a multithreaded program, all shared data is left in an undefined state. Mutexes might still be locked, etc. So it isn't safe to do basically any cleanup at that point without the risk of deadlock. C gets around this problem because multithreading is outside the scope of the language definition, but we don't have that luxury. If you simply want the program to halt without cleanup though, that's much easier.
Comment #9 by hsteoh — 2014-09-16T23:24:50Z
So basically we're screwed unless we invent a language-wide way of triggering threads to terminate asynchronously?
Comment #10 by sean — 2014-09-17T16:42:57Z
If we assume that people are multithreading using std.concurrency we could send "terminate" messages to all running threads, similar to the OwnerTerminated message today. Beyond that... we could maybe send a signal to all running threads on supporting platforms and have those signal handlers throw. Throwing from a signal handler doesn't always work (it's not explicitly supported) but it does on some OSes. The only other thing I can think of would be to build in some flag that threads voluntarily check periodically. But it's just as easy to do this on a per-application basis. There's really little value in building such a flag into Druntime.
Comment #11 by hsteoh — 2014-09-17T17:00:22Z
In that case perhaps the solution is just to assume user code uses std.concurrency and implement exit() according to how you describe it. If they don't use std.concurrency, they probably need to implement their own method of thread control anyway, and so they wouldn't (shouldn't!) be relying on Phobos to properly shutdown all threads. Of course, this must be documented in big bold letters (figuratively speaking) in the documention of the prospective exit() function. I don't like the idea of throwing from a signal handler -- it's in OS-specific territory and will probably be very fragile and non-uniform across platforms. I remember deadalnix's trick of manipulating signal handler return addresses in order to actually perform the throw outside signal handler context, but this is Posix-specific and perhaps even Linux-specific, and there is no guarantee anything of that sort is even implementable across all platforms we support or will support in the future. Besides, it may not address the problem of shared resources and potential deadlocks at all, so it's not even a complete solution to begin with.
Comment #12 by ketmar — 2014-10-02T04:14:55Z
Created attachment 1442 another version, slightly less sophisticated adds ExitError which inherits Error (i intend it to be "catchable" with catch(Throwable)), respects `rt_trapExceptions` flag and adds nothing to std.process (throw your rocks at the door to exit! ;-).
Comment #13 by ketmar — 2014-10-02T04:15:38Z
*** Issue 13554 has been marked as a duplicate of this issue. ***
Comment #14 by code — 2015-02-03T21:28:29Z
Plain old exit(EXIT_FAILURE) should just work.
Comment #15 by hsteoh — 2015-02-03T21:35:07Z
That skips any struct dtors that might be in scope, which could leak resources.
Comment #16 by issues.dlang — 2015-02-03T22:23:55Z
Honestly, I think that this sort of thing should be left up to whatever program wants to attempt it. It's _not_ possible to do in the general case, because there's no way to tell all running threads to exit cleanly. They could be doing _anything_, including just sitting forever in while(1) {}. The only way for them to stop cleanly is to choose to stop cleanly. For that, we could signal them in some way, and then they could choose to shut down, but that requires that all threads have a common way of being signalled to shut down and that they're paying attention to it. And I don't think that that works very well, even if you assume that all threads are using std.concurrency (which is a big assumption), because unless the threads specifically handles that exception being thrown from receive, then who knows what they're going to do. It depends entirely on what the code looks like. Heck, they could be using std.parallelism rather than std.concurrency without even using core.thread.Thread directly and still not be using std.concurrency's receive or receiveOnly. If a program is written with the idea that each thread can be signalled to shut down, then it becomes possible to tell all threads to shut down cleanly, but the code has to be written with that in mind. I don't see how a standard solution makes any sense for that - especially when you consider the high risk that folks will misunderstand it and misuse it - especially if its usage is as simple as exitCleanly() - and that's assuming that we can even do it in the first place. So, I say that we should just leave it up to programs to do this themselves. There is no silver bullet here. And it's not like std.concurrency needs something baked in to be able to do it in your program either. You can just create your own message that your receive calls expect which indicates to a thread that it should shut itself down. Then when you want to shut down, you signal all threads with that message and let them shut down.
Comment #17 by robert.schadek — 2024-12-07T13:30:44Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/17144 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB