By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
434,880 Members | 2,443 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 434,880 IT Pros & Developers. It's quick & easy.

Cleanup patterns

P: n/a
MQ
Hi all

I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful). C++ has exceptions,
the only way I can see to do this neatly in C is to use goto
statements. Is my method of implementing cleanup good, or are their
better ways. Here is an example, which uses the global errno to store
the error.

#define CLEANUP(err) ({errno = (err); goto cleanup})

int example_function()
{
SOMETYPE * a,b,c;
errno = 0;

if(!(a = malloc(sizeof(a))))
CLEANUP(ENOMEM);

if(!(b = malloc(sizeof(b))))
CLEANUP(ENOMEM);

if(!(c = malloc(sizeof(c))))
CLEANUP(ENOMEM);

/* do something here */

cleanup:
if(a)
free(a);
if(b);
free(b);
if(c)
free(c);
if(errno)
return -1;
return 0;
}

Nov 29 '06 #1
Share this Question
Share on Google+
69 Replies


P: n/a

MQ wrote:
Hi all

I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful). C++ has exceptions,
the only way I can see to do this neatly in C is to use goto
statements. Is my method of implementing cleanup good, or are their
better ways. Here is an example, which uses the global errno to store
the error.

#define CLEANUP(err) ({errno = (err); goto cleanup})
Don't use macros with jumps and what not.
int example_function()
{
SOMETYPE * a,b,c;
b and c are not pointers [or at least not at the same level as a]
errno = 0;

if(!(a = malloc(sizeof(a))))
CLEANUP(ENOMEM);

if(!(b = malloc(sizeof(b))))
CLEANUP(ENOMEM);

if(!(c = malloc(sizeof(c))))
CLEANUP(ENOMEM);
I'd do simply

a = malloc(sizeof(*a));
if (a == NULL) { errno = outofmem; goto ERR_A; }
b = ...
if (b == NULL) { errno = outofmem; goto ERR_B; }
....

Then at the end have

errno = 0;

ERR_C:
free(b);
ERR_B:
free(a);
ERR_A:
return errno;
}

Makes the code easy to follow and doesn't involve nasty if's all over
the place (e.g. if you avoided goto all together..)

Tom

Nov 29 '06 #2

P: n/a
MQ wrote:
Hi all

I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful). C++ has exceptions,
the only way I can see to do this neatly in C is to use goto
statements.
In C, it's typical to use nested if's, and/or carefully
initialized variables, and/or decomposition into multiple
functions, and/or goto.
Is my method of implementing cleanup good, or are their
better ways. Here is an example, which uses the global errno to store
the error.
Your method is Bad(tm). For starters, it won't compile.
That could be fixed by changing the macro definition, but then
you'd be invoking undefined behavior if either of the first two
malloc() calls failed. Also, Standard C doesn't define ENOMEM,
and `errno!=0' is not a valid test for failure.

All of these problems are easily fixed or worked around, but
consider: You've advanced CLEANUP as, essentially, a way to help
write better programs. Since it demonstrably has *not* helped
you to write a better program, perhaps it's time to question its
effectiveness, no?
#define CLEANUP(err) ({errno = (err); goto cleanup})

int example_function()
{
SOMETYPE * a,b,c;
errno = 0;

if(!(a = malloc(sizeof(a))))
CLEANUP(ENOMEM);

if(!(b = malloc(sizeof(b))))
CLEANUP(ENOMEM);

if(!(c = malloc(sizeof(c))))
CLEANUP(ENOMEM);

/* do something here */

cleanup:
if(a)
free(a);
if(b);
free(b);
if(c)
free(c);
if(errno)
return -1;
return 0;
}
--
Eric Sosman
es*****@acm-dot-org.invalid

Nov 29 '06 #3

P: n/a
MQ

Eric Sosman wrote:
MQ wrote:
Hi all

I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful). C++ has exceptions,
the only way I can see to do this neatly in C is to use goto
statements.

In C, it's typical to use nested if's, and/or carefully
initialized variables, and/or decomposition into multiple
functions, and/or goto.
Is my method of implementing cleanup good, or are their
better ways. Here is an example, which uses the global errno to store
the error.

Your method is Bad(tm). For starters, it won't compile.
That could be fixed by changing the macro definition, but then
you'd be invoking undefined behavior if either of the first two
malloc() calls failed. Also, Standard C doesn't define ENOMEM,
and `errno!=0' is not a valid test for failure.
Hi Eric

I did throw this together rather quickly and no doubt there will be a
number of errors, but after posting I fixed these problems and it does
compile fine. Ignoring this, I just want to know if the general
structure of the code is good practise. You should be able to
ascertain what I am trying to achieve...
MQ.

Nov 29 '06 #4

P: n/a
MQ wrote:
Eric Sosman wrote:
>>MQ wrote:

>>>Hi all

I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful). C++ has exceptions,
the only way I can see to do this neatly in C is to use goto
statements.

In C, it's typical to use nested if's, and/or carefully
initialized variables, and/or decomposition into multiple
functions, and/or goto.

>>>Is my method of implementing cleanup good, or are their
better ways. Here is an example, which uses the global errno to store
the error.

Your method is Bad(tm). For starters, it won't compile.
That could be fixed by changing the macro definition, but then
you'd be invoking undefined behavior if either of the first two
malloc() calls failed. Also, Standard C doesn't define ENOMEM,
and `errno!=0' is not a valid test for failure.


Hi Eric

I did throw this together rather quickly and no doubt there will be a
number of errors, but after posting I fixed these problems and it does
compile fine. Ignoring this, I just want to know if the general
structure of the code is good practise. You should be able to
ascertain what I am trying to achieve...
It appears you are trying to achieve what I described as
typical: "nested if's, and/or carefully initialized variables,
and/or decomposition into multiple functions, and/or goto."
My original question remains: Given that the attempt led you
into four different errors (and perhaps more in the "fixed"
version we haven't seen yet), do you still think you're going
about things in the best way possible? Note that "it does
compile fine" and "it is correct" are not the same thing!

--
Eric Sosman
es*****@acm-dot-org.invalid

Nov 29 '06 #5

P: n/a
MQ wrote:
>
I am just wondering how most people implement cleanup in C
functions. In particular, if the function opens a number of
resources, these need to be released properly should an error
occur at any point in the function (as well as at the end if
successful). C++ has exceptions, the only way I can see to do
this neatly in C is to use goto statements. Is my method of
implementing cleanup good, or are their better ways. Here is an
example, which uses the global errno to store the error.

#define CLEANUP(err) ({errno = (err); goto cleanup})

int example_function()
{
SOMETYPE * a,b,c;
errno = 0;

if(!(a = malloc(sizeof(a))))
CLEANUP(ENOMEM);

if(!(b = malloc(sizeof(b))))
CLEANUP(ENOMEM);

if(!(c = malloc(sizeof(c))))
CLEANUP(ENOMEM);

/* do something here */

cleanup:
if(a)
free(a);
if(b);
free(b);
if(c)
free(c);
if(errno)
return -1;
return 0;
}
I would implement that as:

int example(void)
{
SOMETYPE *a, *b, *c;

errno = 0;
a = b = c = NULL;
if (!(a = malloc(sizeof *a))) errno = ENOMEM;
else if (!(b = malloc(sizeof *b))) errno = ENOMEM;
else if (!(c = malloc(sizeof *c))) errno = ENOMEM
else {
/* dosomething here */
}
free(a); free(b); free(c);
return -(0 != errno);
}

Notice the absence of obfuscating macros. Also the routine no
longer frees undefined pointers or non-pointers. No goto needed,
although I do not hesitate to use them when appropriate. I would
normally choose different return values.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
Nov 29 '06 #6

P: n/a
MQ
It appears you are trying to achieve what I described as
typical: "nested if's, and/or carefully initialized variables,
and/or decomposition into multiple functions, and/or goto."
My original question remains: Given that the attempt led you
into four different errors (and perhaps more in the "fixed"
version we haven't seen yet), do you still think you're going
about things in the best way possible?
Umm, well, that was my question originally. I am looking for feedback.
And the question is more generic than the example above. I want to
know *generically* what are the best methods for implementing cleanup
in C functions. I can say for sure that *something* needs to be done,
as a function that holds potentially numerous resources cannot clean up
at the point of error, otherwise most of the code will be cleanup
stuff, and obscure the real task that the code is doing.

MQ.

Nov 29 '06 #7

P: n/a
MQ said:
Hi all

I am just wondering how most people implement cleanup in C functions.
I can't speak for most people, but I do it like this:

Attempt to acquire resource
If attempt succeeded
Use resource
Release resource
Else
Handle error
Endif

This code structure is recursively extensible, and functions are a fabulous
mechanism for keeping the indent level down.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Nov 29 '06 #8

P: n/a
MQ wrote:
Hi all

I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful). C++ has exceptions,
the only way I can see to do this neatly in C is to use goto
statements. Is my method of implementing cleanup good, or are their
better ways. Here is an example, which uses the global errno to store
the error.

#define CLEANUP(err) ({errno = (err); goto cleanup})

int example_function()
{
SOMETYPE * a,b,c;
errno = 0;

if(!(a = malloc(sizeof(a))))
CLEANUP(ENOMEM);

if(!(b = malloc(sizeof(b))))
CLEANUP(ENOMEM);

if(!(c = malloc(sizeof(c))))
CLEANUP(ENOMEM);

/* do something here */

cleanup:
if(a)
free(a);
if(b);
free(b);
if(c)
free(c);
if(errno)
return -1;
return 0;
}
This one I'd write (fixing a couple of things along the way) as:

int exampleFunction()
{
SomeType *a = malloc( sizeof (*a) );
SomeType *b = malloc( sizeof (*b) );
SomeType *c = malloc( sizeof (*c) );
int status = a && b && c ? 0 : -1;
if (status == 0)
{
/* do what must be done */
}
free( a ), free( b ), free( c );
return status;
}

I don't think your example is complex enough to demonstrate whatever
value your macro might have.

--
Chris "Magenta - the best colour of sound" Dollin
"No-one here is exactly what he appears." G'kar, /Babylon 5/

Nov 29 '06 #9

P: n/a
CBFalconer wrote:
MQ wrote:
>>I am just wondering how most people implement cleanup in C
functions. In particular, if the function opens a number of
resources, these need to be released properly should an error
occur at any point in the function (as well as at the end if
successful). C++ has exceptions, the only way I can see to do
this neatly in C is to use goto statements. Is my method of
implementing cleanup good, or are their better ways. Here is an
example, which uses the global errno to store the error.

#define CLEANUP(err) ({errno = (err); goto cleanup})

int example_function()
{
SOMETYPE * a,b,c;
errno = 0;

if(!(a = malloc(sizeof(a))))
CLEANUP(ENOMEM);

if(!(b = malloc(sizeof(b))))
CLEANUP(ENOMEM);

if(!(c = malloc(sizeof(c))))
CLEANUP(ENOMEM);

/* do something here */

cleanup:
if(a)
free(a);
if(b);
free(b);
if(c)
free(c);
if(errno)
return -1;
return 0;
}


I would implement that as:

int example(void)
{
SOMETYPE *a, *b, *c;

errno = 0;
a = b = c = NULL;
if (!(a = malloc(sizeof *a))) errno = ENOMEM;
else if (!(b = malloc(sizeof *b))) errno = ENOMEM;
else if (!(c = malloc(sizeof *c))) errno = ENOMEM
Missing a semicolon here, right after the possibly
undefined identifier ENOMEM.
else {
/* dosomething here */
}
free(a); free(b); free(c);
return -(0 != errno);
Note that free() is permitted to set errno to a non-zero
value. So might /* dosomething here */ if it were not a
comment.

errno is not a good holder for program state, because
it's "transient:" most library functions can change it at
will, whether or not any error has occurred. Park a value
in errno, and it may be "gone in sixty seconds."

--
Eric Sosman
es*****@acm-dot-org.invalid
Nov 29 '06 #10

P: n/a
MQ wrote:
Hi all

I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful). C++ has exceptions,
the only way I can see to do this neatly in C is to use goto
statements. Is my method of implementing cleanup good, or are their
better ways.
I went through several phases of this problem,
and ended up quite satisfied with the following
as a general scheme.

1. Define a filescope structure which can hold
all pointers, error indicators etcetera relevant
to the function.

2. In the function, declare an instance of the
structure; initialise it with NULLs; fill it up
as resource allocation proceeds; overwrite
fields in it with NULL after freeing any resource.

3. On a failure, exit with a statement such as:
return tidy_exit (this_error, alloc_struct);

It is a simple exercise to write the function
tidy_exit( ) so that it makes use of the data
in its second parameter as required, and
finally returns a copy of its first parameter.

There were some times when it seemed a bit
like overkill, but (a) it was arbitrarily extendable,
and (b) it kept the main function looking clean.
--

Nov 29 '06 #11

P: n/a
I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful). C++ has exceptions,
the only way I can see to do this neatly in C is to use goto
statements. Is my method of implementing cleanup good, or are their
better ways. Here is an example, which uses the global errno to store
the error.

#define CLEANUP(err) ({errno = (err); goto cleanup})

int example_function()
{
SOMETYPE * a,b,c;
errno = 0;

if(!(a = malloc(sizeof(a))))
CLEANUP(ENOMEM);

if(!(b = malloc(sizeof(b))))
CLEANUP(ENOMEM);

if(!(c = malloc(sizeof(c))))
CLEANUP(ENOMEM);

/* do something here */

cleanup:
if(a)
free(a);
if(b);
free(b);
if(c)
free(c);
if(errno)
return -1;
return 0;
}
I must be the only one who thinks this is good code. I once worked on
a project where we used a style like this and had orders of magnitude
fewer resource leaks than any other project our company had done using
if/then/else. (We were writing a server on Linux that had to process
tons of transactions and stay up for months, so even a small memory
leak would quickly add up and kill it.) The way we did it is a little
subtle, and requires programmer discipline. Basically, every function
that allocates resources must follow certain rules. The good thing is
that it's fairly easy to check whether a given function does, and if
not, to correct it.

Note: what follows is a rough idea, and may suffer the usual lack of
semicolons and stuff - details are left as an exercise to the reader.
In other words, treat the following as pseudo-code.

/* Rule 1: define a couple macros like this so you can use them like
statements
with semicolons, i.e., their syntax isn't too strange when you use
them (macro
is strange, though). We passed around an error message structure,
so our macros
were a little more complex than the version here with errno. */
#define GOTO_IF_BAD_ALLOC(mem, label) \
do {\
if (mem == NULL) { \
errno = ENOMEM; \
goto label;\
}\
while (0)

int example_function(SOMESTRUCT* s)
{
SOMETYPE *a;
SOMETYPE *b;
SOMETYPE *c;
RETURNTYPE r;

/* Rule 2: initialize everything to NULL, and the error to some
generic error condition */
a = NULL;
b = NULL;
c = NULL;
errno = UNDEFINED_ERROR;
/* Note: if the function ever returns UNDEFINED_ERROR, that
indicates a logic error
in the function. (Actual errors, as well as successful
completion, should set the
error code to a real value.) */

/* Rule 3: whenever you allocate, use the macro. */
a = malloc(sizeof(a));
GOTO_IF_BAD_ALLOC(a, cleanup);

b = malloc(sizeof(b));
GOTO_IF_BAD_ALLOC(b, cleanup);

/* Rule 3b: check all function call returns too, with an analogous
macro. */
r = call_some_function(a, b);
GOTO_IF_CALL_RETURNS_ERROR(r, cleanup);
/* Depending how you do it, this macro might need an extra
parameter with error
code, string message, or both. */

c = malloc(sizeof(c));
GOTO_IF_BAD_ALLOC(c, cleanup);
s->someField = c;
c = NULL; /* We no longer own it. */
/* Rule 4: when you pass ownership of a pointer, set it to NULL,
and add a comment
as above if there's even a question that it might be confusing
to some developer
in the future. */

/* do something here */

/* Rule 5: set error code to success right before the cleanup
routine. */
errno = SUCCESS;

/* Rule 6: have a global cleanup routine that all paths go through. */
cleanup:
/* Rule 7: check each owned pointer. If non-NULL, free it. */
if(a)
free(a);
if(b);
free(b);
if(c)
free(c);

/* Rule 8: return value depends on errno. (You could map to 0/1 or
something too,
as long as it depends on error code.) */
return errno;
}

This example shows the major rules, and looks fairly yucky until you
get used to it. However, it scales well if you have some complex
logic, nested ifs, etc. (e.g., if you only allocate c in some set of
circumstances). The basic idea is that every potentially unsafe action
(allocation, function call, etc.) is followed immediately by a macro
checking its return value and jumping to cleanup if there is an error.

Further, it's easy to check whether a given variable may be leaked.
You just check that all the rules are followed for that given variable.
By iterating over all variables this way, you get a robust function.

Like I said, it took a while (probably a week) to get used to this
coding style. But then we were golden, and spent very little time
debugging resource leaks.

Michael

Nov 29 '06 #12

P: n/a

Richard Heathfield <rj*@see.sig.invalidwrote in message
news:uf******************************@bt.com...
MQ said:

I am just wondering how most people implement cleanup in C functions.

I can't speak for most people, but I do it like this:

Attempt to acquire resource
If attempt succeeded
Use resource
Release resource
Else
Handle error
Endif

This code structure is recursively extensible, and functions are a
fabulous
mechanism for keeping the indent level down.
Well, no, except for all trivial examples that aren't speed-sensitive.

For more complex functions, you'll quite often have contigent
dependancies for each resource acquisition that don't fit neatly into
the "nest", and the overhead of the function calling mechanism
means that functions are not infinitely or always a "fabulous
mechanism" for something as silly as "keeping the indent
level down" (unless you like your "C" code to run as slow
as "Java"!).

For me, the answer is whatever works best for the particular
situation; I do tend to use a few basic "patterns" quite often (and
sorry, I do use "goto"s a lot for these purposes when warranted),
but I think the real mistake here in this thread is to find a
"one size fits all" solution.

---
William Ernest Reid

Nov 30 '06 #13

P: n/a
Bill Reid said:
>
Richard Heathfield <rj*@see.sig.invalidwrote in message
news:uf******************************@bt.com...
<snip>
>>
This code structure is recursively extensible, and functions are a
fabulous mechanism for keeping the indent level down.
Well, no, except for all trivial examples that aren't speed-sensitive.
Not so far, not for me anyway. If this method ever turns out to be too slow,
I'll call you. In the meantime, I'll keep the code clear and simple.

<snip>

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Nov 30 '06 #14

P: n/a
MQ

Bill Reid wrote:
For more complex functions, you'll quite often have contigent
dependancies for each resource acquisition that don't fit neatly into
the "nest", and the overhead of the function calling mechanism
means that functions are not infinitely or always a "fabulous
mechanism" for something as silly as "keeping the indent
level down" (unless you like your "C" code to run as slow
as "Java"!).
These are my greatest concerns, as I am writing file system driver code
that needs to be fast efficient.

Nov 30 '06 #15

P: n/a
MQ wrote:
Bill Reid wrote:
For more complex functions, you'll quite often have contigent
dependancies for each resource acquisition that don't fit neatly into
the "nest", and the overhead of the function calling mechanism
means that functions are not infinitely or always a "fabulous
mechanism" for something as silly as "keeping the indent
level down" (unless you like your "C" code to run as slow
as "Java"!).

These are my greatest concerns, as I am writing file system driver code
that needs to be fast efficient.
At the processor level both control flow statements like IF, WHILE
etc., as well as GOTOs, will use the same or same class of JUMP
instructions, so there's no reason, atleast from this point of view, to
gratituously favour GOTO over others like IF, WHILE, FOR etc.

For filesystem drivers, your choice of data structures and their
manipulating algorithms will far outweigh micro-optimisations like
GOTOs, with respect to performance. Also don't forget that hardware
bottlenecks are also likely to be more penalising.

Nov 30 '06 #16

P: n/a
MQ said:
>
Bill Reid wrote:
>For more complex functions, you'll quite often have contigent
dependancies for each resource acquisition that don't fit neatly into
the "nest", and the overhead of the function calling mechanism
means that functions are not infinitely or always a "fabulous
mechanism" for something as silly as "keeping the indent
level down" (unless you like your "C" code to run as slow
as "Java"!).

These are my greatest concerns, as I am writing file system driver code
that needs to be fast efficient.
Rule 1: if it doesn't work, it doesn't matter how fast it is.
Rule 2: it is easier to make a correct program fast than it is to make a
fast program correct.
Rule 3: it is easier to make a readable program correct than it is to make a
correct program readable.

And that's before we get anywhere near the Two Rules of Micro-Optimisation.
For completeness, these are:

Rule 1: Don't do it.
Rule 2 (for experts only): Don't do it yet.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Nov 30 '06 #17

P: n/a

Richard Heathfield <rj*@see.sig.invalidwrote in message
news:fr******************************@bt.com...
MQ said:
Bill Reid wrote:
For more complex functions, you'll quite often have contigent
dependancies for each resource acquisition that don't fit neatly into
the "nest", and the overhead of the function calling mechanism
means that functions are not infinitely or always a "fabulous
mechanism" for something as silly as "keeping the indent
level down" (unless you like your "C" code to run as slow
as "Java"!).
These are my greatest concerns, as I am writing file system driver code
that needs to be fast efficient.

Rule 1: if it doesn't work, it doesn't matter how fast it is.
Rule 2: it is easier to make a correct program fast than it is to make a
fast program correct.
Rule 3: it is easier to make a readable program correct than it is to make
a
correct program readable.
I think these are actually the three rules for pontificating about
programming instead of actually programming...years ago I read
the REAL rules for writing "correct" "fast" and "efficient" code, and
of course, as everybody who's not just pontificating knows,
it boils down to one general rule: pick the two you really want, because
a lot of times you CAN'T have all three...
And that's before we get anywhere near the Two Rules of
Micro-Optimisation.
For completeness, these are:

Rule 1: Don't do it.
Rule 2 (for experts only): Don't do it yet.
I certainly wasn't talking about anything like "micro-optimization".
I'm talking about great big gobs of "MACRO-optimization", like
slowing down or speeding up what was virtually the IDENTICAL
code by a factor of up to ten-fold (I could make the same program run
in either two seconds or 20 seconds)...

This is not exactly an academic exercise for me, since a lot of the
code I work with takes several HOURS to run on a Pentium-class
machine, and I have to run it EVERY day. If I were to ignore certain
WELL-KNOWN basic principles of writing "fast" code, there
wouldn't be enough hours in the day to run it, and the whole
program would become moot...

However, I will admit that for a lot of the "commercial" programs
out there, the actual differences may only amount to a second or
a few seconds, since they are actually only performing limited data
processing, and that my experience looking at a lot of "professional
programming" reveals that most "C" code is written with a multiplicity
of "atomic" function calls, despite the fact that overhead for those
calls slows the program down substantially...this is just one of
several "bad habits" that "C" programmers seem to fall into "naturally"
that tend to make a lot of "C" code slower (than it could be), less
"readable", more bug-prone, and less secure...

---
William Ernest Reid

Dec 8 '06 #18

P: n/a
MQ wrote:
I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful). C++ has exceptions,
the only way I can see to do this neatly in C is to use goto
statements.
Yes, I agree. This is the easiest way of doing it to support complete
generality.
[...] Is my method of implementing cleanup good, or are their
better ways. Here is an example, which uses the global errno to store
the error.

#define CLEANUP(err) ({errno = (err); goto cleanup})

int example_function()
{
SOMETYPE * a,b,c;
errno = 0;

if(!(a = malloc(sizeof(a))))
CLEANUP(ENOMEM);

if(!(b = malloc(sizeof(b))))
CLEANUP(ENOMEM);

if(!(c = malloc(sizeof(c))))
CLEANUP(ENOMEM);

/* do something here */

cleanup:
if(a)
free(a);
if(b);
free(b);
if(c)
free(c);
if(errno)
return -1;
return 0;
}
Well simple mallocs are an especially easy case. You just initialize
them all to NULL, allocate them all, then if any are NULL, just free
them all and return with some "out of memory" error.

The way you can do this in general is:

int retcode = RETURN_OK;
if (NULL == (handleA = resourceA (parms))) {
retcode = ERROCODE(RESA);
Exit1:;
return retcode;
}
if (NULL == (handleB = resourceB (parms))) {
retcode = ERROCODE(RESB);
Exit2:;
destroyresourceA (handleA);
goto Exit1;
}
if (NULL == (handleC = resourceC (parms))) {
retcode = ERROCODE(RESC);
Exit3:;
destroyresourceB (handleB);
goto Exit2;
}

/* Your intended inner loop here. */
/* If you fail fatally here, set retcode and goto Exit4 below. */

retcode = RETURN_OK;
Exit4:;
destroyresourceC (handleC);
goto Exit3;

Which assumes stack-like resource utilization. Its wordy, but its
scalable, and allows fairly straightforward modification (you can
insert, delete or switch the order of the resource utilization fairly
easily) and maintenance. Its also generalized enough to allow you to
fopen files for reading, and construct more complicated data
structures. It also supports and propogates the semantic of "if I
cannot function perfectly, then I will take no action at all and return
with an error code". I.e., you can use this to build ADTs, and to use
ADTs in the face of potential creation failures.

I don't see that there is much you can do to make this clean in C,
other than in especially trivial cases (like just straight flat
mallocs.) You could just store the return code, then jump to a common
switch() statement which would unwind the stack of resource
allocations, but you don't save very much in terms of clarity and you
end up with a code synchronization problem. The advantage of the above
is that the creation and destruction code are right next to each other.

--
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Dec 8 '06 #19

P: n/a
On 28 Nov 2006 18:32:42 -0800, "MQ" wrote:
>I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful).
Not all resources are equal and need equal treatment. It hardly makes
sense to check the return value of malloc. Just use a xmalloc function
which can be found in many variants on the internet (e.g.
http://www.tug.org/tex-archive/dviwa...2xx/xmalloc.c).

Best wishes,
Roland Pibinger
Dec 9 '06 #20

P: n/a
Roland Pibinger wrote:
On 28 Nov 2006 18:32:42 -0800, "MQ" wrote:
I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful).

Not all resources are equal and need equal treatment. It hardly makes
sense to check the return value of malloc. Just use a xmalloc function
which can be found in many variants on the internet (e.g.
http://www.tug.org/tex-archive/dviwa...2xx/xmalloc.c).
Well, it's so simple that most programmers will write their own
versions of it, if need be. The code seems to use the old style
function definition and uses unsigned instead of size_t, which isn't
the way it's done now. And calling exit() with a random value is not
portable. It's better to use EXIT_FAILURE.

In short that code is _old_. Better to rewrite it on case-by-case
basis, (particularly since it's trivial.)

Dec 9 '06 #21

P: n/a
Roland Pibinger said:

<snip>
It hardly makes sense to check the return value of malloc.
It certainly doesn't make sense not to!

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 9 '06 #22

P: n/a
rp*****@yahoo.com (Roland Pibinger) writes:
Not all resources are equal and need equal treatment. It hardly makes
sense to check the return value of malloc. Just use a xmalloc function
which can be found in many variants on the internet (e.g.
http://www.tug.org/tex-archive/dviwa...2xx/xmalloc.c).
Here's that implementation:

void *
xmalloc (size)
unsigned size;
{
void *new_mem = (void *) malloc (size);

if (new_mem == NULL)
{
fprintf (stderr, "fatal: memory exhausted (xmalloc of %u bytes).\n",
size);
/* 1 means success on VMS, so pick a random number (ASCII `K'). */
exit (75);
}

return new_mem;
}

This is buggy: calling xmalloc(0) on some systems will always
cause the program to exit with a claim that memory is exhausted,
which is not (necessarily) true.

Also, EXIT_FAILURE is preferred to "exit (75)", there is no need
to cast the return value of malloc(), and size_t would be
preferred to "unsigned" to measure the size of an object.
(However, it is likely that this is pre-Standard code.)
--
int main(void){char p[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv wxyz.\
\n",*q="kl BIcNBFr.NKEzjwCIxNJC";int i=sizeof p/2;char *strchr();int putchar(\
);while(*q){i+=strchr(p,*q++)-p;if(i>=(int)sizeof p)i-=sizeof p-1;putchar(p[i]\
);}return 0;}
Dec 9 '06 #23

P: n/a
Roland Pibinger wrote:
On 28 Nov 2006 18:32:42 -0800, "MQ" wrote:
I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful).

Not all resources are equal and need equal treatment. It hardly makes
sense to check the return value of malloc. Just use a xmalloc function
which can be found in many variants on the internet (e.g.
http://www.tug.org/tex-archive/dviwa...2xx/xmalloc.c).

Best wishes,
Roland Pibinger
Hmm... this may be okay for smaller programs, but would you really want
to just exit() in larger applications? And I think malloc() can fail
even though there still is (some) memory available, it might just be
too fragmented to allocate the requested size. There are also two other
"problems" with xmalloc(). First, it declares malloc() as being
extern char *malloc ();
instead of
void *malloc (size_t size);
Second, xmalloc() in this example uses unsigned for the size (instead
of size_t).
--
WYCIWYG - what you C is what you get

Dec 9 '06 #24

P: n/a
On Sat, 09 Dec 2006 16:42:14 +0000, Richard Heathfield wrote:
>Roland Pibinger said:
<snip>
>It hardly makes sense to check the return value of malloc.

It certainly doesn't make sense not to!
And what is your program supposed to do when memory is exhausted?
Dec 9 '06 #25

P: n/a
Roland Pibinger wrote:
Not all resources are equal and need equal treatment. It hardly makes
sense to check the return value of malloc. Just use a xmalloc function
which can be found in many variants on the internet (e.g.
http://www.tug.org/tex-archive/dviwa...2xx/xmalloc.c).
Good one.
I hope you don't write safety-critical software.
Dec 9 '06 #26

P: n/a
Roland Pibinger said:
On Sat, 09 Dec 2006 16:42:14 +0000, Richard Heathfield wrote:
>>Roland Pibinger said:
<snip>
>>It hardly makes sense to check the return value of malloc.

It certainly doesn't make sense not to!

And what is your program supposed to do when memory is exhausted?
This is documented in the literature, and has been discussed on this
newsgroup a number of times before. See, for example:

<3D***************@eton.powernet.co.uk>

or

<PM********************@bt.com>

where I bullet-point a few ideas.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 9 '06 #27

P: n/a
Roland Pibinger wrote:
On Sat, 09 Dec 2006 16:42:14 +0000, Richard Heathfield wrote:
>Roland Pibinger said:
><snip>
>>It hardly makes sense to check the return value of malloc.

It certainly doesn't make sense not to!

And what is your program supposed to do when memory is exhausted?
For example, it can save the data it has collected to a file, send
a message, and exit gracefully. Consider a big sort where the
input mechanism collects records in a linked list until memory is
exhauseted, mergesorts the list, dumps it to a temporary file,
discards the list, and repeats, dumping to the next temporary
file. When the input is exhausted it mergest the set of
temporaries into an output file.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>

Dec 9 '06 #28

P: n/a
On Sat, 09 Dec 2006 17:50:09 +0000, Richard Heathfield wrote:
>Roland Pibinger said:
>And what is your program supposed to do when memory is exhausted?

This is documented in the literature, and has been discussed on this
newsgroup a number of times before. See, for example:

<3D***************@eton.powernet.co.uk>
Those are mostly hints how to reduce the dynamic memory consumption of
a program but don't answer the question. OOM usually means that you
must terminate the application. The simplest way is a xmalloc-like
function. If enough context is available some cleanup can be done
before.

Best regards,
Roland Pibinger
Dec 9 '06 #29

P: n/a
On Sat, 09 Dec 2006 12:47:38 -0500, CBFalconer wrote:
>For example, it can save the data it has collected to a file, send
a message, and exit gracefully.
All those functions must guarantee that neither they themself nor any
called function calls *alloc.
>Consider a big sort where the
input mechanism collects records in a linked list until memory is
exhauseted, mergesorts the list, dumps it to a temporary file,
discards the list, and repeats, dumping to the next temporary
file. When the input is exhausted it mergest the set of
temporaries into an output file.
Isn't there an external mergesort?

Best regards,
Roland Pibinger
Dec 9 '06 #30

P: n/a
rp*****@yahoo.com (Roland Pibinger) writes:
On 28 Nov 2006 18:32:42 -0800, "MQ" wrote:
>>I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful).

Not all resources are equal and need equal treatment. It hardly makes
sense to check the return value of malloc. Just use a xmalloc function
which can be found in many variants on the internet (e.g.
http://www.tug.org/tex-archive/dviwa...2xx/xmalloc.c).
I just took a look at xmalloc.c. Ugh.

I suppose it was written to be compatible with pre-ANSI compilers,
something that almost certainly is no longer necessary.

On failure, it prints a message to stderr and terminates the program:

/* 1 means success on VMS, so pick a random number (ASCII `K'). */
exit (75);

Apparently the author wasn't aware that any odd value means success on
VMS.

But those are just details, easily fixed. Here's a more modern
version (it's covered by the GPL, I suppose):

#include <stdio.h>
#include <stdlib.h>

void *xmalloc(size_t size)
{
void *result = malloc(size);
if (result == NULL) {
fprintf(stderr,
"fatal: memory exhausted (xmalloc of %lu bytes).\n",
(unsigned long)size);
exit(EXIT_FAILURE);
}
return result;
}

(A value other than EXIT_FAILURE could be sensible if the code isn't
intended to be 100% portable.)

The problem with this is that it doesn't give the program using it an
opportunity to handle the error itself. If that's what you want -- if
you want to terminate the program immediately if there's an allocation
failure -- then it's not a bad solution. If you want to be able to
recover from an allocation failure, though, then this obviously isn't
what you want to use; instead, you can just call malloc() directly and
add whatever error-handling code you like. And yes, there are cases
where you want to do more than terminate the program on an allocation
failure.

You wrote:

It hardly makes sense to check the return value of malloc.

but that's *exactly* what xmalloc() does.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 9 '06 #31

P: n/a
Roland Pibinger wrote:
On Sat, 09 Dec 2006 12:47:38 -0500, CBFalconer wrote:
>For example, it can save the data it has collected to a file, send
a message, and exit gracefully.

All those functions must guarantee that neither they themself nor
any called function calls *alloc.
Nonsense. At most it must open the putative output file ahead of
time.
>
>Consider a big sort where the
input mechanism collects records in a linked list until memory is
exhauseted, mergesorts the list, dumps it to a temporary file,
discards the list, and repeats, dumping to the next temporary
file. When the input is exhausted it mergest the set of
temporaries into an output file.

Isn't there an external mergesort?
That's what I am building above. These things don't grow on trees.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
Dec 9 '06 #32

P: n/a
Roland Pibinger wrote:
On 28 Nov 2006 18:32:42 -0800, "MQ" wrote:
I am just wondering how most people implement cleanup in C functions.
In particular, if the function opens a number of resources, these need
to be released properly should an error occur at any point in the
function (as well as at the end if successful).

Not all resources are equal and need equal treatment. It hardly makes
sense to check the return value of malloc. Just use a xmalloc function
which can be found in many variants on the internet (e.g.
http://www.tug.org/tex-archive/dviwa...2xx/xmalloc.c).
Besides the objections from the other posters here, this code is
seriously inflexible. You *may* want to ignore the error, or you may
wish to deal with it at the callsite, or maybe you just want assistance
with debugging. But probably, you want some combination of those
three. It pays to make your solution more powerful. For example:

====== ymalloc.h ==============================
/* Just use ymalloc in place of malloc */
#define ymalloc(sz) betterMallocInternal (sz, __FILE__, __LINE__);
extern void * betterMallocInternal (size_t sz, const char * file, int
line);

/* The possible kinds of failures we detect */
enum mallocErrs { MALLOC_ERR_ZERO = 0, MALLOC_ERR_NO_MEM = 1 };

/* Settable error handler for ymalloc (might not be thread safe.) */
extern void (* mallocLogFail) (enum mallocErrs errkind, const char *
file, int line, size_t sz);

====== ymalloc.c ==============================
#include <stdlib.h>

static void logFail (enum mallocErrs errkind, const char * file, int
line, size_t sz) {
char * msgs[2] = { "", "Out of memory; " };
fprintf (stderr, "fatal: %smalloc of %lu bytes at %s[%d].\n",
msgs[errkind], (unsigned long) sz, file, line);
exit (EXIT_FAILURE);
}

void (* mallocLogFail) (enum mallocErrs errkind, const char * file, int
line, size_t sz) = logFail;

void * betterMallocInternal (size_t sz, const char * file, int line) {
void * ptr;

if (!sz) {
mallocLogFail (MALLOC_ERR_ZERO, file, line, sz);
return NULL;
}
if (NULL == (ptr = malloc (sz))) {
mallocLogFail (MALLOC_ERR_NO_MEM, file, line, sz);
}
return ptr;
}
===============================================

The point is that you can choose the policy about what to do about the
failing malloc on a scenario by scenario basis. Malloc() is a slow
enough function, that adding in any arbitrary amount of structure,
debugging, analysis etc, is usually well worth it for the relative
cost.

--
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Dec 9 '06 #33

P: n/a
Roland Pibinger said:
On Sat, 09 Dec 2006 17:50:09 +0000, Richard Heathfield wrote:
>>Roland Pibinger said:
>>And what is your program supposed to do when memory is exhausted?

This is documented in the literature, and has been discussed on this
newsgroup a number of times before. See, for example:

<3D***************@eton.powernet.co.uk>

Those are mostly hints how to reduce the dynamic memory consumption of
a program but don't answer the question.
No, they're suggestions on how to cope when memory is running low - e.g.
when malloc returns NULL (which is a pretty big hint that memory is running
low, is it not?).
OOM usually means that you must terminate the application.
No, it means that *you* must terminate the application (what I call the
"Student Solution", because normally it won't actually lose you any marks
in college). I prefer to deal with matters more robustly.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 9 '06 #34

P: n/a
On 9 Dec 2006 08:32:27 -0800, "santosh" wrote:
>Roland Pibinger wrote:
>http://www.tug.org/tex-archive/dviwa...2xx/xmalloc.c).

Well, it's so simple that most programmers will write their own
versions of it, if need be. The code seems to use the old style
function definition and uses unsigned instead of size_t, which isn't
the way it's done now. And calling exit() with a random value is not
portable. It's better to use EXIT_FAILURE.
Agreed, it was the first example I found on the internet. Google
returns 'about 440,000' hits for 'xmalloc'.
>In short that code is _old_. Better to rewrite it on case-by-case
basis, (particularly since it's trivial.)
Agreed again.

Best regards,
Roland Pibinger
Dec 10 '06 #35

P: n/a
Roland Pibinger wrote:
On Sat, 09 Dec 2006 12:47:38 -0500, CBFalconer wrote:
>For example, it can save the data it has collected to a file, send
a message, and exit gracefully.

All those functions must guarantee that neither they themself nor any
called function calls *alloc.
A fairly common strategy is to malloc() a good-sized
chunk of memory "for emergency use only," sometime during
program initialization when memory is probably plentiful.
If malloc() fails later on, you free() the emergency stash
to provide enough memory for the graceful-shutdown code to
subsist on.

A practical difficulty with this strategy is that it's
hard to estimate what "good-sized" ought to be, and that the
required amount tends to change as time passes and features
are added to the program. Somebody adds an innocent-appearing
but memory-hungry callback via atexit(), and all of a sudden
the stash proves inadequate ... The remedy, I think, is to
include a memory exhaustion test in the regression suite; a
"spiked" version of malloc() et al. can be helpful here. (Of
course, the Standard does not allow one to "spike" a Standard
library function, but there are often sub rosa ways to do so.)

--
Eric Sosman
es*****@acm-dot-org.invalid

Dec 10 '06 #36

P: n/a
Eric Sosman <es*****@acm-dot-org.invalidwrites:
Roland Pibinger wrote:
>On Sat, 09 Dec 2006 12:47:38 -0500, CBFalconer wrote:
>>For example, it can save the data it has collected to a file, send
a message, and exit gracefully.
All those functions must guarantee that neither they themself nor any
called function calls *alloc.

A fairly common strategy is to malloc() a good-sized
chunk of memory "for emergency use only," sometime during
program initialization when memory is probably plentiful.
If malloc() fails later on, you free() the emergency stash
to provide enough memory for the graceful-shutdown code to
subsist on.
The cost of this is that you can run out of memory in cases where
there would have been enough if you hadn't allocated the emergency
stash.

In some cases, you might be able to use some non-critical allocation
as the emergency stash, if you happen to have a sizeable chunk of
allocated memory that you know won't be needed during an emergency
cleanup. But even if that's not possible, I think it's worth the
cost.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 10 '06 #37

P: n/a
Keith Thompson wrote:
Eric Sosman <es*****@acm-dot-org.invalidwrites:
>Roland Pibinger wrote:
>>On Sat, 09 Dec 2006 12:47:38 -0500, CBFalconer wrote:
For example, it can save the data it has collected to a file, send
a message, and exit gracefully.
All those functions must guarantee that neither they themself nor any
called function calls *alloc.
A fairly common strategy is to malloc() a good-sized
chunk of memory "for emergency use only," sometime during
program initialization when memory is probably plentiful.
If malloc() fails later on, you free() the emergency stash
to provide enough memory for the graceful-shutdown code to
subsist on.

The cost of this is that you can run out of memory in cases where
there would have been enough if you hadn't allocated the emergency
stash.
True. Sometimes the canary dies even though the air in
the mine is still just barely breathable.
In some cases, you might be able to use some non-critical allocation
as the emergency stash, if you happen to have a sizeable chunk of
allocated memory that you know won't be needed during an emergency
cleanup. But even if that's not possible, I think it's worth the
cost.
True again: If there's something lying around that you know
won't be needed during the graceful shutdown, that something may
double as the emergency stash. This can be a little more complex
to handle, though. Fragmentation may be a problem (if you wind
up free()ing a lot of little pieces instead of a large chunk),
and there's the question of what to do about malloc() failure
before the disposable data structure is fully constructed. No
reason not to try, though.

--
Eric Sosman
es*****@acm-dot-org.invalid
Dec 10 '06 #38

P: n/a
On Sun, 10 Dec 2006 09:11:17 -0500, Eric Sosman wrote:
A fairly common strategy is to malloc() a good-sized
chunk of memory "for emergency use only," sometime during
program initialization when memory is probably plentiful.
If malloc() fails later on, you free() the emergency stash
to provide enough memory for the graceful-shutdown code to
subsist on.
This strategy mainly postpones OOM. Moreover, you need enough (global)
context for the "graceful-shutdown code" within you alloc function.

Best regards,
Roland Pibinger
Dec 10 '06 #39

P: n/a
Keith Thompson said:
Eric Sosman <es*****@acm-dot-org.invalidwrites:
<snip>
>>
A fairly common strategy is to malloc() a good-sized
chunk of memory "for emergency use only," sometime during
program initialization when memory is probably plentiful.
If malloc() fails later on, you free() the emergency stash
to provide enough memory for the graceful-shutdown code to
subsist on.

The cost of this is that you can run out of memory in cases where
there would have been enough if you hadn't allocated the emergency
stash.
Remember that this subthread was prompted by someone who suggested it's a
waste of time to check malloc's return value at all! Such people never ever
run out of memory, no matter how many Gigabytes of emergency reserve they
allocate. Their programs crash randomly sometimes for no apparent reason,
but at least they never run out of memory!

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 10 '06 #40

P: n/a
Richard Heathfield <rj*@see.sig.invalidwrites:
[...]
Remember that this subthread was prompted by someone who suggested it's a
waste of time to check malloc's return value at all! Such people never ever
run out of memory, no matter how many Gigabytes of emergency reserve they
allocate. Their programs crash randomly sometimes for no apparent reason,
but at least they never run out of memory!
But the person who made this statement presented, as an alternative to
checking malloc's return value, a wrapper function that checks
malloc's return value. (Actually he presented a URL for such a
function.) Some posters here have advocated simply ignoring malloc()
failures; the poster in this thread (sorry, I don't remember who it
was) did not.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 10 '06 #41

P: n/a
Eric Sosman <es*****@acm-dot-org.invalidwrites:
Keith Thompson wrote:
>Eric Sosman <es*****@acm-dot-org.invalidwrites:
[...]
>> A fairly common strategy is to malloc() a good-sized
chunk of memory "for emergency use only," sometime during
program initialization when memory is probably plentiful.
If malloc() fails later on, you free() the emergency stash
to provide enough memory for the graceful-shutdown code to
subsist on.
The cost of this is that you can run out of memory in cases where
there would have been enough if you hadn't allocated the emergency
stash.

True. Sometimes the canary dies even though the air in
the mine is still just barely breathable.
Right. But a more apt analogy here is that you bring a *really big*
canary into the mine with you. If the miners start to have trouble
breathing, you kill the canary and release the oxygen from its
bloodstream into the air, giving the miners enough time to exit
gracefully. (Ok, maybe it's just an inflatable plastic canary.) If
the miners were just about to leave anyway, bringing the canary was a
waste of time -- just as any insurance policy that doesn't pay off is
a waste of money. (Life insurance is a really bad deal if you happen
to be immortal.)

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Dec 10 '06 #42

P: n/a
Keith Thompson wrote:
>
.... snip ...
waste of time -- just as any insurance policy that doesn't pay off
is a waste of money. (Life insurance is a really bad deal if you
happen to be immortal.)
Now I know why I haven't had any for the past 20 years :-) (Apart
from the Social Security death 'benefit').

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>
Dec 11 '06 #43

P: n/a
rp*****@yahoo.com (Roland Pibinger) wrote:
On Sat, 09 Dec 2006 16:42:14 +0000, Richard Heathfield wrote:
Roland Pibinger said:
<snip>
It hardly makes sense to check the return value of malloc.
It certainly doesn't make sense not to!

And what is your program supposed to do when memory is exhausted?
Depends entirely on the situation. If your word processor asks for a
large amount of memory to paste a graphic, and it doesn't get it,
putting up a message saying "sorry, no memory for graphic" is a lot more
civilised than just bombing out of the entire document. Even in the
worst case, putting up a meaningful message and exiting gracefully is
always better than crashing and burning.

Richard
Dec 11 '06 #44

P: n/a
rp*****@yahoo.com (Roland Pibinger) wrote:
On Sat, 09 Dec 2006 12:47:38 -0500, CBFalconer wrote:
Consider a big sort where the
input mechanism collects records in a linked list until memory is
exhauseted, mergesorts the list, dumps it to a temporary file,
discards the list, and repeats, dumping to the next temporary
file. When the input is exhausted it mergest the set of
temporaries into an output file.

Isn't there an external mergesort?
Yes. But they're typically a lot slower than internal sorts, so you
don't use it until you _know_ that you need it. Hence the check on
malloc().

Richard
Dec 11 '06 #45

P: n/a
Keith Thompson said:
Richard Heathfield <rj*@see.sig.invalidwrites:
[...]
>Remember that this subthread was prompted by someone who suggested it's a
waste of time to check malloc's return value at all! Such people never
ever run out of memory, no matter how many Gigabytes of emergency reserve
they allocate. Their programs crash randomly sometimes for no apparent
reason, but at least they never run out of memory!

But the person who made this statement presented, as an alternative to
checking malloc's return value, a wrapper function that checks
malloc's return value.
He did? Oh, so he did. My apologies to Mr Pilinger. Nevertheless, whilst
"detect and die" does at least beat "ignore and hope", it remains a
neophyte solution - acceptable from, say, a first-year student. I'd expect
better from a second-year.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Dec 11 '06 #46

P: n/a
rp*****@yahoo.com (Roland Pibinger) wrote:
On Sat, 09 Dec 2006 17:50:09 +0000, Richard Heathfield wrote:
Roland Pibinger said:
And what is your program supposed to do when memory is exhausted?
This is documented in the literature, and has been discussed on this
newsgroup a number of times before. See, for example:

<3D***************@eton.powernet.co.uk>

Those are mostly hints how to reduce the dynamic memory consumption of
a program but don't answer the question. OOM usually means that you
must terminate the application. The simplest way is a xmalloc-like
function. If enough context is available some cleanup can be done
before.
The problem with xmalloc()-like functions is that they crash anyway;
they just crash with a predictable message. This _may_ be good enough,
but IMO not nearly as often as I see it done in the wild.

Richard
Dec 11 '06 #47

P: n/a
"santosh" <sa*********@gmail.comwrote:
Roland Pibinger wrote:
Not all resources are equal and need equal treatment. It hardly makes
sense to check the return value of malloc. Just use a xmalloc function
which can be found in many variants on the internet (e.g.
http://www.tug.org/tex-archive/dviwa...2xx/xmalloc.c).

Well, it's so simple that most programmers will write their own
versions of it, if need be. The code seems to use the old style
function definition and uses unsigned instead of size_t, which isn't
the way it's done now. And calling exit() with a random value is not
portable. It's better to use EXIT_FAILURE.
Not to mention that the particular random value used in this xmalloc is,
erm, rather silly, given the comment above. Don't _all_ odd values mean
some kind of success on VMS?

Richard
Dec 11 '06 #48

P: n/a
Bill Reid wrote:
Richard Heathfield <rj*@see.sig.invalidwrote in message
news:fr******************************@bt.com...
MQ said:
Bill Reid wrote:
>
>For more complex functions, you'll quite often have contigent
>dependancies for each resource acquisition that don't fit neatly into
>the "nest", and the overhead of the function calling mechanism
>means that functions are not infinitely or always a "fabulous
>mechanism" for something as silly as "keeping the indent
>level down" (unless you like your "C" code to run as slow
>as "Java"!).
>
These are my greatest concerns, as I am writing file system driver code
that needs to be fast efficient.
Rule 1: if it doesn't work, it doesn't matter how fast it is.
Rule 2: it is easier to make a correct program fast than it is to make a
fast program correct.
Rule 3: it is easier to make a readable program correct than it is to make
a
correct program readable.
I think these are actually the three rules for pontificating about
programming instead of actually programming...years ago I read
the REAL rules for writing "correct" "fast" and "efficient" code, and
of course, as everybody who's not just pontificating knows,
it boils down to one general rule: pick the two you really want, because
a lot of times you CAN'T have all three...
And that's before we get anywhere near the Two Rules of
Micro-Optimisation.
For completeness, these are:

Rule 1: Don't do it.
Rule 2 (for experts only): Don't do it yet.
I certainly wasn't talking about anything like "micro-optimization".
I'm talking about great big gobs of "MACRO-optimization", like
slowing down or speeding up what was virtually the IDENTICAL
code by a factor of up to ten-fold (I could make the same program run
in either two seconds or 20 seconds)...
On a lot of modern CPUs the difference between a function call and a
jump (as in generated by switch or if) is nowhere near ten-fold when
compiled with an properly optimising compiler. One fold at most (note
that one-fold is ten times slower) or more typically up to 4 times
slower. Indeed on at least two architectures I code for, a function
call (if not recursive) compiles to execute in exactly the same number
of CPU cycles as a regular jump/branch.
This is not exactly an academic exercise for me, since a lot of the
code I work with takes several HOURS to run on a Pentium-class
machine, and I have to run it EVERY day.
Of course, if you're programming for an architecture as archaic and
register-starved as the Pentium then function call overhead can be an
issue. Even so, improvements have been made to x86-64 which makes the
function call overhead for x86-64 even less than before.

Dec 11 '06 #49

P: n/a
On Mon, 11 Dec 2006 07:41:06 GMT, Richard Bos wrote:
>The problem with xmalloc()-like functions is that they crash anyway;
they just crash with a predictable message.
They don't crash the program, they call exit (which also calls
atexit).
>This _may_ be good enough,
but IMO not nearly as often as I see it done in the wild.
In order to run your program primarily needs one resource, memory.
When it runs out of memory (e.g. due to a memory leak) there is hardly
anything you can do except to (more or less) gracefully terminate the
program. Not even that is always possible since some OS (IIRC, Linux)
never return NULL for malloc even when memory is exhausted.

Best wishes,
Roland Pibinger
Dec 11 '06 #50

69 Replies

This discussion thread is closed

Replies have been disabled for this discussion.