Re: Exception/Error division in D
On 01/06/12 22:35, Walter Bright wrote: On 6/1/2012 11:14 AM, deadalnix wrote: We are talking about runing scope statement and finally when unwiding the stack, not trying to continue the execution of the program. Which will be running arbitrary code not anticipated by the assert failure, and code that is highly unlikely to be desirable for shutdown. Sorry, Walter, that's complete bollocks. try { assert(x == 2); } catch(AssertException e) { foo(); } is exactly equivalent to: version (release) {} else { if (x!=2) foo(); } Bad practice, sure. But it's not running arbitrary, unanticipated code.
Re: Exception/Error division in D
Le 01/06/2012 22:35, Walter Bright a écrit : On 6/1/2012 11:14 AM, deadalnix wrote: We are talking about runing scope statement and finally when unwiding the stack, not trying to continue the execution of the program. Which will be running arbitrary code not anticipated by the assert failure, and code that is highly unlikely to be desirable for shutdown. This is, most of the time, the point of error/exceptions. You rarely recover from them in real life. I believe this is a misunderstanding of what exceptions are for. File not found exceptions, and other errors detected in inputs, are routine and routinely recoverable. This discussion has come up repeatedly in the last 30 years. It's root is always the same - conflating handling of input errors, and handling of bugs in the logic of the program. The two are COMPLETELY different and dealing with them follow completely different philosophies, goals, and strategies. Input errors are not bugs, and vice versa. There is no overlap. I'm pretty sure I understand what you are saying here. We have in fact 3 cases : 1/ A problem during the execution of an operation (Exceptions). 2/ A logical invalid state in the program (Errors). 3/ The program environment is broken (Error too ATM). Case 1/ is out of the current discussion scope. In case 3/, it doesn't even make sense to throw an Error as we do know, because it isn't even sure that this is possible (stack corrupted), or that the information provided are correct. This leave the case 2/ on the table. Programs are usually an aggregate of several smaller component that interacts with each others. Let say, as this is a very common case, I have a program that have a network component and another that perform some calculations. If an assert fails in the component that does calculations, it indicate a malfunction here. Whatever I do in that module, it is likely that it will make no sense. However, unless I consider I may be in case 3/ (but then, it isn't even a good response to throw an Error, so we consider we aren't) I'm sure that the network module is still in good shape and can close the connection.
Re: Exception/Error division in D
Walter Bright wrote: On 5/31/2012 1:05 PM, Jens Mueller wrote: Okay, let's assume I have separate processes maybe even processes on different machines. In one process I get an error. Let's say I want to trigger the other process that it restarts the process or just logs the event whatever makes sense. How do I do this if it not guaranteed that finally/scope blocks are being executed? Presumably the operating system provides a means to tell when a process is no longer running as part of its inter-process communication api. My point is that you may want to access some state of your invalid program. State that is lost otherwise. But maybe just having the core dump is actually enough, i.e. there is no other interesting state. You are probably right that you can always recover from the error when a new process is started. At least I cannot can up with a convincing case. Since the current implementation does not follow the specification regarding scope and finally block being executed in case of Error will try ... catch (...Error) keep working? I have code that uses assertThrows!AssertError to test some in contracts. Will this code break? Jens
Re: Exception/Error division in D
On 6/1/2012 1:15 AM, Jens Mueller wrote: Since the current implementation does not follow the specification regarding scope and finally block being executed in case of Error will try ... catch (...Error) keep working? No. The reason for this is the implementation was not updated after the split between Error and Exception happened. It was overlooked. I have code that uses assertThrows!AssertError to test some in contracts. Will this code break? I don't know exactly what your code is, but if you're relying on scope to unwind in the presence of Errors, that will break.
Re: Exception/Error division in D
Le 31/05/2012 21:47, Walter Bright a écrit : On 5/31/2012 12:40 AM, Jens Mueller wrote: How do I do a graceful shutdown if finally and scope is not guaranteed to be executed? Assuming onAssertError, etc. is of no use because I need to perform different shutdowns due to having different cases or if I defined my own Error, let's say for some device. There's no way to guarantee a graceful shutdown. No way. If you must have such, then the way to do it is to divide your application into separate processes that communicate via interprocess communication, then when one component fails the rest of your app can restart it or do what's necessary, as the rest is not in an invalid state. They're is no way to ensure that an IP packet will go throw the internet. Let just shutdown that silly thing that internet is right now.
Re: Exception/Error division in D
Le 01/06/2012 12:29, Walter Bright a écrit : On 6/1/2012 1:15 AM, Jens Mueller wrote: Since the current implementation does not follow the specification regarding scope and finally block being executed in case of Error will try ... catch (...Error) keep working? No. The reason for this is the implementation was not updated after the split between Error and Exception happened. It was overlooked. I have code that uses assertThrows!AssertError to test some in contracts. Will this code break? I don't know exactly what your code is, but if you're relying on scope to unwind in the presence of Errors, that will break. If you have an error, it is already broken in some way. But this is unreasonable to think that the whole program is broken, except in very specific cases (stack corruption for instance) but in such a case, you can't throw an error anyway.
Re: Exception/Error division in D
On Friday, 1 June 2012 at 12:03:15 UTC, deadalnix wrote: Le 01/06/2012 12:29, Walter Bright a écrit : On 6/1/2012 1:15 AM, Jens Mueller wrote: Since the current implementation does not follow the specification regarding scope and finally block being executed in case of Error will try ... catch (...Error) keep working? No. The reason for this is the implementation was not updated after the split between Error and Exception happened. It was overlooked. I have code that uses assertThrows!AssertError to test some in contracts. Will this code break? I don't know exactly what your code is, but if you're relying on scope to unwind in the presence of Errors, that will break. If you have an error, it is already broken in some way. But this is unreasonable to think that the whole program is broken, except in very specific cases (stack corruption for instance) but in such a case, you can't throw an error anyway. I agree. It should be possible to have an plugin system where not every null pointer dereference in a plugin screws up the hole program. Without using different processes for the plugin. 90% of null pointer dereferences are simple bugs not memory corruption.
Re: Exception/Error division in D
On 6/1/12, Tobias Pankrath tob...@pankrath.net wrote: I agree. It should be possible to have an plugin system where not every null pointer dereference in a plugin screws up the hole program. Without using different processes for the plugin. It should also be possible to correctly release hardware handles regardless of what happened to the app itself. I hate it when apps lock up and can't be killed (e.g. ones using ASIO hardware).
Re: Exception/Error division in D
deadalnix deadal...@gmail.com wrote: Le 31/05/2012 04:10, Walter Bright a écrit : The correct response to a server app crashing is to restart it, not attempt to keep a program in an invalid state running. Should I mention to restart it AFTER A GRACEFUL SHUTDOWN ? No. Abort with crash dump is good way to do controlled shutdown. If there is need for a cleanup, it is better to do it in the upcoming startup where the program state is valid.
Re: Exception/Error division in D
Le 01/06/2012 02:57, Walter Bright a écrit : On 5/31/2012 2:23 AM, Lars T. Kyllingstad wrote: On Thursday, 31 May 2012 at 02:18:22 UTC, Walter Bright wrote: A recoverable exception is NOT a logic bug in your program, which is why it is recoverable. If there is recovery possible from a particular assert error, then you are using asserts incorrectly. I think this is a key point. Asserts are there to verify and debug program logic, they are not part of the logic itself. They are a useful tool for the programmer, nothing more. Specifically, asserts are NOT an error handling mechanism! Right. And I'd like to amplify that the asserts are also there to detect program faults hopefully before damage is done. If a program must continue even after it has failed, then you have a WRONGLY designed system. It is extremely important to understand this point if you are implementing any sort of critical software. We are talking about runing scope statement and finally when unwiding the stack, not trying to continue the execution of the program. This is, most of the time, the point of error/exceptions. You rarely recover from them in real life.
Re: Exception/Error division in D
Le 01/06/2012 14:16, Tobias Pankrath a écrit : On Friday, 1 June 2012 at 12:03:15 UTC, deadalnix wrote: Le 01/06/2012 12:29, Walter Bright a écrit : On 6/1/2012 1:15 AM, Jens Mueller wrote: Since the current implementation does not follow the specification regarding scope and finally block being executed in case of Error will try ... catch (...Error) keep working? No. The reason for this is the implementation was not updated after the split between Error and Exception happened. It was overlooked. I have code that uses assertThrows!AssertError to test some in contracts. Will this code break? I don't know exactly what your code is, but if you're relying on scope to unwind in the presence of Errors, that will break. If you have an error, it is already broken in some way. But this is unreasonable to think that the whole program is broken, except in very specific cases (stack corruption for instance) but in such a case, you can't throw an error anyway. I agree. It should be possible to have an plugin system where not every null pointer dereference in a plugin screws up the hole program. Without using different processes for the plugin. 90% of null pointer dereferences are simple bugs not memory corruption. You want to crash an airplane or what ???
Re: Exception/Error division in D
On Friday, June 01, 2012 03:29:17 Walter Bright wrote: On 6/1/2012 1:15 AM, Jens Mueller wrote: Since the current implementation does not follow the specification regarding scope and finally block being executed in case of Error will try ... catch (...Error) keep working? No. The reason for this is the implementation was not updated after the split between Error and Exception happened. It was overlooked. I have code that uses assertThrows!AssertError to test some in contracts. Will this code break? I don't know exactly what your code is, but if you're relying on scope to unwind in the presence of Errors, that will break. std.exception.assertThrown will catch AssertErrors just fine, though I thought that it had a warning about catching non-Exceptions, and glancing at it now, it doesn't look like it does. I should probably add that. In any case, it's specifically designed so that it can catch AssertErrors, and nothing it itself does should require cleanup, but if the expression evaluated would have required cleanup, and the AssertError skipped it, then whatever program state is affected by that is now invalid. Anyone using assertThrown!AssertError is going to have to worry about that just as much as when you catch an Error yourself. However, it _is_ true that that's safer right now then it would be if scope statements, destructors, and finally blocks were no longer run for Errors, since in many, many cases, failed assertions in unit tests wouldn't really invalidate anything in the program. What's more likely to happen is that files won't get cleaned up and stuff ilke that. But the AssertError in unit tests case is really the only case that I'm aware of where I see a strong argument for having cleanup occur for Errors. - Jonathan M Davis
Re: Exception/Error division in D
On Friday, June 01, 2012 14:16:48 Tobias Pankrath wrote: 90% of null pointer dereferences are simple bugs not memory corruption. True, but it means that you have a logic bug, which means that your program is in an invalid state anyway, and continuing could do who-knows-what. If you want to handle dereferencing null pointers, then simply check the pointer before dereferencing it. And even if 90% of null pointer dereferences are simple bugs and not memory corruption, the program has _no_ way of knowing which it's dealing with, so it must assume the worst case scenario. - Jonathan M Davis
Re: Exception/Error division in D
On Friday, June 01, 2012 20:14:45 deadalnix wrote: We are talking about runing scope statement and finally when unwiding the stack, not trying to continue the execution of the program. Except that that _is_ continuing execution. It's only running cleanup code, but if the program state is invalid, it can't assume that it's any safer to run that code than the code following whatever caused the Error. And if you _really_ need that cleanup code to run, then you need to find other ways to make sure that it happens anyway (either that or you need to find a way to make it so that it doesn't need to happen), because your program could be killed for external reasons outside of your control. - Jonathan M Davis
Re: Exception/Error division in D
On 6/1/2012 11:14 AM, deadalnix wrote: We are talking about runing scope statement and finally when unwiding the stack, not trying to continue the execution of the program. Which will be running arbitrary code not anticipated by the assert failure, and code that is highly unlikely to be desirable for shutdown. This is, most of the time, the point of error/exceptions. You rarely recover from them in real life. I believe this is a misunderstanding of what exceptions are for. File not found exceptions, and other errors detected in inputs, are routine and routinely recoverable. This discussion has come up repeatedly in the last 30 years. It's root is always the same - conflating handling of input errors, and handling of bugs in the logic of the program. The two are COMPLETELY different and dealing with them follow completely different philosophies, goals, and strategies. Input errors are not bugs, and vice versa. There is no overlap.
Re: Exception/Error division in D
On 2012-05-30 23:16, Jonathan M Davis wrote: You can catch them to print out additional information or whatever is useful to generate more information about the Error. In fact, just what the Error gives you is already more useful: message, file, line number, stack trace, etc. That alone makes an Error more useful than a halt instruction. If I recall correctly you has been arguing that Errors shouldn't be catchable, as they are today, and this needed to be fixed. Hmm, or was that Steven. You can catch them to attempt explicit cleanup that absolutely must be done for whatever reason (with the knowledge that it's potentially dangerous to do that cleanup due to the Error). You can catch them in very controlled circumstances where you know that continuing is safe (obviously this isn't the sort of thing that you do in @safe code). For instance, in some restricted cases, that could be done with an OutOfMemoryError. But when you do that sort of thing you have to catch the Error _very_ close to the throw point and be sure that there's no cleanup code in between. It only works when you can guarantee yourself that the program state is not being compromised by the Error, and you're able to guarantee that continuing from the catch point is safe. That works in some cases with AssertError in unit test code but becomes problematic as such code becomes more complex. I'm mostly interested in letting the user know something went wrong and then exit the application. -- /Jacob Carlborg
Re: Exception/Error division in D
On Thursday, May 31, 2012 08:26:18 Jacob Carlborg wrote: On 2012-05-30 23:16, Jonathan M Davis wrote: You can catch them to print out additional information or whatever is useful to generate more information about the Error. In fact, just what the Error gives you is already more useful: message, file, line number, stack trace, etc. That alone makes an Error more useful than a halt instruction. If I recall correctly you has been arguing that Errors shouldn't be catchable, as they are today, and this needed to be fixed. Hmm, or was that Steven. No. I haven't been arguing that. It's not particularly safe to catch Errors, but they're catchable on purpose. It's catching and _handling_ an Error that's generally a bad idea - that and continuing to execute after catching the Error rather than letting the program terminate. It may be appropriate in very rare examples where the programmer knows what they're doing, but in general, catching them to do much beyond print out additional information or maybe do some absolutely critical cleanup is a bad idea. The real question is whether any cleanup should be attempted on Error (i.e. destructors, scope statements, and finally blocks). Running that code is risky when an Error occurs, because the program is in an invalid state. It's possible that running it could actually do damage of some variety, depending on the state of the program and what the cleanup does. But skipping all of that cleanup can be a problem too, since that cleanup generally needs to be done. Depending on what the Error was, the cleanup may actually work and therefore leave the program in a less invalid state. So, it's a question of whether attempting cleanup in an invalid state or skipping cleanup in an invalid state is riskier. Per Walter, there's no guarantee that that cleanup will occur, but with the current implementation it almost always does. You can catch them to attempt explicit cleanup that absolutely must be done for whatever reason (with the knowledge that it's potentially dangerous to do that cleanup due to the Error). You can catch them in very controlled circumstances where you know that continuing is safe (obviously this isn't the sort of thing that you do in @safe code). For instance, in some restricted cases, that could be done with an OutOfMemoryError. But when you do that sort of thing you have to catch the Error _very_ close to the throw point and be sure that there's no cleanup code in between. It only works when you can guarantee yourself that the program state is not being compromised by the Error, and you're able to guarantee that continuing from the catch point is safe. That works in some cases with AssertError in unit test code but becomes problematic as such code becomes more complex. I'm mostly interested in letting the user know something went wrong and then exit the application. That would be the most typical use case for catching an Error and certainly is the least risky of the reasons that you might do it. - Jonathan M Davis
Re: Exception/Error division in D
On 2012-05-31 08:39, Jonathan M Davis wrote: No. I haven't been arguing that. It's not particularly safe to catch Errors, but they're catchable on purpose. It's catching and _handling_ an Error that's generally a bad idea - that and continuing to execute after catching the Error rather than letting the program terminate. It may be appropriate in very rare examples where the programmer knows what they're doing, but in general, catching them to do much beyond print out additional information or maybe do some absolutely critical cleanup is a bad idea. The real question is whether any cleanup should be attempted on Error (i.e. destructors, scope statements, and finally blocks). Running that code is risky when an Error occurs, because the program is in an invalid state. It's possible that running it could actually do damage of some variety, depending on the state of the program and what the cleanup does. But skipping all of that cleanup can be a problem too, since that cleanup generally needs to be done. Depending on what the Error was, the cleanup may actually work and therefore leave the program in a less invalid state. So, it's a question of whether attempting cleanup in an invalid state or skipping cleanup in an invalid state is riskier. Per Walter, there's no guarantee that that cleanup will occur, but with the current implementation it almost always does. Ok, I'm sorry if I misunderstood you or confused you with someone else. -- /Jacob Carlborg
Re: Exception/Error division in D
Walter Bright wrote: On 5/30/2012 2:32 AM, Don Clugston wrote: In fact, generally, the point of an AssertError is to prevent the program from entering an invalid state. It's already in an invalid state when the assert fails, by definition. It is not recoverable. The only option is a more or less graceful shutdown. How do I do a graceful shutdown if finally and scope is not guaranteed to be executed? Assuming onAssertError, etc. is of no use because I need to perform different shutdowns due to having different cases or if I defined my own Error, let's say for some device. Jens
Re: Exception/Error division in D
Le 31/05/2012 04:10, Walter Bright a écrit : On 5/30/2012 5:58 PM, Kapps wrote: All code has bugs in it. It's nice being notified about it and all, but if you release a server application, and it crashes every single time it encounters any bug... well, your customers will not have a fun time. The correct response to a server app crashing is to restart it, not attempt to keep a program in an invalid state running. Should I mention to restart it AFTER A GRACEFUL SHUTDOWN ?
Re: Exception/Error division in D
Le 31/05/2012 03:06, Artur Skawina a écrit : On 05/31/12 00:21, Jonathan M Davis wrote: Now, it's perfectly possible to design code which never checks for null pointers and if a null pointer is dereferenced throws an Exception and attempts to recover from it (assuming that it's possible to detect the dereference and throw at that point, which AFAIK is impossible with segfaults - maybe it could be done on Windows with its Access Violations, but segfaults trigger a signal handler, and you're screwed at that point). But writing code No, it's easily recoverable. That does not mean however that it would be a good idea to map segfaults to exceptions as a language feature. And dereferencing a null pointer is *not* guaranteed to trap, all you need is a large enough offset and you will get silent data corruption. int i = 42; auto j = cast(size_t)i; ubyte* p = null; p[j] = 13; assert(i!=42); // oops artur Most system protect at least the first page (the first 4Kb ). For other it is doable within druntime with page protection. For bigger stuff, they have to be forbidden in @safe code or runtime check should be added.
Re: Exception/Error division in D
Le 31/05/2012 04:17, Walter Bright a écrit : On 5/30/2012 8:05 AM, Steven Schveighoffer wrote: I'd classify errors/exceptions into three categories: 1. corruption/segfault -- not recoverable under any reasonable circumstances. Special cases exist (such as a custom paging mechanism). 2. program invariant errors (i.e. assert errors) -- Recovery is not defined by the runtime, so you must do it manually. Any decision the runtime makes will be arbitrary, and could be wrong. 3. try/catch exceptions -- these are planned for and *expected* to occur because the program cannot control it's environment. e.g. EOF when none was expected. A recoverable exception is NOT a logic bug in your program, which is why it is recoverable. If there is recovery possible from a particular assert error, then you are using asserts incorrectly. Assert errors occur because your program has entered an unanticipated, invalid state. There's no such thing as knowing how to put it back into a valid state, because you don't know where it went wrong and how much is corrupted, etc. A failure in database component should prevent me from close a network connection properly in an unrelated part of the program. This is called failing gracefully. And this highly recommended, and you KNOW that the system will fail at some point.
Re: Exception/Error division in D
Le 31/05/2012 00:21, Jonathan M Davis a écrit : On Thursday, May 31, 2012 00:01:16 deadalnix wrote: Le 30/05/2012 17:29, Don Clugston a écrit : There's a big difference. A segfault is a machine error. The integrity of the machine model has been violated, and the machine is in an out-of-control state. In particular, the stack may be corrupted, so stack unwinding may not be successful. But, in an assert error, the machine is completely intact; the error is at a higher level, which does not interfere with stack unwinding. Damage is possible only if you've written your destructors/finally code extremely poorly. Note that, unlike C++, it's OK to throw a new Error or Exception from inside a destructor. But with (say) a stack overflow, you don't necessarily know what code is being executed. It could do anything. Most segfault are null deference or unintizialized pointer deference. Both are recoverable. If you dereferenced a null pointer, it's a bug in your code. Your code is assuming that the pointer was non-null, which was obviously incorrect, because it was null. That's _not_ recoverable in the general case. Your code was obviously written with the assumption that the pointer was non-null, so your code is wrong, and so continuing to execute it makes no sense, because it's in an invalid state and could do who-knows-what. If there's any possibility of a pointer being null, the correct thing to do is to check it before dereferencing it. If you don't, it's bug. I want to remind you that the subject is knowing if scope and finally block should be triggered by errors. Such blocks are perfectly safe to execute in such a situation. Additionally, I may want to abort the current operation, but not the whole program. This is doable on such an error. Now, it's perfectly possible to design code which never checks for null pointers and if a null pointer is dereferenced throws an Exception and attempts to recover from it (assuming that it's possible to detect the dereference and throw at that point, which AFAIK is impossible with segfaults - maybe it could be done on Windows with its Access Violations, but segfaults trigger a signal handler, and you're screwed at that point). Well, I have a pull request in druntime that do exactly what you claim is impossible.
Re: Exception/Error division in D
On Wed, 30 May 2012 22:36:50 +0100, Jonathan M Davis jmdavisp...@gmx.com wrote: In general, a segfault is definitely worse, but logic errors can_ be just as bad in terms of the damage that they can do (especially in cmparison with segfaults caused by null pointers as opposed to those caused by memory corruption). It all depends on what the logic error is and what happens if you try and continue with the program in such a state. In fact.. a logic error - or rather an assert triggered by a bad argument or similar may be the result of memory corruption which was undetected i.e. buffer overflow on the stack overwriting an integer passed to a function which asserts it is within a range, and it isn't. So, you can't even be sure that logic errors aren't caused by memory corruption. R -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Re: Exception/Error division in D
On Thursday, 31 May 2012 at 02:18:22 UTC, Walter Bright wrote: On 5/30/2012 8:05 AM, Steven Schveighoffer wrote: I'd classify errors/exceptions into three categories: 1. corruption/segfault -- not recoverable under any reasonable circumstances. Special cases exist (such as a custom paging mechanism). 2. program invariant errors (i.e. assert errors) -- Recovery is not defined by the runtime, so you must do it manually. Any decision the runtime makes will be arbitrary, and could be wrong. 3. try/catch exceptions -- these are planned for and *expected* to occur because the program cannot control it's environment. e.g. EOF when none was expected. A recoverable exception is NOT a logic bug in your program, which is why it is recoverable. If there is recovery possible from a particular assert error, then you are using asserts incorrectly. I think this is a key point. Asserts are there to verify and debug program logic, they are not part of the logic itself. They are a useful tool for the programmer, nothing more. Specifically, asserts are NOT an error handling mechanism! If you compile the code with -release (which is often the case for production code), the asserts won't even be included. Therefore, when designing the error handling mechanisms for a program, one should just pretend the asserts aren't there. There is no point in writing code which shuts down gracefully when it is compiled without -release, but trudges on in an invalid state when compiled in release mode. Then you should have been using enforce() instead. -Lars
Re: Exception/Error division in D
Le 31/05/2012 11:23, Lars T. Kyllingstad a écrit : On Thursday, 31 May 2012 at 02:18:22 UTC, Walter Bright wrote: On 5/30/2012 8:05 AM, Steven Schveighoffer wrote: I'd classify errors/exceptions into three categories: 1. corruption/segfault -- not recoverable under any reasonable circumstances. Special cases exist (such as a custom paging mechanism). 2. program invariant errors (i.e. assert errors) -- Recovery is not defined by the runtime, so you must do it manually. Any decision the runtime makes will be arbitrary, and could be wrong. 3. try/catch exceptions -- these are planned for and *expected* to occur because the program cannot control it's environment. e.g. EOF when none was expected. A recoverable exception is NOT a logic bug in your program, which is why it is recoverable. If there is recovery possible from a particular assert error, then you are using asserts incorrectly. I think this is a key point. Asserts are there to verify and debug program logic, they are not part of the logic itself. They are a useful tool for the programmer, nothing more. Specifically, asserts are NOT an error handling mechanism! If you compile the code with -release (which is often the case for production code), the asserts won't even be included. Therefore, when designing the error handling mechanisms for a program, one should just pretend the asserts aren't there. There is no point in writing code which shuts down gracefully when it is compiled without -release, but trudges on in an invalid state when compiled in release mode. Then you should have been using enforce() instead. -Lars Your are lost in the details of that specific case. assert is a very specific issue. What is discussed here is much broader.
Re: Exception/Error division in D
On 5/31/2012 12:40 AM, Jens Mueller wrote: How do I do a graceful shutdown if finally and scope is not guaranteed to be executed? Assuming onAssertError, etc. is of no use because I need to perform different shutdowns due to having different cases or if I defined my own Error, let's say for some device. There's no way to guarantee a graceful shutdown. No way. If you must have such, then the way to do it is to divide your application into separate processes that communicate via interprocess communication, then when one component fails the rest of your app can restart it or do what's necessary, as the rest is not in an invalid state.
Re: Exception/Error division in D
On 5/31/2012 2:06 AM, deadalnix wrote: A failure in database component should prevent me from close a network connection properly in an unrelated part of the program. It *is* related if the process space is shared. The correct way to do what you're proposing is to make each component a separate process. Then, you are guaranteed that the failure of one component is separable, and restartable. This is called failing gracefully. And this highly recommended, and you KNOW that the system will fail at some point. In a shared memory space, you have no guarantees whatsoever that the failure in the component is not a failure in the rest of the program. You cannot tell if it is related or not.
Re: Exception/Error division in D
Walter Bright wrote: On 5/31/2012 12:40 AM, Jens Mueller wrote: How do I do a graceful shutdown if finally and scope is not guaranteed to be executed? Assuming onAssertError, etc. is of no use because I need to perform different shutdowns due to having different cases or if I defined my own Error, let's say for some device. There's no way to guarantee a graceful shutdown. No way. If you must have such, then the way to do it is to divide your application into separate processes that communicate via interprocess communication, then when one component fails the rest of your app can restart it or do what's necessary, as the rest is not in an invalid state. Okay, let's assume I have separate processes maybe even processes on different machines. In one process I get an error. Let's say I want to trigger the other process that it restarts the process or just logs the event whatever makes sense. How do I do this if it not guaranteed that finally/scope blocks are being executed? I don't need a guarantee that the shutdown will work in each and every case. All I need is the possibility to perform a more graceful shutdown. Jens
Re: Exception/Error division in D
On May 31, 2012, at 1:05 PM, Jens Mueller jens.k.muel...@gmx.de wrote: Walter Bright wrote: On 5/31/2012 12:40 AM, Jens Mueller wrote: How do I do a graceful shutdown if finally and scope is not guaranteed to be executed? Assuming onAssertError, etc. is of no use because I need to perform different shutdowns due to having different cases or if I defined my own Error, let's say for some device. There's no way to guarantee a graceful shutdown. No way. If you must have such, then the way to do it is to divide your application into separate processes that communicate via interprocess communication, then when one component fails the rest of your app can restart it or do what's necessary, as the rest is not in an invalid state. Okay, let's assume I have separate processes maybe even processes on different machines. In one process I get an error. Let's say I want to trigger the other process that it restarts the process or just logs the event whatever makes sense. How do I do this if it not guaranteed that finally/scope blocks are being executed? I don't need a guarantee that the shutdown will work in each and every case. All I need is the possibility to perform a more graceful shutdown. You pretty much need a local process monitor. This is needed anyway, since not every failure may throw. Say SIGBUS on Posix, for example.
Re: Exception/Error division in D
On 5/31/2012 1:05 PM, Jens Mueller wrote: Okay, let's assume I have separate processes maybe even processes on different machines. In one process I get an error. Let's say I want to trigger the other process that it restarts the process or just logs the event whatever makes sense. How do I do this if it not guaranteed that finally/scope blocks are being executed? Presumably the operating system provides a means to tell when a process is no longer running as part of its inter-process communication api.
Re: Exception/Error division in D
On 5/31/2012 2:23 AM, Lars T. Kyllingstad wrote: On Thursday, 31 May 2012 at 02:18:22 UTC, Walter Bright wrote: A recoverable exception is NOT a logic bug in your program, which is why it is recoverable. If there is recovery possible from a particular assert error, then you are using asserts incorrectly. I think this is a key point. Asserts are there to verify and debug program logic, they are not part of the logic itself. They are a useful tool for the programmer, nothing more. Specifically, asserts are NOT an error handling mechanism! Right. And I'd like to amplify that the asserts are also there to detect program faults hopefully before damage is done. If a program must continue even after it has failed, then you have a WRONGLY designed system. It is extremely important to understand this point if you are implementing any sort of critical software.
Re: Exception/Error division in D
Le 24/05/2012 12:27, Denis Shelomovskij a écrit : Let's talk about an abstract situation without caring about breaking existing code, current docs, implementation etc. Definitions: * an Exception is something that tigers scope guards and executes catch/finally blocks if thrown; * an Error is something that doesn't do it. As a result _we can't do any clean-up if an Error is thrown_ because scope guards and catch/finally blocks aren't executed and the program is in invalid state because of this. Of course it's theoretically possible to code without scope guards and catch/finally blocks but isn't applicably for a real project. E.g. in some editor if and Error is thrown there is no ability to save opened documents. Main Question: What do you think can be an Error? Can Integer Divide by Zero be an Error? Definitely, not. Can Access Violation be an Error? No, because it's very common to access a field/virtual member function of a null object. Can Out of memory be an Error? No, because e.g. if I read a user file that require me to create a large array ( 100 MiB, e.g.) I don't want to crash, but just tell, that Dear user, the file can't be opened because it requires... So what am I think can be an Error? IMHO, nothing. Because throwing everything can indicate that program in very good/terribly bad state and compiler doesn't know anything about that. And because fatal error is fatal the program should just try to print error and close instead of throwing something. Let's now return to the real D world. Current implementation treats Errors as Exceptions for now. Documentation keeps silence. All listed can't be an error cases are Errors (and it's terrible). So why do we has Exception/Error division in D? Because of nothrow. Personally I don't need nothrow for that high cost of making D unusable for me. Lets realize and solve Exception/Error problem and solve nothrow in the second place. Related links: http://forum.dlang.org/thread/1566418.J7qGkEti3s@lyonel http://d.puremagic.com/issues/show_bug.cgi?id=8135 http://d.puremagic.com/issues/show_bug.cgi?id=8136 http://d.puremagic.com/issues/show_bug.cgi?id=8137 P.S. By the way, the only problem I see in current implementation is a luck of Object finalized assertion (True disposable objects (add Finalized! assertion) NG thread that didn't interest anybody). The fact that error don't trigger scope and everything is nonsensial. Today, we know how to implement exception with NO RUNTIME COST when exception isn't thrown. No reason not to do it, except executable size. As this is a specific constraint, we may want to enable it by a compiler switch, but not by default. I see Error as problem that can occur anywhere in any piece of code. I think D have some flaw in Exception management. See Segfault vs NullPointerException discussions in that very forum. Segfault may be OK for some application, but not for a server app that need to be robust. Error exists because of nothrow. As some exceptions can be thrown ANYWHERE, we need a way to separate what is expected to fail, and that have to be handled to be nothrow, and what can go wrong anywhere (basically, when druntime cannot do its job for some reasons).
Re: Exception/Error division in D
Le 24/05/2012 20:39, Steven Schveighoffer a écrit : I'd argue this is unrecoverable. Access Violation results from corruption, and the damage has already been done. Even a null pointer access can be the cause of previous corruption. There is no recovery from memory corruption. There is no way to plan for it. Now, if you want to handle it specifically for your program, that should be doable, at your own risk. But there's no way throwing an Exception is a valid result. https://github.com/D-Programming-Language/druntime/pull/187 I think this is a mandatory patch due to nullable types by default. Arguably, nullable by default is the problem.
Re: Exception/Error division in D
Le 24/05/2012 21:33, Sean Kelly a écrit : On May 24, 2012, at 11:39 AM, Steven Schveighoffer wrote: On Thu, 24 May 2012 06:27:12 -0400, Denis Shelomovskijverylonglogin@gmail.com wrote: Let's talk about an abstract situation without caring about breaking existing code, current docs, implementation etc. Definitions: * an Exception is something that tigers scope guards and executes catch/finally blocks if thrown; * an Error is something that doesn't do it. I'll give you a different definition: * an Exception is something that can be handled far away from the context of the error, because the system can safely unwind the stack. * an Error must be handled at the point the error occurred, or the program state is by definition invalid. This is a good point. OutOfMemory conditions aside, the only time I'd want to recover from an Error condition was at the point the event occurred, not somewhere up the stack. Often, the point of Exception isn't to recover, but to fail as cleanly as possible. To do so, Error must trigger finally blocks and scope statement. They probably shouldn't be catchable in @safe code because of the possible invalid state of the program. But still, often recovering isn't the point when it come to problem as bad as Errors.
Re: Exception/Error division in D
Le 29/05/2012 18:53, Sean Kelly a écrit : On May 24, 2012, at 11:50 PM, Jacob Carlborg wrote: On 2012-05-24 21:33, Sean Kelly wrote: This is a good point. OutOfMemory conditions aside, the only time I'd want to recover from an Error condition was at the point the event occurred, not somewhere up the stack. You never feel you want to catch at the top level, print a sensible error message and then exit the application? Instead of the application just disappearing for user. Well sure, but I wouldn't consider this recovery. As said, recovery isn't the only point of exceptions. For problems as bad as this, you often want to fail cleanly, eventually print an error message or something. Exception handling discussion are often very focussed on recovery, but the fact is that it is A use case, but not THE use case. It is very common in real case that you cannot recover from some problems, and just want to fail without messing everything up.
Re: Exception/Error division in D
On Wednesday, May 30, 2012 10:26:36 deadalnix wrote: The fact that error don't trigger scope and everything is nonsensial. If an Error is truly unrecoverable (as they're generally supposed to be), then what does it matter? Something fatal occured in your program, so it terminates. Because it's an Error, you can get a stack trace and report something before the program actually terminates, but continuing execution after an Error is considered to be truly _bad_ idea, so in general, why does it matter whether scope statements, finally blocks, or destructors get executed? It's only rarer cases where you're trying to do something like create a unit test framework on top of assert that you would need to catch an Error, and that's questionable enough as it is. In normal program execution, an error is fatal, so cleanup is irrelevant and even potentially dangerous, because your program is already in an invalid state. - Jonathan M Davis
Re: Exception/Error division in D
On 30.05.2012 12:33, deadalnix wrote: Le 24/05/2012 21:33, Sean Kelly a écrit : On May 24, 2012, at 11:39 AM, Steven Schveighoffer wrote: On Thu, 24 May 2012 06:27:12 -0400, Denis Shelomovskijverylonglogin@gmail.com wrote: Let's talk about an abstract situation without caring about breaking existing code, current docs, implementation etc. Definitions: * an Exception is something that tigers scope guards and executes catch/finally blocks if thrown; * an Error is something that doesn't do it. I'll give you a different definition: * an Exception is something that can be handled far away from the context of the error, because the system can safely unwind the stack. * an Error must be handled at the point the error occurred, or the program state is by definition invalid. This is a good point. OutOfMemory conditions aside, the only time I'd want to recover from an Error condition was at the point the event occurred, not somewhere up the stack. Often, the point of Exception isn't to recover, but to fail as cleanly as possible. To do so, Error must trigger finally blocks and scope statement. Yes, finally the voice of wisdom! They probably shouldn't be catchable in @safe code because of the possible invalid state of the program. Interesting point btw. But still, often recovering isn't the point when it come to problem as bad as Errors. -- Dmitry Olshansky
Re: Exception/Error division in D
On 30/05/12 10:40, Jonathan M Davis wrote: On Wednesday, May 30, 2012 10:26:36 deadalnix wrote: The fact that error don't trigger scope and everything is nonsensial. If an Error is truly unrecoverable (as they're generally supposed to be), then what does it matter? Something fatal occured in your program, so it terminates. Because it's an Error, you can get a stack trace and report something before the program actually terminates, but continuing execution after an Error is considered to be truly _bad_ idea, so in general, why does it matter whether scope statements, finally blocks, or destructors get executed? It's only rarer cases where you're trying to do something like create a unit test framework on top of assert that you would need to catch an Error, and that's questionable enough as it is. In normal program execution, an error is fatal, so cleanup is irrelevant and even potentially dangerous, because your program is already in an invalid state. That's true for things like segfaults, but in the case of an AssertError, there's no reason to believe that cleanup would cause any damage. In fact, generally, the point of an AssertError is to prevent the program from entering an invalid state. And it's very valuable to log it properly.
Re: Exception/Error division in D
On Wednesday, May 30, 2012 11:32:00 Don Clugston wrote: On 30/05/12 10:40, Jonathan M Davis wrote: On Wednesday, May 30, 2012 10:26:36 deadalnix wrote: The fact that error don't trigger scope and everything is nonsensial. If an Error is truly unrecoverable (as they're generally supposed to be), then what does it matter? Something fatal occured in your program, so it terminates. Because it's an Error, you can get a stack trace and report something before the program actually terminates, but continuing execution after an Error is considered to be truly _bad_ idea, so in general, why does it matter whether scope statements, finally blocks, or destructors get executed? It's only rarer cases where you're trying to do something like create a unit test framework on top of assert that you would need to catch an Error, and that's questionable enough as it is. In normal program execution, an error is fatal, so cleanup is irrelevant and even potentially dangerous, because your program is already in an invalid state. That's true for things like segfaults, but in the case of an AssertError, there's no reason to believe that cleanup would cause any damage. In fact, generally, the point of an AssertError is to prevent the program from entering an invalid state. An assertion failure really isn't all that different from a segfault. By definition, if an assertion fails, the program is an invalid state, because the whole point of the assertion is to guarantee something about the program's state. Now, if a segfault occurs (particularly if it's caused by something other than a null pointer), the program is likely to be in a _worse_ state, but it's in an invalid state in either case. In neither case does it make any sense to try and recover, and in both cases, there's a definite risk in executing any further code - including cleanup code. Yes, the segfault is probably worse but not necessarily all that much worse. A logic error can be just as insidious to the state of a program as memory corruption, depending on what it is. And it's very valuable to log it properly. Yes, which is why it's better to have an Error thrown rather than a halt instruction be executed. But that doesn't mean that any attempt at cleanup is any more valid. - Jonathan M Davis
Re: Exception/Error division in D
On 2012-05-30 12:59, Jonathan M Davis wrote: Yes, which is why it's better to have an Error thrown rather than a halt instruction be executed. But that doesn't mean that any attempt at cleanup is any more valid. If you're not supposed to be able to catch Errors then what's the difference? -- /Jacob Carlborg
Re: Exception/Error division in D
On 30.05.2012 17:28, Jacob Carlborg wrote: On 2012-05-30 12:59, Jonathan M Davis wrote: Yes, which is why it's better to have an Error thrown rather than a halt instruction be executed. But that doesn't mean that any attempt at cleanup is any more valid. If you're not supposed to be able to catch Errors then what's the difference? Having half-flushed/synced database file is no good. I've had pleasure of restoring such things by hand. Trust me, you DON'T want it. A common technique that can kick start half-flushed binary file is appending certain amount of zeros until it fits. Depending on structure it may need more then that. -- Dmitry Olshansky
Re: Exception/Error division in D
Le 30/05/2012 11:32, Don Clugston a écrit : On 30/05/12 10:40, Jonathan M Davis wrote: On Wednesday, May 30, 2012 10:26:36 deadalnix wrote: The fact that error don't trigger scope and everything is nonsensial. If an Error is truly unrecoverable (as they're generally supposed to be), then what does it matter? Something fatal occured in your program, so it terminates. Because it's an Error, you can get a stack trace and report something before the program actually terminates, but continuing execution after an Error is considered to be truly _bad_ idea, so in general, why does it matter whether scope statements, finally blocks, or destructors get executed? It's only rarer cases where you're trying to do something like create a unit test framework on top of assert that you would need to catch an Error, and that's questionable enough as it is. In normal program execution, an error is fatal, so cleanup is irrelevant and even potentially dangerous, because your program is already in an invalid state. That's true for things like segfaults, but in the case of an AssertError, there's no reason to believe that cleanup would cause any damage. In fact, generally, the point of an AssertError is to prevent the program from entering an invalid state. And it's very valuable to log it properly. For segfault, it has been proven to be useful in other languages.
Re: Exception/Error division in D
Le 30/05/2012 12:59, Jonathan M Davis a écrit : And it's very valuable to log it properly. Yes, which is why it's better to have an Error thrown rather than a halt instruction be executed. But that doesn't mean that any attempt at cleanup is any more valid. Sorry but that is bullshit. What can be the benefit of not trying to clean things up ? Do you really consider that corrupted files, client waiting forever at the other end of a connection or any similar stuff is a good thing ? Because that is what you are advocating. I may sound good on the paper, but in real life, system DOES fail. It isn't a question of if, but a question of when and how often, and what to do about it.
Re: Exception/Error division in D
On May 30, 2012, at 7:21 AM, deadalnix deadal...@gmail.com wrote: Le 30/05/2012 12:59, Jonathan M Davis a écrit : And it's very valuable to log it properly. Yes, which is why it's better to have an Error thrown rather than a halt instruction be executed. But that doesn't mean that any attempt at cleanup is any more valid. Sorry but that is bullshit. What can be the benefit of not trying to clean things up ? Do you really consider that corrupted files, client waiting forever at the other end of a connection or any similar stuff is a good thing ? Because that is what you are advocating. I may sound good on the paper, but in real life, system DOES fail. It isn't a question of if, but a question of when and how often, and what to do about it. I'd certainly at least want to be given the option of cleaning up when an Error is thrown. If not, I have a feeling that in circumstances where I really wanted it I'd do something horrible to make sure it happened in some other way.
Re: Exception/Error division in D
On Wed, 30 May 2012 05:32:00 -0400, Don Clugston d...@nospam.com wrote: On 30/05/12 10:40, Jonathan M Davis wrote: On Wednesday, May 30, 2012 10:26:36 deadalnix wrote: The fact that error don't trigger scope and everything is nonsensial. If an Error is truly unrecoverable (as they're generally supposed to be), then what does it matter? Something fatal occured in your program, so it terminates. Because it's an Error, you can get a stack trace and report something before the program actually terminates, but continuing execution after an Error is considered to be truly _bad_ idea, so in general, why does it matter whether scope statements, finally blocks, or destructors get executed? It's only rarer cases where you're trying to do something like create a unit test framework on top of assert that you would need to catch an Error, and that's questionable enough as it is. In normal program execution, an error is fatal, so cleanup is irrelevant and even potentially dangerous, because your program is already in an invalid state. That's true for things like segfaults, but in the case of an AssertError, there's no reason to believe that cleanup would cause any damage. There's also no reason to assume that orderly cleanup *doesn't* cause any damage. In fact, it's not reasonable to assume *anything*. Which is the point. If you want to recover from an error, you have to do it manually. It should be doable, but the default handling should not need to be defined (i.e. implementations should be free to do whatever they want). But there is no reasonable *default* for handling an error that the runtime can assume. I'd classify errors/exceptions into three categories: 1. corruption/segfault -- not recoverable under any reasonable circumstances. Special cases exist (such as a custom paging mechanism). 2. program invariant errors (i.e. assert errors) -- Recovery is not defined by the runtime, so you must do it manually. Any decision the runtime makes will be arbitrary, and could be wrong. 3. try/catch exceptions -- these are planned for and *expected* to occur because the program cannot control it's environment. e.g. EOF when none was expected. The largest problem with the difference between 2 and 3 is the actual decision of whether an exceptional case is categorized as 2 or 3 can be decoupled from the code that decides between them. For example: double invert(double x) { assertOrEnfoce?(x != 0); // which should it be? return 1.0/x; } case 1: void main() { writeln(invert(0)); // clearly a program error } case 2: int main(string[] args) { writeln(invert(to!double(args[1])); // clearly a catchable error } I don't know of a good way to solve that... -Steve
Re: Exception/Error division in D
On May 30, 2012, at 8:05 AM, Steven Schveighoffer schvei...@yahoo.com wrote: On Wed, 30 May 2012 05:32:00 -0400, Don Clugston d...@nospam.com wrote: On 30/05/12 10:40, Jonathan M Davis wrote: On Wednesday, May 30, 2012 10:26:36 deadalnix wrote: The fact that error don't trigger scope and everything is nonsensial. If an Error is truly unrecoverable (as they're generally supposed to be), then what does it matter? Something fatal occured in your program, so it terminates. Because it's an Error, you can get a stack trace and report something before the program actually terminates, but continuing execution after an Error is considered to be truly _bad_ idea, so in general, why does it matter whether scope statements, finally blocks, or destructors get executed? It's only rarer cases where you're trying to do something like create a unit test framework on top of assert that you would need to catch an Error, and that's questionable enough as it is. In normal program execution, an error is fatal, so cleanup is irrelevant and even potentially dangerous, because your program is already in an invalid state. That's true for things like segfaults, but in the case of an AssertError, there's no reason to believe that cleanup would cause any damage. There's also no reason to assume that orderly cleanup *doesn't* cause any damage. In fact, it's not reasonable to assume *anything*. Which is the point. If you want to recover from an error, you have to do it manually. It should be doable, but the default handling should not need to be defined (i.e. implementations should be free to do whatever they want). But there is no reasonable *default* for handling an error that the runtime can assume. I'd classify errors/exceptions into three categories: 1. corruption/segfault -- not recoverable under any reasonable circumstances. Special cases exist (such as a custom paging mechanism). 2. program invariant errors (i.e. assert errors) -- Recovery is not defined by the runtime, so you must do it manually. Any decision the runtime makes will be arbitrary, and could be wrong. 3. try/catch exceptions -- these are planned for and *expected* to occur because the program cannot control it's environment. e.g. EOF when none was expected. The largest problem with the difference between 2 and 3 is the actual decision of whether an exceptional case is categorized as 2 or 3 can be decoupled from the code that decides between them. For example: double invert(double x) { assertOrEnfoce?(x != 0); // which should it be? return 1.0/x; } case 1: void main() { writeln(invert(0)); // clearly a program error } case 2: int main(string[] args) { writeln(invert(to!double(args[1])); // clearly a catchable error } I don't know of a good way to solve that... Sounds like a good argument for the assert handler in core.runtime.
Re: Exception/Error division in D
On 30/05/12 12:59, Jonathan M Davis wrote: On Wednesday, May 30, 2012 11:32:00 Don Clugston wrote: On 30/05/12 10:40, Jonathan M Davis wrote: On Wednesday, May 30, 2012 10:26:36 deadalnix wrote: The fact that error don't trigger scope and everything is nonsensial. If an Error is truly unrecoverable (as they're generally supposed to be), then what does it matter? Something fatal occured in your program, so it terminates. Because it's an Error, you can get a stack trace and report something before the program actually terminates, but continuing execution after an Error is considered to be truly _bad_ idea, so in general, why does it matter whether scope statements, finally blocks, or destructors get executed? It's only rarer cases where you're trying to do something like create a unit test framework on top of assert that you would need to catch an Error, and that's questionable enough as it is. In normal program execution, an error is fatal, so cleanup is irrelevant and even potentially dangerous, because your program is already in an invalid state. That's true for things like segfaults, but in the case of an AssertError, there's no reason to believe that cleanup would cause any damage. In fact, generally, the point of an AssertError is to prevent the program from entering an invalid state. An assertion failure really isn't all that different from a segfault. By definition, if an assertion fails, the program is an invalid state, because the whole point of the assertion is to guarantee something about the program's state. There's a big difference. A segfault is a machine error. The integrity of the machine model has been violated, and the machine is in an out-of-control state. In particular, the stack may be corrupted, so stack unwinding may not be successful. But, in an assert error, the machine is completely intact; the error is at a higher level, which does not interfere with stack unwinding. Damage is possible only if you've written your destructors/finally code extremely poorly. Note that, unlike C++, it's OK to throw a new Error or Exception from inside a destructor. But with (say) a stack overflow, you don't necessarily know what code is being executed. It could do anything. Now, if a segfault occurs (particularly if it's caused by something other than a null pointer), the program is likely to be in a _worse_ state, but it's in an invalid state in either case. In neither case does it make any sense to try and recover, and in both cases, there's a definite risk in executing any further code - including cleanup code. Yes, the segfault is probably worse but not necessarily all that much worse. A logic error can be just as insidious to the state of a program as memory corruption, depending on what it is. I'm surprised by your response, I didn't think this was controversial. We could just as easily have said assert() throws an AssertException. (Or have two kinds of assert, one which is an Error and the other merely an Exception).
Re: Exception/Error division in D
Steven Schveighoffer wrote: On Wed, 30 May 2012 05:32:00 -0400, Don Clugston d...@nospam.com wrote: On 30/05/12 10:40, Jonathan M Davis wrote: On Wednesday, May 30, 2012 10:26:36 deadalnix wrote: The fact that error don't trigger scope and everything is nonsensial. If an Error is truly unrecoverable (as they're generally supposed to be), then what does it matter? Something fatal occured in your program, so it terminates. Because it's an Error, you can get a stack trace and report something before the program actually terminates, but continuing execution after an Error is considered to be truly _bad_ idea, so in general, why does it matter whether scope statements, finally blocks, or destructors get executed? It's only rarer cases where you're trying to do something like create a unit test framework on top of assert that you would need to catch an Error, and that's questionable enough as it is. In normal program execution, an error is fatal, so cleanup is irrelevant and even potentially dangerous, because your program is already in an invalid state. That's true for things like segfaults, but in the case of an AssertError, there's no reason to believe that cleanup would cause any damage. There's also no reason to assume that orderly cleanup *doesn't* cause any damage. In fact, it's not reasonable to assume *anything*. Which is the point. If you want to recover from an error, you have to do it manually. It should be doable, but the default handling should not need to be defined (i.e. implementations should be free to do whatever they want). But there is no reasonable *default* for handling an error that the runtime can assume. I'd classify errors/exceptions into three categories: 1. corruption/segfault -- not recoverable under any reasonable circumstances. Special cases exist (such as a custom paging mechanism). 2. program invariant errors (i.e. assert errors) -- Recovery is not defined by the runtime, so you must do it manually. Any decision the runtime makes will be arbitrary, and could be wrong. 3. try/catch exceptions -- these are planned for and *expected* to occur because the program cannot control it's environment. e.g. EOF when none was expected. The largest problem with the difference between 2 and 3 is the actual decision of whether an exceptional case is categorized as 2 or 3 can be decoupled from the code that decides between them. For example: double invert(double x) { assertOrEnfoce?(x != 0); // which should it be? return 1.0/x; } It's a logic error. Thus, double invert(double x) in { assert(x != 0); } body { return 1.0/x; } case 1: void main() { writeln(invert(0)); // clearly a program error } Obviously a logic error. case 2: int main(string[] args) { writeln(invert(to!double(args[1])); // clearly a catchable error } This should be int main(string[] args) { auto arg = to!double(args[1]); enforce(arg != 0); writeln(invert(arg)); } The enforce is needed because args[1] is user input. If the programmer controlled the value of arg and believes arg != 0 always holds then no enforce would be needed. Doesn't this make sense? Jens PS For the record, I think (like most) that Errors should like Exceptions work with scope, etc. The only arguments against is the theoretical possibility of causing more damage while cleaning up. I say theoretical because there was no practical example given. It seems that it may cause more damage but it does not need to. Of course, if damage happens it's the programmers fault but it's also the programmer's fault if he does not try to do a graceful shutdown, i.e. closing sockets, sending a crash report, or similar.
Re: Exception/Error division in D
On 30.05.2012 19:05, Steven Schveighoffer wrote: On Wed, 30 May 2012 05:32:00 -0400, Don Clugston d...@nospam.com wrote: On 30/05/12 10:40, Jonathan M Davis wrote: On Wednesday, May 30, 2012 10:26:36 deadalnix wrote: The fact that error don't trigger scope and everything is nonsensial. If an Error is truly unrecoverable (as they're generally supposed to be), then what does it matter? Something fatal occured in your program, so it terminates. Because it's an Error, you can get a stack trace and report something before the program actually terminates, but continuing execution after an Error is considered to be truly _bad_ idea, so in general, why does it matter whether scope statements, finally blocks, or destructors get executed? It's only rarer cases where you're trying to do something like create a unit test framework on top of assert that you would need to catch an Error, and that's questionable enough as it is. In normal program execution, an error is fatal, so cleanup is irrelevant and even potentially dangerous, because your program is already in an invalid state. That's true for things like segfaults, but in the case of an AssertError, there's no reason to believe that cleanup would cause any damage. There's also no reason to assume that orderly cleanup *doesn't* cause any damage. In fact, it's not reasonable to assume *anything*. Which is the point. If you want to recover from an error, you have to do it manually. It should be doable, but the default handling should not need to be defined (i.e. implementations should be free to do whatever they want). But there is no reasonable *default* for handling an error that the runtime can assume. I'd say that calling scope, destructors etc. on Error being thrown is the most _useful_ thing in all cases. If you realy-realy afraid of memory corruption killing sensitive data, taking control of OS and so on - you just catch Errors early on inside such sensitive functions. And call C's abort(). And that's it. Let's make common and hard case default and automatic plz. -- Dmitry Olshansky
Re: Exception/Error division in D
On Wed, 30 May 2012 11:47:34 -0400, Jens Mueller jens.k.muel...@gmx.de wrote: Steven Schveighoffer wrote: case 2: int main(string[] args) { writeln(invert(to!double(args[1])); // clearly a catchable error } This should be int main(string[] args) { auto arg = to!double(args[1]); enforce(arg != 0); writeln(invert(arg)); } The enforce is needed because args[1] is user input. If the programmer controlled the value of arg and believes arg != 0 always holds then no enforce would be needed. Doesn't this make sense? Yes and no. Yes, the ultimate result of what you wrote is the desired functionality. But no, I don't think you have properly solved the problem. Consider that user data, or environment data, can come from anywhere, and at any time. Consider also that you have decoupled the function parameter validation from the function itself! Ideally, invert should be the one deciding whether the original data is valid or not. In order to write correct code, I must know what the contents of invert are as the writer of main. I'd rather do something like: int main(string[] args) { auto argToInvert = to!double(args[1]); validateInvertArgs(argToInvert); // uses enforce invert(argToInvert); } Note that even *this* isn't ideal, because now the author of invert has to write and maintain a separate function for validating its arguments, even though invert is *already* validating its arguments. It's almost as if, I want to re-use the same code inside invert that validates its arguments, but use a different mechanism to flag an error, depending on the source of the arguments. It can get even more tricky, if say a function has two parameters, and one is hard-coded and the other comes from user input. PS For the record, I think (like most) that Errors should like Exceptions work with scope, etc. The only arguments against is the theoretical possibility of causing more damage while cleaning up. I say theoretical because there was no practical example given. It seems that it may cause more damage but it does not need to. Of course, if damage happens it's the programmers fault but it's also the programmer's fault if he does not try to do a graceful shutdown, i.e. closing sockets, sending a crash report, or similar. Indeed, it's all up to the programmer to handle the situation properly. If an assert occurs, the program may be already in an invalid state, and *trying to save* files or close/flush databases may corrupt the data. My point is, it's impossible for the runtime to know that your code is properly handling the error or not, and that running all the finally/scope blocks will not be worse than not doing it. -Steve
Re: Exception/Error division in D
Steven Schveighoffer wrote: On Wed, 30 May 2012 11:47:34 -0400, Jens Mueller jens.k.muel...@gmx.de wrote: Steven Schveighoffer wrote: case 2: int main(string[] args) { writeln(invert(to!double(args[1])); // clearly a catchable error } This should be int main(string[] args) { auto arg = to!double(args[1]); enforce(arg != 0); writeln(invert(arg)); } The enforce is needed because args[1] is user input. If the programmer controlled the value of arg and believes arg != 0 always holds then no enforce would be needed. Doesn't this make sense? Yes and no. Yes, the ultimate result of what you wrote is the desired functionality. But no, I don't think you have properly solved the problem. Consider that user data, or environment data, can come from anywhere, and at any time. Consider also that you have decoupled the function parameter validation from the function itself! Ideally, invert should be the one deciding whether the original data is valid or not. In order to write correct code, I must know what the contents of invert are as the writer of main. I'd rather do something like: int main(string[] args) { auto argToInvert = to!double(args[1]); validateInvertArgs(argToInvert); // uses enforce invert(argToInvert); } Note that even *this* isn't ideal, because now the author of invert has to write and maintain a separate function for validating its arguments, even though invert is *already* validating its arguments. It's almost as if, I want to re-use the same code inside invert that validates its arguments, but use a different mechanism to flag an error, depending on the source of the arguments. It can get even more tricky, if say a function has two parameters, and one is hard-coded and the other comes from user input. Why should invert validate its arguments? invert just states if the input has this and that property, then I will return the inverse of the argument. And it makes sure that its assumptions actually hold. And these assumption are that fundamental that failing to verify these is an error. Why should it do more than that? Actually, it can't do more than that because it does not know what to do. Assuming the user passed 0 then different recovery approaches are possible. PS For the record, I think (like most) that Errors should like Exceptions work with scope, etc. The only arguments against is the theoretical possibility of causing more damage while cleaning up. I say theoretical because there was no practical example given. It seems that it may cause more damage but it does not need to. Of course, if damage happens it's the programmers fault but it's also the programmer's fault if he does not try to do a graceful shutdown, i.e. closing sockets, sending a crash report, or similar. Indeed, it's all up to the programmer to handle the situation properly. If an assert occurs, the program may be already in an invalid state, and *trying to save* files or close/flush databases may corrupt the data. My point is, it's impossible for the runtime to know that your code is properly handling the error or not, and that running all the finally/scope blocks will not be worse than not doing it. I thought this is the only argument for not executing finally/scope blocks. Because running these in case of an Error may actually be worse than not running them. Not that we have an example of such code but that's the theoretical issue brought up against executing finally/scope in case of an Error. Jens
Re: Exception/Error division in D
On Wednesday, May 30, 2012 15:28:22 Jacob Carlborg wrote: On 2012-05-30 12:59, Jonathan M Davis wrote: Yes, which is why it's better to have an Error thrown rather than a halt instruction be executed. But that doesn't mean that any attempt at cleanup is any more valid. If you're not supposed to be able to catch Errors then what's the difference? You can catch them to print out additional information or whatever is useful to generate more information about the Error. In fact, just what the Error gives you is already more useful: message, file, line number, stack trace, etc. That alone makes an Error more useful than a halt instruction. You can catch them to attempt explicit cleanup that absolutely must be done for whatever reason (with the knowledge that it's potentially dangerous to do that cleanup due to the Error). You can catch them in very controlled circumstances where you know that continuing is safe (obviously this isn't the sort of thing that you do in @safe code). For instance, in some restricted cases, that could be done with an OutOfMemoryError. But when you do that sort of thing you have to catch the Error _very_ close to the throw point and be sure that there's no cleanup code in between. It only works when you can guarantee yourself that the program state is not being compromised by the Error, and you're able to guarantee that continuing from the catch point is safe. That works in some cases with AssertError in unit test code but becomes problematic as such code becomes more complex. - Jonathan M Davis
Re: Exception/Error division in D
On Wednesday, May 30, 2012 17:29:30 Don Clugston wrote: On 30/05/12 12:59, Jonathan M Davis wrote: On Wednesday, May 30, 2012 11:32:00 Don Clugston wrote: On 30/05/12 10:40, Jonathan M Davis wrote: On Wednesday, May 30, 2012 10:26:36 deadalnix wrote: The fact that error don't trigger scope and everything is nonsensial. If an Error is truly unrecoverable (as they're generally supposed to be), then what does it matter? Something fatal occured in your program, so it terminates. Because it's an Error, you can get a stack trace and report something before the program actually terminates, but continuing execution after an Error is considered to be truly _bad_ idea, so in general, why does it matter whether scope statements, finally blocks, or destructors get executed? It's only rarer cases where you're trying to do something like create a unit test framework on top of assert that you would need to catch an Error, and that's questionable enough as it is. In normal program execution, an error is fatal, so cleanup is irrelevant and even potentially dangerous, because your program is already in an invalid state. That's true for things like segfaults, but in the case of an AssertError, there's no reason to believe that cleanup would cause any damage. In fact, generally, the point of an AssertError is to prevent the program from entering an invalid state. An assertion failure really isn't all that different from a segfault. By definition, if an assertion fails, the program is an invalid state, because the whole point of the assertion is to guarantee something about the program's state. There's a big difference. A segfault is a machine error. The integrity of the machine model has been violated, and the machine is in an out-of-control state. In particular, the stack may be corrupted, so stack unwinding may not be successful. But, in an assert error, the machine is completely intact; the error is at a higher level, which does not interfere with stack unwinding. Damage is possible only if you've written your destructors/finally code extremely poorly. Note that, unlike C++, it's OK to throw a new Error or Exception from inside a destructor. But with (say) a stack overflow, you don't necessarily know what code is being executed. It could do anything. There is definitely a difference in severity. Clearly memory corruption is more severe than a logic error in your code. However, in the general case, if you have a logic error in your code which is caught by an assertion, there's no way to know without actually examining the code how valid the state of the program is at that point. It's in an invalid state _by definition_, because the assertion was testing the validity of the state of the program, and it failed. So, at that point, it's only a question of degree. _How_ invalid is the state? Since there's no way for the program to know how severe the logic error was, it has no way of knowing whether it's safe to run any cleanup code (the same as the program has no way of knowing whether a segfault is relatively minor - e.g. a null pointer - or absolutely catastrophic - e.g. memory is horribly corrupted). If you got an OutOfMemoryError rather than one specifically indicating a logic error (as with Errors such as AssertError or RangeError), then that's specifcally telling you that your program has run out of a particular resource (i.e. memory), which means that any code which assumes that that resource is available (which in the case of memory is pretty much all code) will fail. Running cleanup code could be very precarious at that point if it allocates any memory (which a lot of cleanup code wouldn't, but I'm sure that it would be very easy to find cleanup code which did). Any further attempts at allocation would result in more OutOfMemoryErrors and leaving the cleanup code only partially run, thereby possibly making things even worse, depending on what the cleanup code does. Running cleanup code is _not_ safe when an Error is thrown, because the program is definitely in an invalid state at that point, even if it's not as bad as a segfault can be. Now, it may be that that risk is worth it, especially since a lot of the time, cleanup code won't be invalidated in the least by whatever caused Errors elsewhere in the program, and there are definitely plenty of cases where at least attempting to cleanup everything is better than skipping it all due of an Error somewhere else in the program. But it's still not safe. It's a question of whether we think that the risks posed by trying to run cleanup code after the program is in an invalid enough state that an Error was thrown are too great to attempt cleanup or whether we think that the problems caused by skipping that cleanup are greater. Now, if a segfault occurs (particularly if it's caused by something other than a null pointer), the program is likely to be
Re: Exception/Error division in D
Le 30/05/2012 17:29, Don Clugston a écrit : There's a big difference. A segfault is a machine error. The integrity of the machine model has been violated, and the machine is in an out-of-control state. In particular, the stack may be corrupted, so stack unwinding may not be successful. But, in an assert error, the machine is completely intact; the error is at a higher level, which does not interfere with stack unwinding. Damage is possible only if you've written your destructors/finally code extremely poorly. Note that, unlike C++, it's OK to throw a new Error or Exception from inside a destructor. But with (say) a stack overflow, you don't necessarily know what code is being executed. It could do anything. Most segfault are null deference or unintizialized pointer deference. Both are recoverable.
Re: Exception/Error division in D
On Thursday, May 31, 2012 00:01:16 deadalnix wrote: Le 30/05/2012 17:29, Don Clugston a écrit : There's a big difference. A segfault is a machine error. The integrity of the machine model has been violated, and the machine is in an out-of-control state. In particular, the stack may be corrupted, so stack unwinding may not be successful. But, in an assert error, the machine is completely intact; the error is at a higher level, which does not interfere with stack unwinding. Damage is possible only if you've written your destructors/finally code extremely poorly. Note that, unlike C++, it's OK to throw a new Error or Exception from inside a destructor. But with (say) a stack overflow, you don't necessarily know what code is being executed. It could do anything. Most segfault are null deference or unintizialized pointer deference. Both are recoverable. If you dereferenced a null pointer, it's a bug in your code. Your code is assuming that the pointer was non-null, which was obviously incorrect, because it was null. That's _not_ recoverable in the general case. Your code was obviously written with the assumption that the pointer was non-null, so your code is wrong, and so continuing to execute it makes no sense, because it's in an invalid state and could do who-knows-what. If there's any possibility of a pointer being null, the correct thing to do is to check it before dereferencing it. If you don't, it's bug. Now, it's perfectly possible to design code which never checks for null pointers and if a null pointer is dereferenced throws an Exception and attempts to recover from it (assuming that it's possible to detect the dereference and throw at that point, which AFAIK is impossible with segfaults - maybe it could be done on Windows with its Access Violations, but segfaults trigger a signal handler, and you're screwed at that point). But writing code which just assumes that pointers are non-null and will throw if they are null is incredibly sloppy. It means that you're treating pointers like you'd treat user input rather than as part of your code. - Jonathan M Davis
Re: Exception/Error division in D
On Wednesday, 30 May 2012 at 22:22:01 UTC, Jonathan M Davis wrote: On Thursday, May 31, 2012 00:01:16 deadalnix wrote: Most segfault are null deference or unintizialized pointer deference. Both are recoverable. If you dereferenced a null pointer, it's a bug in your code. Your code is assuming that the pointer was non-null, which was obviously incorrect, because it was null. That's _not_ recoverable in the general case. Your code was obviously written with the assumption that the pointer was non-null, so your code is wrong, and so continuing to execute it makes no sense, because it's in an invalid state and could do who-knows-what. If there's any possibility of a pointer being null, the correct thing to do is to check it before dereferencing it. If you don't, it's bug. Now, it's perfectly possible to design code which never checks for null pointers and if a null pointer is dereferenced throws an Exception and attempts to recover from it (assuming that it's possible to detect the dereference and throw at that point, which AFAIK is impossible with segfaults - maybe it could be done on Windows with its Access Violations, but segfaults trigger a signal handler, and you're screwed at that point). But writing code which just assumes that pointers are non-null and will throw if they are null is incredibly sloppy. It means that you're treating pointers like you'd treat user input rather than as part of your code. - Jonathan M Davis All code has bugs in it. It's nice being notified about it and all, but if you release a server application, and it crashes every single time it encounters any bug... well, your customers will not have a fun time.
Re: Exception/Error division in D
On Thursday, 31 May 2012 at 00:58:42 UTC, Kapps wrote: All code has bugs in it. It's nice being notified about it and all, but if you release a server application, and it crashes every single time it encounters any bug... well, your customers will not have a fun time. Note that this assumes you're in a part of code where you can isolate and revert anything the error may cause. Roll back a database transaction, disconnect a client, abort their web page request, etc.
Re: Exception/Error division in D
On 05/31/12 00:21, Jonathan M Davis wrote: Now, it's perfectly possible to design code which never checks for null pointers and if a null pointer is dereferenced throws an Exception and attempts to recover from it (assuming that it's possible to detect the dereference and throw at that point, which AFAIK is impossible with segfaults - maybe it could be done on Windows with its Access Violations, but segfaults trigger a signal handler, and you're screwed at that point). But writing code No, it's easily recoverable. That does not mean however that it would be a good idea to map segfaults to exceptions as a language feature. And dereferencing a null pointer is *not* guaranteed to trap, all you need is a large enough offset and you will get silent data corruption. int i = 42; auto j = cast(size_t)i; ubyte* p = null; p[j] = 13; assert(i!=42); // oops artur
Re: Exception/Error division in D
On May 30, 2012, at 5:58 PM, Kapps opantm2+s...@gmail.com wrote: On Wednesday, 30 May 2012 at 22:22:01 UTC, Jonathan M Davis wrote: On Thursday, May 31, 2012 00:01:16 deadalnix wrote: Most segfault are null deference or unintizialized pointer deference. Both are recoverable. If you dereferenced a null pointer, it's a bug in your code. Your code is assuming that the pointer was non-null, which was obviously incorrect, because it was null. That's _not_ recoverable in the general case. Your code was obviously written with the assumption that the pointer was non-null, so your code is wrong, and so continuing to execute it makes no sense, because it's in an invalid state and could do who-knows-what. If there's any possibility of a pointer being null, the correct thing to do is to check it before dereferencing it. If you don't, it's bug. Now, it's perfectly possible to design code which never checks for null pointers and if a null pointer is dereferenced throws an Exception and attempts to recover from it (assuming that it's possible to detect the dereference and throw at that point, which AFAIK is impossible with segfaults - maybe it could be done on Windows with its Access Violations, but segfaults trigger a signal handler, and you're screwed at that point). But writing code which just assumes that pointers are non-null and will throw if they are null is incredibly sloppy. It means that you're treating pointers like you'd treat user input rather than as part of your code. - Jonathan M Davis All code has bugs in it. It's nice being notified about it and all, but if you release a server application, and it crashes every single time it encounters any bug... well, your customers will not have a fun time. It's worth noting that Google apps abort on error. For server apps, a process crash is often designed to be invisible to the client, as much to allow seamless code upgrades as anything.
Re: Exception/Error division in D
On 5/30/2012 5:58 PM, Kapps wrote: All code has bugs in it. It's nice being notified about it and all, but if you release a server application, and it crashes every single time it encounters any bug... well, your customers will not have a fun time. The correct response to a server app crashing is to restart it, not attempt to keep a program in an invalid state running. Attempting to continue normal operation of a program that has entered an invalid state is bad news from front to back. It is wrong wrong wrong wrong.
Re: Exception/Error division in D
On 5/30/2012 2:32 AM, Don Clugston wrote: In fact, generally, the point of an AssertError is to prevent the program from entering an invalid state. It's already in an invalid state when the assert fails, by definition. It is not recoverable. The only option is a more or less graceful shutdown.
Re: Exception/Error division in D
On 5/30/2012 8:05 AM, Steven Schveighoffer wrote: I'd classify errors/exceptions into three categories: 1. corruption/segfault -- not recoverable under any reasonable circumstances. Special cases exist (such as a custom paging mechanism). 2. program invariant errors (i.e. assert errors) -- Recovery is not defined by the runtime, so you must do it manually. Any decision the runtime makes will be arbitrary, and could be wrong. 3. try/catch exceptions -- these are planned for and *expected* to occur because the program cannot control it's environment. e.g. EOF when none was expected. A recoverable exception is NOT a logic bug in your program, which is why it is recoverable. If there is recovery possible from a particular assert error, then you are using asserts incorrectly. Assert errors occur because your program has entered an unanticipated, invalid state. There's no such thing as knowing how to put it back into a valid state, because you don't know where it went wrong and how much is corrupted, etc.
Re: Exception/Error division in D
On May 24, 2012, at 11:50 PM, Jacob Carlborg wrote: On 2012-05-24 21:33, Sean Kelly wrote: This is a good point. OutOfMemory conditions aside, the only time I'd want to recover from an Error condition was at the point the event occurred, not somewhere up the stack. You never feel you want to catch at the top level, print a sensible error message and then exit the application? Instead of the application just disappearing for user. Well sure, but I wouldn't consider this recovery.
Re: Exception/Error division in D
On 2012-05-24 21:33, Sean Kelly wrote: This is a good point. OutOfMemory conditions aside, the only time I'd want to recover from an Error condition was at the point the event occurred, not somewhere up the stack. You never feel you want to catch at the top level, print a sensible error message and then exit the application? Instead of the application just disappearing for user. If the application can't print the error message it just fails in the same way as if you had not catch the error. -- /Jacob Carlborg
Re: Exception/Error division in D
On May 24, 2012, at 3:27 AM, Denis Shelomovskij wrote: Let's talk about an abstract situation without caring about breaking existing code, current docs, implementation etc. Definitions: * an Exception is something that tigers scope guards and executes catch/finally blocks if thrown; * an Error is something that doesn't do it. As a result _we can't do any clean-up if an Error is thrown_ because scope guards and catch/finally blocks aren't executed and the program is in invalid state because of this. Of course it's theoretically possible to code without scope guards and catch/finally blocks but isn't applicably for a real project. E.g. in some editor if and Error is thrown there is no ability to save opened documents. Main Question: What do you think can be an Error? Can Integer Divide by Zero be an Error? Definitely, not. Can Access Violation be an Error? No, because it's very common to access a field/virtual member function of a null object. Can Out of memory be an Error? No, because e.g. if I read a user file that require me to create a large array ( 100 MiB, e.g.) I don't want to crash, but just tell, that Dear user, the file can't be opened because it requires... So what am I think can be an Error? IMHO, nothing. Because throwing everything can indicate that program in very good/terribly bad state and compiler doesn't know anything about that. And because fatal error is fatal the program should just try to print error and close instead of throwing something. I agree. However, the benefit to having Error is so that nothrow can exist. If every exception were considered recoverable then we'd have to throw out nothrow as well, since basically anything can generate an access violation, for example. Or we could weaken nothrow so that it didn't even allow memory allocations, which would render it largely useless. For what it's worth, the D runtime does do clean-up for both Errors and Exceptions today. The only difference is codgen for scope statements and such inside nothrow functions--instead of being rewritten as try/finally the on-exit code is just inserted at the proper scope exit points. Let's now return to the real D world. Current implementation treats Errors as Exceptions for now. Documentation keeps silence. All listed can't be an error cases are Errors (and it's terrible). So why do we has Exception/Error division in D? Because of nothrow. Personally I don't need nothrow for that high cost of making D unusable for me. Lets realize and solve Exception/Error problem and solve nothrow in the second place. Seems you already know this. Oops. I'm inclined to agree, personally. nothrow is less useful in D than in C++ because it's safe to throw from dtors in D (problems related to throwing from a finalizer during a GC collection aside--that's more an exception safety issue for the GC than anything else).
Re: Exception/Error division in D
I'd say, removing nothrow and Error from D would be a good idea. Everybody throws Exception from anywhere. What would be the practical reason not to do that (besides potentially breaking code)? On Thu, May 24, 2012 at 9:51 PM, Sean Kelly s...@invisibleduck.org wrote: On May 24, 2012, at 3:27 AM, Denis Shelomovskij wrote: Let's talk about an abstract situation without caring about breaking existing code, current docs, implementation etc. Definitions: * an Exception is something that tigers scope guards and executes catch/finally blocks if thrown; * an Error is something that doesn't do it. As a result _we can't do any clean-up if an Error is thrown_ because scope guards and catch/finally blocks aren't executed and the program is in invalid state because of this. Of course it's theoretically possible to code without scope guards and catch/finally blocks but isn't applicably for a real project. E.g. in some editor if and Error is thrown there is no ability to save opened documents. Main Question: What do you think can be an Error? Can Integer Divide by Zero be an Error? Definitely, not. Can Access Violation be an Error? No, because it's very common to access a field/virtual member function of a null object. Can Out of memory be an Error? No, because e.g. if I read a user file that require me to create a large array ( 100 MiB, e.g.) I don't want to crash, but just tell, that Dear user, the file can't be opened because it requires... So what am I think can be an Error? IMHO, nothing. Because throwing everything can indicate that program in very good/terribly bad state and compiler doesn't know anything about that. And because fatal error is fatal the program should just try to print error and close instead of throwing something. I agree. However, the benefit to having Error is so that nothrow can exist. If every exception were considered recoverable then we'd have to throw out nothrow as well, since basically anything can generate an access violation, for example. Or we could weaken nothrow so that it didn't even allow memory allocations, which would render it largely useless. For what it's worth, the D runtime does do clean-up for both Errors and Exceptions today. The only difference is codgen for scope statements and such inside nothrow functions--instead of being rewritten as try/finally the on-exit code is just inserted at the proper scope exit points. Let's now return to the real D world. Current implementation treats Errors as Exceptions for now. Documentation keeps silence. All listed can't be an error cases are Errors (and it's terrible). So why do we has Exception/Error division in D? Because of nothrow. Personally I don't need nothrow for that high cost of making D unusable for me. Lets realize and solve Exception/Error problem and solve nothrow in the second place. Seems you already know this. Oops. I'm inclined to agree, personally. nothrow is less useful in D than in C++ because it's safe to throw from dtors in D (problems related to throwing from a finalizer during a GC collection aside--that's more an exception safety issue for the GC than anything else). -- Bye, Gor Gyolchanyan.
Re: Exception/Error division in D
On Thu, 24 May 2012 06:27:12 -0400, Denis Shelomovskij verylonglogin@gmail.com wrote: Let's talk about an abstract situation without caring about breaking existing code, current docs, implementation etc. Definitions: * an Exception is something that tigers scope guards and executes catch/finally blocks if thrown; * an Error is something that doesn't do it. I'll give you a different definition: * an Exception is something that can be handled far away from the context of the error, because the system can safely unwind the stack. * an Error must be handled at the point the error occurred, or the program state is by definition invalid. As a result _we can't do any clean-up if an Error is thrown_ because scope guards and catch/finally blocks aren't executed and the program is in invalid state because of this. Of course it's theoretically possible to code without scope guards and catch/finally blocks but isn't applicably for a real project. E.g. in some editor if and Error is thrown there is no ability to save opened documents. Main Question: What do you think can be an Error? Can Integer Divide by Zero be an Error? Definitely, not. I agree with you there. However, the problem isn't nearly as bad as you say. The runtime doesn't actually deal with SIGFPE on Unix based systems, and most places where an Error is thrown, it's within asserts, which are compiled out in release code. If you get one of these, you can handle the signal. I think on Windows it's handled for you, but there should be a way to intercept this and do recovery. Can Access Violation be an Error? No, because it's very common to access a field/virtual member function of a null object. I'd argue this is unrecoverable. Access Violation results from corruption, and the damage has already been done. Even a null pointer access can be the cause of previous corruption. There is no recovery from memory corruption. There is no way to plan for it. Now, if you want to handle it specifically for your program, that should be doable, at your own risk. But there's no way throwing an Exception is a valid result. Can Out of memory be an Error? No, because e.g. if I read a user file that require me to create a large array ( 100 MiB, e.g.) I don't want to crash, but just tell, that Dear user, the file can't be opened because it requires... Right, out of memory is only an error if your program's invariant depends on the memory allocation. You can plan for the above easily enough, but not realistically for all tasks and all library code that require allocation. For example, let's say you are restructuring a hash table, and you reallocate some nodes. You have half transferred over the old structure to the new structure, and you run out of memory. How to recover from this? In summary, I think we can adjust Windows divide by zero errors to allow you to handle them manually, the Posix version is fine (no Error is generated), access violations are unequivocally Errors, and out of memory should be fixable by making allocation routines that do not throw (just return null). An interesting idea would be to allow a global handler for all errors. So if you throw an Exception, it unwinds the stack like normal. If you throw an Error, it checks a handler to see if you want to handle that error (via registering a function/delegate), and if not handled throws Error without properly unwinding the stack. -Steve
Re: Exception/Error division in D
On May 24, 2012, at 11:39 AM, Steven Schveighoffer wrote: On Thu, 24 May 2012 06:27:12 -0400, Denis Shelomovskij verylonglogin@gmail.com wrote: Let's talk about an abstract situation without caring about breaking existing code, current docs, implementation etc. Definitions: * an Exception is something that tigers scope guards and executes catch/finally blocks if thrown; * an Error is something that doesn't do it. I'll give you a different definition: * an Exception is something that can be handled far away from the context of the error, because the system can safely unwind the stack. * an Error must be handled at the point the error occurred, or the program state is by definition invalid. This is a good point. OutOfMemory conditions aside, the only time I'd want to recover from an Error condition was at the point the event occurred, not somewhere up the stack. Can Access Violation be an Error? No, because it's very common to access a field/virtual member function of a null object. I'd argue this is unrecoverable. Access Violation results from corruption, and the damage has already been done. Even a null pointer access can be the cause of previous corruption. There is no recovery from memory corruption. There is no way to plan for it. Now, if you want to handle it specifically for your program, that should be doable, at your own risk. But there's no way throwing an Exception is a valid result. Right. Recovery is potentially valid at the point of failure, not somewhere up the stack. Can Out of memory be an Error? No, because e.g. if I read a user file that require me to create a large array ( 100 MiB, e.g.) I don't want to crash, but just tell, that Dear user, the file can't be opened because it requires... Right, out of memory is only an error if your program's invariant depends on the memory allocation. You can plan for the above easily enough, but not realistically for all tasks and all library code that require allocation. For example, let's say you are restructuring a hash table, and you reallocate some nodes. You have half transferred over the old structure to the new structure, and you run out of memory. How to recover from this? I think it's fair to expect code that allocates be exception-safe in the face of allocation errors. I know I'm always very careful with containers so that an allocation failure doesn't result in corruption, for example. In summary, I think we can adjust Windows divide by zero errors to allow you to handle them manually, the Posix version is fine (no Error is generated), access violations are unequivocally Errors, and out of memory should be fixable by making allocation routines that do not throw (just return null). It would be kind of cool if there were some sort of unified way to handle system-generated errors, though I don't know that this is practical. signals sort of work on Windows, but I'm pretty sure the contextual sigaction stuff does not. An interesting idea would be to allow a global handler for all errors. So if you throw an Exception, it unwinds the stack like normal. If you throw an Error, it checks a handler to see if you want to handle that error (via registering a function/delegate), and if not handled throws Error without properly unwinding the stack. ^^ this
Re: Exception/Error division in D
On Thu, 24 May 2012 15:33:07 -0400, Sean Kelly s...@invisibleduck.org wrote: On May 24, 2012, at 11:39 AM, Steven Schveighoffer wrote: Can Out of memory be an Error? No, because e.g. if I read a user file that require me to create a large array ( 100 MiB, e.g.) I don't want to crash, but just tell, that Dear user, the file can't be opened because it requires... Right, out of memory is only an error if your program's invariant depends on the memory allocation. You can plan for the above easily enough, but not realistically for all tasks and all library code that require allocation. For example, let's say you are restructuring a hash table, and you reallocate some nodes. You have half transferred over the old structure to the new structure, and you run out of memory. How to recover from this? I think it's fair to expect code that allocates be exception-safe in the face of allocation errors. I know I'm always very careful with containers so that an allocation failure doesn't result in corruption, for example. I don't think it's fair to expect *all* code to be able to safely recover from an out of memory exception. I pretty much *never* write code that worries about out of memory errors. One cannot always expect an operation involving hundreds of allocations to be atomic. That being said, we should provide a mechanism so you can handle it, as it's reliably detectable and very recoverable in many situations. -Steve