Next: C++/CLI limitations?
From: Gerhard Menzl on 15 Jul 2005 10:47 Dave Harris wrote: > I think your real problem there is that you have tons of unsaved work. > You can cope with program bugs, maybe, but you probably can't cope > with O/S crashes or power cuts so you need a more general strategy. > > My employers write word processors. Our approach is to write the > document to a temporary file every 10 or so minutes. We delete it > during a normal shutdown and also after a successful user-save. Any > documents found during start-up are then unsaved work lost due to a > crash, and we ask the user if she wants them restored. > > This mitigates the problem. They never lose more than 10 minutes work. > That's acceptable because crashes should be rare (if they are a daily > occurance we are already in big trouble). We are confident we can > restore the document because we had a known sane state when it was > written out. Automated saving is certainly a good idea, but it does not really address my point, for which I merely used the word processor as an example. You can do a lot of typing, drawing, or whatever in ten minutes. Destroying that work without at least attempting to save it upon a failed assertion is not going to enhance your reputation as a software vendor. -- Gerhard Menzl #dogma int main () Humans may reply by replacing the thermal post part of my e-mail address with "kapsch" and the top level domain part with "net". [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Peter Dimov on 15 Jul 2005 11:05 Gerhard Menzl wrote: > Peter Dimov wrote: > >> The two options aren't "abort and destroy hours of work" and "throw >> an exception". The two options are "throw an exception" and "don't >> throw an exception". >> >> In particular, nothing prevents the failed assertion handler to >> attempt an emergency save, using a different file name (to not >> clobber the "last known good" save), a different file format (if the >> native format consists of a dump of the data structures and will >> likely produce an unreadable file), and a different, extra-paranoid >> code path. _Then_ abort. > > How can a global handler obtain the necessary context information to > perform the emergency save? And how is throwing an exception and postponing the emergency save until unwinding is complete better at obtaining that context information? >> Yes, I tried to gave an example of that in the other post. >> Unfortunately, two important global invariants are "the heap is not >> corrupted" and "there are no dangling pointers that are causing >> damage", and a violation of those is usually manifested as a >> violation of another (possibly local) invariant. > > I agree that in case of a corrupted heap (or stack), there is no point > in continuing. But there are many local invariants that do not affect > the global program state and thus hardly warrant an abort. How would > you handle those? The problem is that a corrupted heap or a dangling pointer can easily break local invariants. When you see a broken local invariant, it is not safe to assume that it is caused by a local error that will go away when the stack is unwound. >> A broken database connection, a corrupted table, a corrupted data >> file are not logic errors. The number of logic errors in a program is >> constant and does not depend on external factors. (Unless the program >> itself changes, i.e. you consider the information in the database >> "code", a part of the program.) > > Sort of. A considerable part of the program logic rests in stored > procedures in the database itself. Well, the good thing here is that a broken database invariant can't affect the other program invariants because they don't share the same address space, so it might be possible for the part of the program that doesn't need access to the database to function normally. [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Alf P. Steinbach on 15 Jul 2005 12:10 * Peter Dimov: > > And how is throwing an exception and postponing the emergency save until > unwinding is complete better at obtaining that context information? The "and..." seems to have no connection to earlier thread content. > The problem is that a corrupted heap or a dangling pointer can easily break > local invariants. When you see a broken local invariant, it is not safe to > assume that it is caused by a local error that will go away when the stack > is unwound. Correct but hardly relevant: nothing is 100% safe to assume at any time, and generalizing from "can theoretically be hit in the head by a meteorite" to "should not ever be outdoors" isn't what I'd call practical. Also, it's inconsistent with your position. It implies that nothing should be done. > Well, the good thing here is that a broken database invariant can't affect > the other program invariants because they don't share the same address > space, Sorry, but that's incorrect. Paraphrasing what you wrote just a sentence or two earlier, when you see a broken local invariant it's not guaranteed that it's caused by a local error, and depending on the kind of invariant and other context, it may in some cases be most likely caused by a non-local error. A broken database invariant can have been caused by a broken invariant elsewhere in the client program, and/or it can have caused broken invariants elsewhere in the client program, and/or it can come to cause such. > so it might be possible for the part of the program that doesn't need > access to the database to function normally. Correct but hardly relevant: that's possible anyway. -- A: Because it messes up the order in which people normally read text. Q: Why is it such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail? [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: David Abrahams on 15 Jul 2005 12:10 Gerhard Menzl <gerhard.menzl(a)hotmail.com> writes: > David Abrahams wrote: > > >> You seem to assume that aborting is the only alternative to unwinding >> when a violated invariant is detected. In an interactive application >> like a word processor it's usually possible to recover the state of >> the document and offer the user an opportunity to save when a violated >> invariant is detected. All of that can be done without any >> unwinding. > > No, I was referring to an earlier posting of yours where you wrote: > >> If preconditions are broken, your program state is broken, by >> definition. Trying to recover is generally ill-advised. > > and > >> Stop execution so I can debug the program. Good! > > My understanding is that recovery without unwinding requires a separate, > handcrafted mechanism. Who said anything about recovery? I am talking about stopping execution at the earliest possible moment when an invariant violation is detected. > Otherwise, how would you obtain the necessary context that is > normally not accessible in a global, low-level function like an > assertion handler? I don't understand the question. > You argued (elsewhere) that the Undo feature in a word processor > provides such a mechanism (or a substantial building block of one) > anyway. Well, what about applications that don't have an Undo feature? I guess you'd better write one ;-) Imagine you wrote a word processor without Undo. You wouldn't really expect stack unwinding to automatically undo any edits in progress would you? If you have crucial program state that needs to be restored (and, e.g. written to a file) before exiting, you need a mechanism to handle it. >> In that case, a corrupted database table is by definition *not* a >> broken precondition. IIUC, you are expected to write software that is >> guaranteed to work whether the database table is corrupt or not, and >> you seem to accept that challenge. Great! It's similar to writing >> software that is robust in the face of invalid user input. If the >> user's input is invalid, well, there's some functionality they can't >> get to until the input is corrected. Nobody I know would consider >> valid user input a precondition in that case. > > I would not compare user input, which is always unpredictable, with the > state of a database. If, as in my case, a database table is accessed > exclusively by my application, which reads in data that have been > written by itself during earlier sessions, corrupt data are closer to a > broken invariant (or precondition) than to invalid user input. A precondition violation is -- by definition -- unrecoverable. http://en.wikipedia.org/wiki/Precondition If you want to use some other definition where it's a fuzzier notion, be my guest, but then we are going to be talking at cross-purposes. I also warn you that the term "precondition" loses almost all of its power when you do that. >> In your application, calling database table integrity a precondition >> is only going to confuse things and make your code more complicated. >> Once you understand that database integrity is not a precondition, it >> becomes very clear that you need to check for corruption in certain >> places and make sure that you do something sensible if you detect it. > > I think it depends on whether you regard the database as an integral > part of your system or something external. No. If your program is expected to be resilient against condition ~X, then X is not a precondition. Do yourself a favor and use the clear and accepted terminology. Your programs will benefit. -- Dave Abrahams Boost Consulting www.boost-consulting.com [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Alf P. Steinbach on 15 Jul 2005 21:00
* David Abrahams: > > A precondition violation is -- by definition -- unrecoverable. > http://en.wikipedia.org/wiki/Precondition > > If you want to use some other definition where it's a fuzzier notion, > be my guest, but then we are going to be talking at cross-purposes. I > also warn you that the term "precondition" loses almost all of its > power when you do that. Sorry, but that's incorrect. As a simple counter-example, write a Basic interpreter in C++, then when a precondition fails in the executing Basic interpreter, recover at the C++ level -- the C++ program can happily continue executing. Note that the existence of this _one_ counter-example invalidates your notion of absolute unrecoverability. As a more extreme example, you can turn off that computer, reboot, and restore all state from some restore point. You might and probably will argue that that's not recovery within the "program". And that's correct as far as it goes, but that argument is precisely the direction one might need to direct one's attention to understand this. Can you recover without rebooting the physical computer? Then you're within the OS "program" (and nowadays some folks prefer to run everything in a virtual OS just to be able to do this, it's not academic). Can you recover with some even less extreme measure? Then you might well be within an ordinary C++ simple application program. Can you recover without sacrificing a thread or two? Then you're even within the C++ standard's limited notion of program, and one example was given above. It's all about abstraction levels. Generalizing from a limited context and level up to "everything", as you did, is not valid. -- A: Because it messes up the order in which people normally read text. Q: Why is it such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail? [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |