Malcolm McLean wrote, On 24/09/08 10:42:
Depends what you mean by "defensive programming". Generally aborts,
whether caused by segfaults or assert fails or exits, are not considered
to be defensive. You could argue that exit should be treated differently
because a correct program may call exit(EXIT_FAILURE).
I stated above why in this case it *is* defensive. It defends against
the database being corrupted.
No, as soon as the program is incorrect you've lost control.
Not always.
You want to
tell a human that the program can no longer be guaranteed to produce
correct results. Usually the simplest way to do this is to terminate.
Without additional systems aborting the program does *not* notify a
human. The human is likely at best sat at a desk several yards away not
watching the kit.
The exception is when no back-up can be available, in which case
defensive programming is appropriate - suppress the error, and hope.
Trigger the alarm. Or with the night vision system for the plane have
some night vision goggles in the cockpit and a radar altitude warning
system.
In fact life support machines are alarmed.
Yes, and that alarm plus a human within hearing range is one of the
backup systems! One that can always outvote the main system!
It's difficult to provide a
guaranteed continuous power supply, for instance. Hardware components
fail, people pull out plugs.
I'm fully aware of that. This is why not all systems are software
systems. Some are procedures humans are required to follow, some are
humans auditing that humans are following the procedures. Some are
hardware (for instance the monitoring of the heart rate not being simply
the same software controlling the breathing).
That was tried - triplicate control systems with a majority of two vote
on error. You still need to suppress errors in the individual threads,
or soon you've got a duplicate system and no possibility of a majority
vote. It didn't work well, largely because the software tended to fail
under the same circumstances.
The alternate system in this case can be a separate oxygen tank with a
mechanical control that has enough oxygen to keep the astronaut alive
while being rescued. This is what scuba divers do, they have a small
emergency tank which is independent from the main system.
If you are sitting at the debugger testing the defensively-written
function, yes.
You don't need to be. Notice the references to log files above.
Consider this
void defensivewriteimage(void *rgb, int width, int height)
{
if(width <= 0 || height <= 0)
return;
writeimage(rgb, width, height);
}
Great. test the code and it works as specified. Then two years later
someone calls it with a negative width, and no image is saved. The
program continues, and the user wonders why no image appears on his
disk.
That is not hard to debug. Stick a breakpoint on the return and you
catch it immediately. Or, as I suggested above, put in logging and look
at the logs. Or if it is an interactive program add in the code to
report the problem to the user.
Sometimes that's what you want. However it its a program for
internal use, the bug will come out if you replace the defensive if()
with an assert().
It does not come out on the production version if you do that. On the
production version you overwrite your original version of the image file
with some garbage and have your customer even more annoyed than if it
had simply failed to save the changed image.
In any case, it is easy to debug with one breakpoint or the addition of
logging or other reporting on the defensive action.
With the defensive programming, it's a long bughunt.
No, it isn't. I've been there with more complex scenarios than that.
Before I added the defensive code it was hard, I added the defensive
code and it managed to work well enough for the user whilst *also*
giving the developer (not always me) the necessary information to know
*exactly* what to look for in the code.
As for writing to a log file, that's fine if you have only one program
with an error-logging system set up.
It's fine with lots of them as well.
But it renders our function
inherently unreusable.
Ah, so the contracts my company has for lots of money are to provide
software built on inherently unreusable libraries. Unreusable libraries
that are used in different software written and maintained by other
companies.
Again, sometime that doesn't matter, but often
you need modular components.
It is *easy* to provide logging mechanisms that don't break modularity.
t_logfunc libwhatever_registerlog(t_logfunc logfunc);
Only one of many ways.
Generally defensive programming is a bad idea.
Wrong. It aids in debugging (the defensive code provides good hooks if
done properly) and
You want to terminate on
bugs.
Not always.
Another possible safety related (or even critical) system. The night
vision system for an aircraft. There is a bug in the image processing
software. Do you want the imaging to stop and not be able to see where
you are going or do you want a slightly corrupted image?
Even a segfault is often preferable to wrong results.
Sometimes it is, sometimes it isn't. If aborting the program on a
particular type of detected problem is the best thing to do then
aborting it *with* an appropriate message is the action you take.
Oh, and for a daemon I have worked on one of the defensive actions is
tracking a segfault and trying to send an email if one occurs to allow
the administrator to deal with it before the users know there is a problem.