June 01, 2012
On Fri, 01 Jun 2012 19:43:06 +0200, Walter Bright <newshound2@digitalmars.com> wrote:

> On 6/1/2012 1:48 AM, Dmitry Olshansky wrote:
>> Or better - save game and then crash gracefully.
>
> That can result in saving a corrupted game state, which then will not load, or worse, load and then cause another crash.
>
> I would suggest instead implementing an auto-save feature which automatically saves the game state at regular intervals.

Autosave is a good idea. Autoloading last save upon resuming the game is not.
But saving the game before crashing, then explicitly telling the player that
something went wrong and the save might be corrupted and not to expect too much
from it, I think that's pretty good.

Of course, the crashing part may be deep buried and will only trigger under
obscure circumstances 100 hours down the road, in which case the autosave is
definitely the correct solution. Basically it's about provability and
consequence. If you can check the save file for corruption upon next start,
no problem! If the result is two hours lost, no problem! If you can't
check the save file, and the result may be 100 hours lost, don't save a
potentially corrupted file (I'm looking at you, Bethesda).
June 02, 2012
On 6/1/2012 3:25 PM, Simen Kjaeraas wrote:
> Autosave is a good idea. Autoloading last save upon resuming the game is not.
> But saving the game before crashing, then explicitly telling the player that
> something went wrong and the save might be corrupted and not to expect too much
> from it, I think that's pretty good.

An awful lot of game players are not computer professionals and will not understand such instructions nor tolerate them.
June 02, 2012
01.06.2012 21:43, Walter Bright написал:
> On 6/1/2012 1:48 AM, Dmitry Olshansky wrote:
>> Or better - save game and then crash gracefully.
>
> That can result in saving a corrupted game state, which then will not
> load, or worse, load and then cause another crash.
>
> I would suggest instead implementing an auto-save feature which
> automatically saves the game state at regular intervals.

Even in my old D1 3D editor I implemented auto-saving because it was hard to recover from internal errors and I decided to log-and-crash. And it still works perfectly, user can loose at most a minute of work!

I'd like every program to do the same thing. More that that, my favourite game (Windows XP SP3) sometimes fails to start (you know, "system32\config\<reg file> is corrupted" error) and I have to launch my restore script and almost every time the last restore point is broken (and its creation time is often a few hours before last shut down)!

So trying to save after and program logic error is definitely a mistake.

-- 
Денис В. Шеломовский
Denis V. Shelomovskij
June 04, 2012
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.
June 04, 2012
On 01/06/12 12:26, Walter Bright wrote:
> On 6/1/2012 1:48 AM, Dmitry Olshansky wrote:
>> On 01.06.2012 5:16, Walter Bright wrote:
>>> On 5/31/2012 3:22 AM, Dmitry Olshansky wrote:
>>>> On 31.05.2012 13:06, deadalnix wrote:
>>>>> This is called failing gracefully. And this highly recommended, and
>>>>> you
>>>>> KNOW that the system will fail at some point.
>>>>
>>>> Exactly. + The point I tried to argue but it was apparently lost:
>>>> doing stack unwinding and cleanup on most Errors (some Errors like
>>>> stack
>>>> overflow might not recoverable) is the best thing to do.
>>>
>>> This is all based on the assumption that the program is still in a valid
>>> state after an assert fail, and so any code executed after that and the
>>> data it relies on is in a workable state.
>>>
>> > This is a completely wrong assumption.
>>
>> To be frank a "completely wrong assumption" is flat-out exaggeration.
>> The only
>> problem that can make it "completely wrong" is memory corruption.
>> Others just
>> depend on specifics of system, e.g. wrong arithmetic in medical
>> software ==
>> critical, arithmetic bug in "refracted light color component" in say
>> 3-D game is
>> no problem, just log it and recover. Or better - save game and then crash
>> gracefully.
>
> Except that you do not know why the arithmetic turned out wrong - it
> could be the result of memory corruption.

This argument seems to be:

1. There exist cases where you cannot know why the assert failed.
2. Therefore you never know why an assert failed.
3. Therefore it is not safe to unwind the stack from a nothrow function.

Spot the fallacies.

The fallacy in moving from 2 to 3 is more serious than the one from 1 to 2: this argument is not in any way dependent on the assert occuring in a nothrow function. Rather, it's an argument for not having AssertError at all.



June 04, 2012
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.

June 04, 2012
On Mon, 04 Jun 2012 06:20:56 -0400, Don Clugston <dac@nospam.com> wrote:

> 1. There exist cases where you cannot know why the assert failed.
> 2. Therefore you never know why an assert failed.
> 3. Therefore it is not safe to unwind the stack from a nothrow function.
>
> Spot the fallacies.
>
> The fallacy in moving from 2 to 3 is more serious than the one from 1 to 2: this argument is not in any way dependent on the assert occuring in a nothrow function. Rather, it's an argument for not having AssertError at all.

I'm not sure that is the issue here at all.  What I see is that the unwinding of the stack is optional, based on the assumption that there's no "right" answer.

However, there is an underlying driver for not unwinding the stack -- nothrow.  If nothrow results in the compiler optimizing out whatever hooks a function needs to properly unwind itself (my limited understanding is that this helps performance), then there *is no choice*, you can't properly unwind the stack.

-Steve
June 05, 2012
On 04/06/12 21:29, Steven Schveighoffer wrote:
> On Mon, 04 Jun 2012 06:20:56 -0400, Don Clugston <dac@nospam.com> wrote:
>
>> 1. There exist cases where you cannot know why the assert failed.
>> 2. Therefore you never know why an assert failed.
>> 3. Therefore it is not safe to unwind the stack from a nothrow function.
>>
>> Spot the fallacies.
>>
>> The fallacy in moving from 2 to 3 is more serious than the one from 1
>> to 2: this argument is not in any way dependent on the assert occuring
>> in a nothrow function. Rather, it's an argument for not having
>> AssertError at all.
>
> I'm not sure that is the issue here at all. What I see is that the
> unwinding of the stack is optional, based on the assumption that there's
> no "right" answer.
>
> However, there is an underlying driver for not unwinding the stack --
> nothrow. If nothrow results in the compiler optimizing out whatever
> hooks a function needs to properly unwind itself (my limited
> understanding is that this helps performance), then there *is no
> choice*, you can't properly unwind the stack.
>
> -Steve

No, this whole issue started because the compiler currently does do unwinding whenever it can. And Walter claimed that's a bug, and it should be explicitly disabled.

It is, in my view, an absurd position. AFAIK not a single argument has been presented in favour of it. All arguments have been about "you should never unwind Errors".
June 05, 2012
On Tuesday, June 05, 2012 08:53:16 Don Clugston wrote:
> On 04/06/12 21:29, Steven Schveighoffer wrote:
> > On Mon, 04 Jun 2012 06:20:56 -0400, Don Clugston <dac@nospam.com> wrote:
> >> 1. There exist cases where you cannot know why the assert failed.
> >> 2. Therefore you never know why an assert failed.
> >> 3. Therefore it is not safe to unwind the stack from a nothrow function.
> >> 
> >> Spot the fallacies.
> >> 
> >> The fallacy in moving from 2 to 3 is more serious than the one from 1 to 2: this argument is not in any way dependent on the assert occuring in a nothrow function. Rather, it's an argument for not having AssertError at all.
> > 
> > I'm not sure that is the issue here at all. What I see is that the unwinding of the stack is optional, based on the assumption that there's no "right" answer.
> > 
> > However, there is an underlying driver for not unwinding the stack -- nothrow. If nothrow results in the compiler optimizing out whatever hooks a function needs to properly unwind itself (my limited understanding is that this helps performance), then there *is no choice*, you can't properly unwind the stack.
> > 
> > -Steve
> 
> No, this whole issue started because the compiler currently does do unwinding whenever it can. And Walter claimed that's a bug, and it should be explicitly disabled.
> 
> It is, in my view, an absurd position. AFAIK not a single argument has been presented in favour of it. All arguments have been about "you should never unwind Errors".

It's quite clear that we cannot completely, correctly unwind the stack in the face of Errors. As such, no one should be relying on stack unwinding when an Error is thrown. The implementation may manage it in some cases, but it's going to be unreliable in the general case regardless of how desirable it may or may not be.

The question is whether it's better to skip stack undwinding entirely when an Error is thrown. There are definitely cases where that would be better, since running cleanup code could just make things worse, corrupting even more stuff (including files and the like which may persist passed the termination of the program). On the other hand, there's a lot of cleanup code which would execute just fine when most Errors are thrown, and not running cleanup code causes its own set of problems. There's no way for the program to know which of the two situations that it's in when an Error is thrown. So, we have to pick one or the other.

I really don't know which is the better way to go. I'm very tempted to go with Walter on this one, since it would avoid making the worst case scenario worse, and if you have cleanup which _must_ be done, you're going to have to find a different way to handle it, because even perfect stack unwinding won't protect you from everything (e.g. power loss killing the computer). But arguably, the general case is cleaner if we do as much stack unwinding as we can.

Regardless, I think that there are a number of people in this thread who are mistaken in how recoverable they think Errors and/or segfaults are, and they seem to be the ones pushing the hardest for full stack unwinding on the theory that they could somehow ensure safe recovery and a clean shutdown when an Error occurs, which is almost never possible, and certainly isn't possible in the general case.

- Jonathan M Davis
June 05, 2012
On 05/06/12 09:07, Jonathan M Davis wrote:
> On Tuesday, June 05, 2012 08:53:16 Don Clugston wrote:
>> On 04/06/12 21:29, Steven Schveighoffer wrote:
>>> On Mon, 04 Jun 2012 06:20:56 -0400, Don Clugston<dac@nospam.com>  wrote:
>>>> 1. There exist cases where you cannot know why the assert failed.
>>>> 2. Therefore you never know why an assert failed.
>>>> 3. Therefore it is not safe to unwind the stack from a nothrow function.
>>>>
>>>> Spot the fallacies.
>>>>
>>>> The fallacy in moving from 2 to 3 is more serious than the one from 1
>>>> to 2: this argument is not in any way dependent on the assert occuring
>>>> in a nothrow function. Rather, it's an argument for not having
>>>> AssertError at all.
>>>
>>> I'm not sure that is the issue here at all. What I see is that the
>>> unwinding of the stack is optional, based on the assumption that there's
>>> no "right" answer.
>>>
>>> However, there is an underlying driver for not unwinding the stack --
>>> nothrow. If nothrow results in the compiler optimizing out whatever
>>> hooks a function needs to properly unwind itself (my limited
>>> understanding is that this helps performance), then there *is no
>>> choice*, you can't properly unwind the stack.
>>>
>>> -Steve
>>
>> No, this whole issue started because the compiler currently does do
>> unwinding whenever it can. And Walter claimed that's a bug, and it
>> should be explicitly disabled.
>>
>> It is, in my view, an absurd position. AFAIK not a single argument has
>> been presented in favour of it. All arguments have been about "you
>> should never unwind Errors".
>
> It's quite clear that we cannot completely, correctly unwind the stack in the
> face of Errors.

Well that's a motherhood statement. Obviously in the face of extreme memory corruption you can't guarantee *any* code is valid.
The *main* reason why stack unwinding would not be possible is if nothrow intentionally omits stack unwinding code.

> As such, no one should be relying on stack unwinding when an
> Error is thrown.

This conclusion DOES NOT FOLLOW. And I am getting so sick of the number of times this fallacy has been repeated in this thread.

These kinds of generalizations are completely invalid in a systems programming language.

> Regardless, I think that there are a number of people in this thread who are
> mistaken in how recoverable they think Errors and/or segfaults are, and they
> seem to be the ones pushing the hardest for full stack unwinding on the theory
> that they could somehow ensure safe recovery and a clean shutdown when an
> Error occurs, which is almost never possible, and certainly isn't possible in
> the general case.
>
> - Jonathan M Davis

Well I'm pushing it because I implemented it (on Windows).

I'm less knowledgeable about what happens on other systems, but know that on Windows, the whole system is far, far more robust than most people on this thread seem to think.

I can't see *any* problem with executing catch(Error) clauses. I cannot envisage a situation where that can cause a problem. I really cannot.

And catch(Exception) clauses won't be run, because of the exception chaining scheme we have implemented.

The only difficult case is 'finally' clauses, which may be expecting an Exception.