5 ms·
There are 3 separate things that each require their different approach. - errors i.e bugs made by programmer - logical "error" conditions that the program is
by ensiferum 6y ago
There are 3 separate things that each require their different approach.
- errors i.e bugs made by programmer
- logical "error" conditions that the program is expected to handle for example network connection failed or user input failed
- unexpected error conditions that typically boild down to resource allocation errors, socket could not be allocated or memory allocation failed etc.
In my experience all of these are best handled with a different tool.
For bugs use an assert that dumps the core and leaves a clear stack trace.
For conditions that the program needs to handle use error codes.
And finally for the truly unexpected case use exceptions .
- tsimionescu 6y agoWhy dump core when you can log the bug and continue? Sure, in development we want things to fail fast and loud, but when deployed with a customer, I don't want my whole program to crash because there is one obscure code path that has a problem. And even for conditions that the program is expected to handle, 99.9% of the time all it can do is notify the user and ask for guidance, which means that the error must be bubbled up from a networking or storage layer all the way to the presentation layer - a perfect task for exceptions or something like an error monad. The only problem with exceptions or error monads is that they get tricky in the presence of resources that need to be released, and even that is well handled with patterns like RAII.
- dllthomas 6y ago> I don't want my whole program to crash because there is one obscure code path that has a problem. If that one obscure code path corrupted my state, I want to limit the incorrect actions that the software takes based on that state. This "want" of mine is to be balanced with all the other things I want out of the program, and the relative weights will vary by context... but it is often the case that continuing erroneously risks more harm than simply falling over.
- tsimionescu 6y agoThat is indeed a very common need in a non-memory-safe language. Still, not all bugs have unbounded impact. As long as memory is not corrupted, thing like off by one errors and null pointer exceptions can often be safely recovered from by simply reverting the operation that hit the error until some kind of safe point (such as the last user interaction, or the last thread start). Edit: spelling.
- ensiferum 6y agoSo you're suggesting basically writing by logic to deal with bugs i.e. For cases when the program has failed its own logical constraints? For me a bug is like a division by zero. The program violates its own logic and the only logical conclusion is the termination. I find the fixing is often much simpler and faster to have a loud but bang than some obscure unwanted incorrect result.
- tsimionescu 6y agoWell, let me ask you this: why not stop the computer when a program encounters a bug? Why not the whole cluster? Theoretically, an attempt to write to a null pointer could happen because you have corrupted a database or file and the entire system is now in an unreliable state. The answer is that just as a process has some degree of isolation from other processes on the same system, and from the kernel, similarly components of a process can be well enough isolated that only the specific component that encountered the bug needs to be stopped. This is never 100% safe, but how safe it is depends greatly on the technology and architecture.
- david422 6y agoIt often depends on what software you are writing. Writing a web service? Sure, you can kill the process. You only affect that 1 request and the user can retry and other users are unaffected. Writing a server that maintains state for multiple realtime clients? Or a user facing program? If an error can be safely constrained, it can be preferable to log the error but keep the application running.
- Animats 6y ago
- ensiferum 6y agoBecause correctness is important to me. I don't want my programs to silently go about in a buggy state producing incorrect results in a corrupted state.
- tsimionescu 6y agoNot all bugs put the whole program in a corrupted state. Especially if your prigram is written to be mostly stateless. A common pattern is that a state change is tried, it fails because of a bug or some other error, and it is reverted, and an error shown to a user. I would call this a robust program. Of course, not any error can be recovered this way, but it is very often possible (assuming we are taking about memory-safe languages; otherwise, the balance of probabilities is entirely the other way around).
- ensiferum 6y agoIn practice you always have mutated state in a complex system and not everything moves in transactional steps. People seem scared to dump core but I find that doing it makes my programs much robust and also simpler. I have no muddled logic to "deal with" bugs in the program itself. They simply will abort the program.
- tsimionescu 6y agoI replied the same in another thread, but why not dump the core of all the processes on the system, instead of just the one that encountered the error? And why stop on this system, and not dump core on other systems that were communicating with this one over a network? The process is one boundary of isolation, and you are making a bet that the corruption has not crossed this boundry. You can take the same bet with subcomponents of the process, just as in larger systems style may actually prefer to reboot the whole machine or even kill it and spin up another. This all depends on the architecture and technology you are working with. If a user has input some bad data that I didn't think to validate (user inputs 11 in a page number field, when there are 5 pages in total), an operation is initiated on that data (user presses the 'go' button), and that operation is known to be stateless, when it encounters an error (ArrayIndexOutOfBounds), we can safely abort the operation, log the stack trace, and signal an 'internal server error' to the user without having to kill the whole process. Not to mention, in a program with many non-transactional state changes, dumping core could be a source of persistent corruption in itself, if another thread was doing something as simple as, for example, writing a JSON file.
- wvenable 6y ago> Why dump core when you can log the bug and continue? I see from your replies what you're trying to say. If an error occurs, most likely you want the entire operation to abort -- that doesn't necessarily mean the whole program depending on the program. For example, if I have a GUI app and the "save" operation fails and I typically roll that back right to the event loop of the application and the user gets an error and they can retry the save. For other types of applications, killing the whole process is ending the operation.
- tsimionescu 6y agoYes, exactly! And on the other end of spectrum, there are even systems where it makes sense to go further than killing a single process, and kill the whole container or even VM where a buggy condition was encountered.
- identity0 6y agoFor the third case it’s better to just abort. Tell the user to get more RAM or something. What are you supposed to do when you’re out of memory? Catch the exception? Then what? Related, I always find it funny when C programmers write `if (malloced == NULL) return NULL;` Either you’re going to forget that this can happen and dereference null (in which case it’s just better to abort the program immediately) or the caller will check this and then close the program. If it doesn’t, the next malloc will be null anyways, and the problem repeats. Just call abort().
- ensiferum 6y agoWell memory failure checking is usually put in the "can't do shit" category which isn't necessarily true. Both in C and in C++ bad_alloc or null from mallon indicate that the memory manager could not find the memory. This may or may not mean that your application has overcommitted memory in the OS level. Completely depends on the actual memory manager. So therefore the failure to me is just a general resource allocation failure. Would you dump core of your program failed to allocate a socket ? Or mutex?