When to check the return value of malloc

R

Richard Heathfield

santosh said:
That's what assert is for.

No. Assertions are for something that cannot happen if the program is
correctly written (but may well happen if it is not!). They are not for
detecting low-probability runtime events.

On another note - whilst TTBOMKAB there are no recorded instances of a
monkey typing out the works of Shakespeare, there is at least one recorded
instance of a primate /writing/ out the works of Shakespeare!
Low-probability events happen with remarkable regularity - or, to put it
another way, million-to-one chances come off nine times out of ten.
 
C

CBFalconer

Richard said:
santosh said:

No. Assertions are for something that cannot happen if the program
is correctly written (but may well happen if it is not!). They are
not for detecting low-probability runtime events.

And also for detecting hardware failures, which do not require
program writing failures. Considering the number of machines
without ECC memory protection, this is an impressive number.
 
R

Randy Howard

And also for detecting hardware failures, which do not require
program writing failures. Considering the number of machines
without ECC memory protection, this is an impressive number.

Also realize that machines with ECC memory can also corrupt memory,
just not nearly as often.
 
R

Richard Heathfield

CBFalconer said:
And also for detecting hardware failures, which do not require
program writing failures.

I disagree. If a programmer is sufficiently prescient to predict a hardware
failure using an assertion, he can do so just as well using an ordinary
if(), and can thus fail the program elegantly instead of having it crash
out with possible data loss.
 
I

Ian Collins

Richard said:
CBFalconer said:


I disagree. If a programmer is sufficiently prescient to predict a hardware
failure using an assertion, he can do so just as well using an ordinary
if(), and can thus fail the program elegantly instead of having it crash
out with possible data loss.
The result is the same, if assert is defined to fail the program
elegantly and only used for conditions that "can't happen", then it's as
good a way to trap errors as any.
 
R

Richard Heathfield

Ian Collins said:
The result is the same,

No, sir. A program that detects a hardware error through an assertion
failure will either abend immediately or ignore the error completely! A
program that detects a hardware error through normal channels, as it were,
will be in a position to save out data if necessary, before halting
elegantly. When an artic driver sees a "bridge out" sign on a narrow
(can't-turn-round) road, he doesn't smash through the sign and leap for
safety just before his vehicle flies into the canyon. He parks up well in
advance, and gets busy on his mobile phone. Save Your Data.
if assert is defined to fail the program
elegantly and only used for conditions that "can't happen",

How you use it is up to you. I use it only for conditions that "can't
happen if the program is correct".
then it's as good a way to trap errors as any.

No, because it offers no opportunity for graceful (failsafe) degradation,
let alone recovery.
 
A

Antoninus Twink

CBFalconer said:


I disagree. If a programmer is sufficiently prescient to predict a hardware
failure using an assertion, he can do so just as well using an ordinary
if(), and can thus fail the program elegantly instead of having it crash
out with possible data loss.

Another classic claim by HeathField! That really is right up there
alongside "strncpy isn't a string function" and "C has no global
variables"...

Of course! "Good" programmers like HeathField (who already don't make
any mistakes, and don't write code with bugs, as they often delight in
telling Jacob when he argues for optional bounds-checking or something),
they can fail the program elegantly even if there's a hardware fault!
Your computer was obliterated in a nuclear bombblast - but don't worry!
HeathField's program will manage to clean up gracefully and avoid
possible data loss.

I know HeathField may be a god in clc, but in the real world the small
matter of physical reality might just be there to constrain his "good
programming"...
 
I

Ian Collins

Richard said:
Ian Collins said:


How you use it is up to you. I use it only for conditions that "can't
happen if the program is correct".
The program has to assume the foundations on which it is built are well
behaved.
No, because it offers no opportunity for graceful (failsafe) degradation,
let alone recovery.
It the hardware providing the program's date becomes unreliable, all
opportunities for graceful degradation have passed.
 
K

Kelsey Bjarnason

You can have something that will never happen, yet could happen.

By definition, you cannot. If it will never happen, it will never
happen.

So again: the entire reason for the existence of xmalloc is to handle a
situation which you assert never happens. Thus either you're simply full
of crap in your assertion the situation will never happen, or your
function is completely pointless, has no reason for existing.

So which is it?
 
K

Kelsey Bjarnason

[snips]

Sure. Once or twice in your program you may have a legitimate memory
request that could exceed the range of an int.

Why once or twice?

On 16-bit systems, which I was coding on pretty extensively for a while,
not too long back - allocation requests exceeding the range of an int
were, for much of what I did, the *norm*, not the exception.

Today, I don't _often_ allocate buffers large enough to exceed the size
of an int, but it's far from unheard of.
In that case you, the
programmer, should be aware that you are asking for an enormous amount
of memory.

Err, obviously.
Unless you totally control the system, you should also be
aware that it is rather likely that the request won't be honoured.

Depends on the system, the app, too many things.
The test if(ptr == NULL) is trivial. What's non-trivial is what goes in
the if condition

"ptr == NULL" doesn't seem terribly non-trivial.
, You might want to put up a box for the user saying
"image too large". In which case your windowing system had better be in
scope.

No, I don't. I want a response back from my allocation function that
tells me it couldn't honour the allocation, that I can do any of a
million possible things with it.

In one application, for example, it will continue to work, simply a
little less efficiently, with a smaller buffer. I attempt to allocate a
buffer of one size, for maximal efficiency, then reduce the buffer size
requested until it works, or a complete memory exhaustion is discovered.
In the latter case, fine, error out, gracefully. In the other cases,
keep right on going. The user need not even be aware of it; if there's
insufficient memory available for a full buffer, it's likely he has many
other things running as well, meaning the slowdown is likely to be more
affected by other apps accessing resources such as the HD than whatever I
lose in buffer size.
If the allocation is most unlikely to fail, why go through all this,

Because unlike you, we're all quite aware that allocations fail regularly
in the real world, thus we prefer to write code that deals with those
issues, rather than pretending they don't happen.

Perhaps more to the point, we note that your xmalloc function, despite
being broken *and* lying to the user, *also* makes the very same
assumption: that the allocation can fail. Thus even you don't buy the
crap about allocation failure being so unlikely.

Your function exists to handle a situation which you merrily assert
doesn't happen, can't happen, or - depending on which story you're
selling today - rarely happens. If it doesn't happen, your code is
pointless. If it does happen, even rarely, the best solution is to
return a value to the caller, that they can decide what to do about it.

There is no case, ever, in which using xmalloc can be justified, IMO,
even if it fixed the inherent problems and didn't lie to the user. Error
returns exist for a reason; they need to be handled, not ignored. You
don't actually handle them, though, you just wait for them to go away,
something which could well end up being a "wait forever", where a
sensibly written app would not end up waiting forever, it would cope with
the error and move on.
 
K

Kelsey Bjarnason

What you are describing is a classic case of over-engineering.

No, it's a case of engineering the application to do what it *should* do,
and engineering it *well* to do that job.

Overengineering is what you get when the designs are significantly beyond
the requirements, like writing a calculator app that handles everything
from bignums to integration, when what was asked for was a simple 4-
function calculator for kitchen-scope math.
Yes, your
program will be better, but you have to write lines and lines of custom
code to handle the failure of every single allocation.

No, I don't. I have to write one relatively simple loop which does the
job for me, consisting of a grand total of about ten lines of code.

Remember that
means cleaning up, unwinding the program state, destroying every object,
until you come to a point where you can recover.

No, it doesn't. I'm coming to the conclusion you have no idea how to
engineer an application, as what you're saying is complete and utter
bollocks.
If the memory request
was trivial, most of the time the program will fail to get memory again
on recovery, and will eventually close down or be closed down anyway.

What "most of the time" are you talking about here? In an MP system such
as Windows or Linux, an allocation that fails now may work ten seconds
from now, meanwhile the app may be able to do other useful things.

In other cases - such as the one I mentioned - an allocation failure is
not only not a fatal condition, it is an expected and trivial condition;
the app asks for as much memory as it can use for maximal efficiency, but
this has no bearing on how much memory it requires _to work_.

As a simple example, let's ponder a function which parses data from a
file, where the expectation is that the typical file so parsed is
relatively large - say several gigabytes.

Option 1: run through it, character by character, using fgetc() or some
equivalent. This was actually tested, and performance sucked.

Option 2: run through it line by line, using fgets or some equivalent.
This was also tested, works better, but not as well as it should.

Option 3: allocate a buffer of size N, read N bytes' worth of data,
process in memory. This, based on testing, is the most efficient, with
efficiency increasing, on average, with increasing values of N, to a
point, say around 64MB, where the benefits of more memory are
insufficient to justify increased usage.

If I'm building such an app (I did), I'm going to want to allocate 64MB.

However...

The app will continue to work, just less efficiently, with considerably
less than 64MB allocated. It *needs* a minimum of 1-2MB, which means
that 2MB would be the "fail point", not 64MB.

Any allocation system which allows me to ask for the full 64MB *but
doesn't allow me to detect the failure and adjust the requested size* is
completely useless to me for such a case.

And that's exactly what xmalloc does: it allows me to request the 64MB
I'd like, but prevents me from detecting the failure and adjusting the
request.

You complain that malloc causes problems. Even assuming you had a point,
your solution is worse than the problem. At least with malloc, *I* can
adjust to conditions. *I* can choose to use a smaller buffer. *I* can
detect the error and decide that perhaps I should try another task, then
check back later to see if memory is available. With your code, no such
options are available.

Granted, with your "user function" or whatever you call it, I can, in
principle, say "try again, with a smaller amount of memory", but that
doesn't help, as I cannot tell the calling code that the change has
occurred; it asked for 64MB, what was *actually* allocated was 16MB, how
the hell does it know that? It doesn't - there's no way to respond back
that the request was not honoured.
But what if the memory requests are so small that in fact the chance of
the computer breaking is greater than the chance of malloc() failure?

And what if the sun was covered in bright green puppy dogs? Such
questions make no sense.

Here's some reality for you: multitasking systems, particularly multi-
user systems, do not generally have fixed memory consumption. My word
processor requires 2MB now, but 20MB five minutes from now, as I open a
larger document. In the background, logrotate fires, calling a
compression program to archive the old logs. A scheduled app - leafnode,
say - fires, sucking up memory to get its work done.

It is hardly impossible to exhaust available memory, even in a
comparatively lightly-loaded system - and my boxes, as a rule, are
anything *but* lightly-loaded.

The notion an allocation failure is so unlikely it won't happen before
the hardware fails is simply so blindingly naive I can't believe you
actually think that makes a sensible argument - which in turn supports my
earlier contention, that you have no idea how to engineer an application.
If
you've a backup computer, that runs a mirror of the program, sure, the
application must never terminate. It's running a nuclear power station
or something. But if you don't provide such a hardware mirror, can you
really say "the application must never terminate?"

Who said "the application must never terminate"? You did. I said that
unmanaged, ungraceful termination is a bad idea. An even worse idea is
to build code in such a manner as to *force* such failures. A truly,
stupendously idiotic idea is to build code such that your only available
option is to terminate ungracefully, rather than do so gracefully, or
choose to perform another task, or whatever else the developer sees as a
reasonable course of action.

By your thinking, we could drastically simplify the entire C standard
library.

Take, oh, fopen. Use your design: you either get back a properly-opened
file, or the app dies. No option whatsoever to respond back to the
calling code, perhaps to log that the file didn't exist, or couldn't be
opened, so carry on with the next one.

Or how about, oh, fwrite. Can't write the data? No problem, after all,
it's not like the data matters or anything, so we'll just try the write
and if it works, we'll say so and if it fails, we'll abort.

How about fread? You wanted 512 bytes, but there were only 300 in the
file? That could be considered an error, so let's not return an error
result and handle it, let's pop up a window saying "Please add more data
to the file" and wait for the user to do so, or abort the application.

Error returns, count returns, these things exist for a reason, and the
reason is *not* so we can simply disregard them, it is so that we can
*detect* them and *deal with* them.

Your entire approach is so fundamentally flawed from a software design
perspective, never mind a coding perspective, that it simply is not of
any use at all, not in a toy program, not in a real program. Maybe it
keeps you happy at night, I don't know, I don't care; it - the entire
methodology, never mind the specific code - will never make it into any
application I have the fortune to be working on, and if I ever do
encounter it, it will be summarily removed, with extreme prejudice.

Frankly, if I saw that sort of thing in anything claiming to be real
code, I'd take steps to have the programmer fired; he's simply not
competent to be working on anything more complex than "Hello, world".

I'm not trying to insult you, Malcolm, I'm trying to point out something
very basic here. Your design is to, in essence, pretend that such
failures don't exist, when the simple reality is that they do. Hell, the
code for xmalloc _itself_ admits they do, else it wouldn't need to
validate the call to malloc. In fact, the very *existence* of xmalloc
says such failures occur; if they didn't, you wouldn't want xmalloc,
you'd just use malloc and not bother to check for NULLs.

Software designed with a rose-coloured view may make life easier for the
developer initially, as he doesn't have to write the error handling code,
but when it's faced with a big bad world where such things happen, not
just rarely but regularly, the code written in such a blinkered manner
suddenly needs a major overhaul, because error handling needs to be
hacked in after the fact, instead of designed in up front.

Using functions such as xmalloc, the way it works, isn't just dangerous
to our apps - it can't be, as there's simply no way most of us would ever
let such blinkered notions into our code in the first place - it is
dangerous to *your* apps as well, as it means you're not designing proper
error handling in, and when you finally discover you need it, you haven't
got the mechanisms in place to deal with it.

If you actually have something to replace malloc which *actually improves
the resultant application* then by all means, bring it on. xmalloc isn't
it. It's designed from an inappropriate view of the world, it lies to
the user and it doesn't even meet the lies it tells, so it's not a viable
solution.
 
R

Randy Howard

The program has to assume the foundations on which it is built are well
behaved.

It the hardware providing the program's date becomes unreliable, all
opportunities for graceful degradation have passed.

Not true. In many cases, you could alert the user that the system
clock has become unreliable, and ask if they want to abort, save the
data at that point anyway into a new file, continue, etc.
 
I

Ian Collins

Randy said:
Not true. In many cases, you could alert the user that the system
clock has become unreliable, and ask if they want to abort, save the
data at that point anyway into a new file, continue, etc.
Typo on my part, that should have been "If the hardware providing the
program's data becomes unreliable"
 
M

Malcolm McLean

Kelsey Bjarnason said:
No, I don't. I want a response back from my allocation function that
tells me it couldn't honour the allocation, that I can do any of a
million possible things with it.

In one application, for example, it will continue to work, simply a
little less efficiently, with a smaller buffer.
What you are describing is a classic case of over-engineering. Yes, your
program will be better, but you have to write lines and lines of custom code
to handle the failure of every single allocation. Remember that means
cleaning up, unwinding the program state, destroying every object, until you
come to a point where you can recover. If the memory request was trivial,
most of the time the program will fail to get memory again on recovery, and
will eventually close down or be closed down anyway. But not always.
Occasionally the strategy works, and it is the strategy you must use if the
application must never terminate.
But what if the memory requests are so small that in fact the chance of the
computer breaking is greater than the chance of malloc() failure? If you've
a backup computer, that runs a mirror of the program, sure, the application
must never terminate. It's running a nuclear power station or something. But
if you don't provide such a hardware mirror, can you really say "the
application must never terminate?" Are you over-engineering again? I suggest
you are.
 
I

Ian Collins

Kelsey said:
As a simple example, let's ponder a function which parses data from a
file, where the expectation is that the typical file so parsed is
relatively large - say several gigabytes.

Option 1: run through it, character by character, using fgetc() or some
equivalent. This was actually tested, and performance sucked.

Option 2: run through it line by line, using fgets or some equivalent.
This was also tested, works better, but not as well as it should.

Option 3: allocate a buffer of size N, read N bytes' worth of data,
process in memory. This, based on testing, is the most efficient, with
efficiency increasing, on average, with increasing values of N, to a
point, say around 64MB, where the benefits of more memory are
insufficient to justify increased usage.
You forgot option 4, use the operating system's file mapping API and let
the OS take care of the memory allocations.
 
S

santosh

Kelsey Bjarnason wrote:

And that's exactly what xmalloc does: it allows me to request the 64MB
I'd like, but prevents me from detecting the failure and adjusting the
request.

You complain that malloc causes problems. Even assuming you had a
point, your solution is worse than the problem. At least with malloc,
*I* can adjust to conditions. *I* can choose to use a smaller buffer.
*I* can detect the error and decide that perhaps I should try another
task, then check back later to see if memory is available. With your
code, no such options are available.

Granted, with your "user function" or whatever you call it, I can, in
principle, say "try again, with a smaller amount of memory", but that
doesn't help, as I cannot tell the calling code that the change has
occurred; it asked for 64MB, what was *actually* allocated was 16MB,
how the hell does it know that? It doesn't - there's no way to
respond back that the request was not honoured.

You can set a global variable from the call-back to tell the higher
level code the amount of memory that was actually allocated. Ugly, but
doable.

<snip>
 
C

CBFalconer

Randy said:
Also realize that machines with ECC memory can also corrupt memory,
just not nearly as often.

Of course. However, the reliability is increased by something like
a factor of 1000 or more. I seem to remember quotes of 100,000
thirty years ago.
 
C

CBFalconer

Richard said:
CBFalconer said:

I disagree. If a programmer is sufficiently prescient to predict a
hardware failure using an assertion, he can do so just as well using
an ordinary if(), and can thus fail the program elegantly instead of
having it crash out with possible data loss.

But there is no need for prescience. If a condition exists,
something is bollixed. The evil bollixer can be hard or soft (or
both).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,811
Messages
2,569,693
Members
45,476
Latest member
CoreyOddo

Latest Threads

Top