xmalloc

Discussion in 'C Programming' started by Malcolm McLean, Jun 23, 2007.

  1. Continuing the error-handling thread.

    I am floating

    void *xmalloc(int sz)
    {
    void *answer;

    assert(sz >= 0);
    if(sz == 0)
    sz = 1;
    answer = malloc(sz);
    if(!answer)
    {
    fprintf(stderr, "Can't allocate %d byte%c\n", sz, sz == 1 ? ' ', 's');
    exit(EXIT_FAILURE);
    }
    return answer;
    }

    as a solution to the malloc() problem.
    (Chuck Falconer's suggestion)
    You call it for trivial allocations on the basis that if the computer won't
    give a few bytes of memory, not much can be done.
    However you wouldn't call it to allocate an image, for example, because
    legitmate images can be quite large in relation to computer memories.


    --
    Free games and programming goodies.
    http://www.personal.leeds.ac.uk/~bgy1mm
     
    Malcolm McLean, Jun 23, 2007
    #1
    1. Advertising

  2. Malcolm McLean said:

    > Continuing the error-handling thread.
    >
    > I am floating
    >
    > void *xmalloc(int sz)
    > {
    > void *answer;
    >
    > assert(sz >= 0);
    > if(sz == 0)
    > sz = 1;
    > answer = malloc(sz);
    > if(!answer)
    > {
    > fprintf(stderr, "Can't allocate %d byte%c\n", sz, sz == 1 ? ' ',
    > 's'); exit(EXIT_FAILURE);
    > }
    > return answer;
    > }
    >
    > as a solution to the malloc() problem.


    What malloc() problem? And this "solution" suffers from the same problem
    as any other "solution" of the same kind - it's no earthly use in a
    library, and libraries are where malloc calls belong.

    There are two reasons why it's no earthly use in a library.

    For one thing, it displays a message on stderr, which isn't much cop in
    a system where stderr messages are ignored - e.g. a Win32 GUI app. But
    that's trivial to fix - either remove the message completely, or add a
    callback or a pointer to an error code, so that the application can
    worry about how to display the message.

    The second problem is also quite easy to fix, and it's this - the
    function calls exit(), thus taking the decision to quit the program out
    of the hands of the programmer. Therefore, the programmer won't call
    this function himself, and won't be able to call any other function in
    the library that /does/ call it.

    Here's my suggested replacement, which incorporates all the fixes I see
    as being necessary:

    #include <stdlib.h>

    void *xmalloc(size_t sz)
    {
    return malloc(sz);
    }


    > You call it for trivial allocations on the basis that if the computer
    > won't give a few bytes of memory, not much can be done.


    Whether that is true depends on the problem you're trying to solve. For
    example, it may well be possible to switch to a static solution which
    is perhaps not as fast as the dynamic solution but which will
    nevertheless do the Right Thing in a reasonable time.

    <snip>

    --
    Richard Heathfield <http://www.cpax.org.uk>
    Email: -www. +rjh@
    Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
    "Usenet is a strange place" - dmr 29 July 1999
     
    Richard Heathfield, Jun 23, 2007
    #2
    1. Advertising

  3. "Richard Heathfield" <> wrote in message
    news:...
    > Malcolm McLean said:
    >
    >> Continuing the error-handling thread.
    >>
    >> I am floating
    >>
    >> void *xmalloc(int sz)
    >> {
    >> void *answer;
    >>
    >> assert(sz >= 0);
    >> if(sz == 0)
    >> sz = 1;
    >> answer = malloc(sz);
    >> if(!answer)
    >> {
    >> fprintf(stderr, "Can't allocate %d byte%c\n", sz, sz == 1 ? ' ',
    >> 's'); exit(EXIT_FAILURE);
    >> }
    >> return answer;
    >> }
    >>
    >> as a solution to the malloc() problem.

    >
    > What malloc() problem? And this "solution" suffers from the same problem
    > as any other "solution" of the same kind - it's no earthly use in a
    > library, and libraries are where malloc calls belong.
    >
    > There are two reasons why it's no earthly use in a library.
    >
    > For one thing, it displays a message on stderr, which isn't much cop in
    > a system where stderr messages are ignored - e.g. a Win32 GUI app. But
    > that's trivial to fix - either remove the message completely, or add a
    > callback or a pointer to an error code, so that the application can
    > worry about how to display the message.
    >
    > The second problem is also quite easy to fix, and it's this - the
    > function calls exit(), thus taking the decision to quit the program out
    > of the hands of the programmer. Therefore, the programmer won't call
    > this function himself, and won't be able to call any other function in
    > the library that /does/ call it.
    >
    > Here's my suggested replacement, which incorporates all the fixes I see
    > as being necessary:
    >
    > #include <stdlib.h>
    >
    > void *xmalloc(size_t sz)
    > {
    > return malloc(sz);
    > }
    >
    >
    >> You call it for trivial allocations on the basis that if the computer
    >> won't give a few bytes of memory, not much can be done.

    >
    > Whether that is true depends on the problem you're trying to solve. For
    > example, it may well be possible to switch to a static solution which
    > is perhaps not as fast as the dynamic solution but which will
    > nevertheless do the Right Thing in a reasonable time.
    >

    In which case would call regular malloc()
    However you are doubling the cost of the software with that strategy, all
    because a 2GB machine might refuse to give a few hundred bytes of memory. If
    it is running a life-support machine, fair enough, but most software
    doesn't.

    On the other hand you could argue that xmalloc() is a bad idea, because no
    library that uses it could ever find its way into a life-support machine.
    On the other hand, you could argue that the life support machine is more
    likely to blow a fuse than to exit in xmalloc(), so you need a backup
    anyway.

    I take your point about vandalised stderrs.
    Maybe we should put an assert fail in there as well / instead.
    --
    Free games and programming goodies.
    http://www.personal.leeds.ac.uk/~bgy1mm
     
    Malcolm McLean, Jun 23, 2007
    #3
  4. Malcolm McLean said:

    >
    > "Richard Heathfield" <> wrote in message
    > news:...
    >> Malcolm McLean said:
    >>

    <snip>

    >>> You call it for trivial allocations on the basis that if the
    >>> computer won't give a few bytes of memory, not much can be done.

    >>
    >> Whether that is true depends on the problem you're trying to solve.
    >> For example, it may well be possible to switch to a static solution
    >> which is perhaps not as fast as the dynamic solution but which will
    >> nevertheless do the Right Thing in a reasonable time.
    >>

    > In which case would call regular malloc()
    > However you are doubling the cost of the software with that strategy,
    > all because a 2GB machine might refuse to give a few hundred bytes of
    > memory.


    The way I see it is this: by writing an inferior program that is
    prepared to bomb out at the drop of a hat, I can *reduce* the cost of
    writing the program, but it *would be* an inferior program. And then
    I'd have to market the program to the kind of people who used to buy
    Cortinas, or Edsels. I prefer to deal with Maserati types. :)

    <snip>

    --
    Richard Heathfield <http://www.cpax.org.uk>
    Email: -www. +rjh@
    Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
    "Usenet is a strange place" - dmr 29 July 1999
     
    Richard Heathfield, Jun 23, 2007
    #4
  5. On Sat, 23 Jun 2007 18:47:44 +0000, Richard Heathfield wrote:
    >The second problem is also quite easy to fix, and it's this - the
    >function calls exit(), thus taking the decision to quit the program out
    >of the hands of the programmer. Therefore, the programmer won't call
    >this function himself, and won't be able to call any other function in
    >the library that /does/ call it.


    You can often see programs that check the return value of malloc but
    you hardly (practically never) see any code that actually handles the
    OOM (out-of-memory) condition (e.g. frees some pre-allocated memory).
    Strange, isn't it?
    Moreover, C libraries usually crash when the caller provides
    insufficient resources or invalid arguments. In that sense abort() is
    preferable to exit() for OOM.

    >For
    >example, it may well be possible to switch to a static solution which
    >is perhaps not as fast as the dynamic solution but which will
    >nevertheless do the Right Thing in a reasonable time.


    The 'static solution' may cause a 'stack' overflow. BTW, how would you
    handle the second type of OOM, 'stack' overflow? The same way as OOM
    for dynamic memory?


    --
    Roland Pibinger
    "The best software is simple, elegant, and full of drama" - Grady Booch
     
    Roland Pibinger, Jun 23, 2007
    #5
  6. Malcolm McLean

    Eric Sosman Guest

    Malcolm McLean wrote:
    > [...]
    > However you are doubling the cost of the software with that strategy,
    > all because a 2GB machine might refuse to give a few hundred bytes of
    > memory. [...]


    A 2GB machine *will* refuse to allocate a few hundred
    bytes of memory -- immediately after a successful allocation
    of 2GB. Maybe the program could choose to discard the 2GB
    allocation that's just holding a bunch of cache, and try again
    on the couple hundred bytes it Really Needs? Too late, Malcolm
    has already euthanized it. Requesciat in pacem.

    --
    Eric Sosman
    lid
     
    Eric Sosman, Jun 23, 2007
    #6
  7. Roland Pibinger said:

    <snip>

    > You can often see programs that check the return value of malloc but
    > you hardly (practically never) see any code that actually handles the
    > OOM (out-of-memory) condition (e.g. frees some pre-allocated memory).
    > Strange, isn't it?


    Yes. It's a great shame.

    > Moreover, C libraries usually crash when the caller provides
    > insufficient resources or invalid arguments. In that sense abort() is
    > preferable to exit() for OOM.


    Well, I do see your point, but personally I'd prefer for the program to
    make sensible choices that minimise inconvenience to the user.

    >>For
    >>example, it may well be possible to switch to a static solution which
    >>is perhaps not as fast as the dynamic solution but which will
    >>nevertheless do the Right Thing in a reasonable time.

    >
    > The 'static solution' may cause a 'stack' overflow.


    So make sure it doesn't, by avoiding stress to your 'stack'.

    > BTW, how would you
    > handle the second type of OOM, 'stack' overflow? The same way as OOM
    > for dynamic memory?


    One of the reasons dynamic memory is so useful is that there's a heck of
    a lot of it to go around, compared to a typical 'stack' - but the other
    great advantage thereof is that you /can/ detect an OOM condition with
    malloc, whereas you can't with 'stack' overflow - at least, not
    portably. Nevertheless, you can still do quite a lot with a 'stack'
    that doesn't involve a significant risk of overflow (the adjective
    being necessary only because the C Standard gives us very little in the
    way of guarantees about 'stack' capacity - in practice, nowadays it is
    only a problem if you treat the 'stack' rather recklessly).

    --
    Richard Heathfield <http://www.cpax.org.uk>
    Email: -www. +rjh@
    Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
    "Usenet is a strange place" - dmr 29 July 1999
     
    Richard Heathfield, Jun 23, 2007
    #7
  8. Malcolm McLean

    Eric Sosman Guest

    Roland Pibinger wrote:
    > On Sat, 23 Jun 2007 18:47:44 +0000, Richard Heathfield wrote:
    >> The second problem is also quite easy to fix, and it's this - the
    >> function calls exit(), thus taking the decision to quit the program out
    >> of the hands of the programmer. Therefore, the programmer won't call
    >> this function himself, and won't be able to call any other function in
    >> the library that /does/ call it.

    >
    > You can often see programs that check the return value of malloc but
    > you hardly (practically never) see any code that actually handles the
    > OOM (out-of-memory) condition (e.g. frees some pre-allocated memory).
    > Strange, isn't it?


    Not all that strange. Half the programmers in the world
    are below-average.

    But if you follow Malcolm's Maxims, even the above-average
    programmers will be helpless. Programs that deal with malloc()
    failure in a significant way may be a minority, but those that
    do it *need* to to do it. Malcolm argues, in essence, that
    such programs should not be written in C.

    > Moreover, C libraries usually crash when the caller provides
    > insufficient resources or invalid arguments. In that sense abort() is
    > preferable to exit() for OOM.


    Oh, heaven spare us! Not only do you want to kill off
    the program, but you also want to prevent it from running
    its carefully-registered atexit() handlers? And you don't
    care whether buffers are flushed, streams are closed, and
    temporary files are removed? What do you do for amusement,
    juggle flasks of nitroglycerin while fire-walking?

    --
    Eric Sosman
    lid
     
    Eric Sosman, Jun 23, 2007
    #8
  9. "Roland Pibinger" <> wrote in message
    news:...
    > On Sat, 23 Jun 2007 18:47:44 +0000, Richard Heathfield wrote:
    >>The second problem is also quite easy to fix, and it's this - the
    >>function calls exit(), thus taking the decision to quit the program out
    >>of the hands of the programmer. Therefore, the programmer won't call
    >>this function himself, and won't be able to call any other function in
    >>the library that /does/ call it.

    >
    > You can often see programs that check the return value of malloc but
    > you hardly (practically never) see any code that actually handles the
    > OOM (out-of-memory) condition (e.g. frees some pre-allocated memory).
    > Strange, isn't it?
    > Moreover, C libraries usually crash when the caller provides
    > insufficient resources or invalid arguments. In that sense abort() is
    > preferable to exit() for OOM.
    >

    Normally if you've written the program in such a way that it makes a lot of
    trival allocations, there is nothing you can do on failure, short of calling
    a back-up routine that uses fixed buffers.
    So you handle fail conditions for the few large legitimate allocations,
    which might run the computer out of memory, and abort on the rest.

    You can declare a global "world" pointer, and then in atexit try to save the
    state of the program, but there are two problems. Firstly if the OS won't
    give you a few hundred bytes, it might well not open files for you either.
    Secondly, all the code has to be very carefully written so that an
    allocation failure never causes the state to lose coherence. Otherwise you
    will crash when you try to write a null pointer, or worse, write an image
    that looks right but is in fact subtly corrupted.

    So there really isn't an easy answer.
    >>For
    >>example, it may well be possible to switch to a static solution which
    >>is perhaps not as fast as the dynamic solution but which will
    >>nevertheless do the Right Thing in a reasonable time.

    >
    > The 'static solution' may cause a 'stack' overflow. BTW, how would you
    > handle the second type of OOM, 'stack' overflow? The same way as
    > OOM for dynamic memory?
    >

    So if you use recursive subroutines you've already got one potential source
    of crashes on malicious or excessive input. That reduces the motivation to
    fix malloc(), because you can't guarantee correct operation anyway.

    --
    Free games and programming goodies.
    http://www.personal.leeds.ac.uk/~bgy1mm
     
    Malcolm McLean, Jun 23, 2007
    #9
  10. Malcolm McLean

    Ian Collins Guest

    Richard Heathfield wrote:
    > Malcolm McLean said:
    >
    >> Continuing the error-handling thread.
    >>
    >> I am floating
    >>
    >> void *xmalloc(int sz)
    >> {


    Should be size_t.

    >> void *answer;
    >>
    >> assert(sz >= 0);
    >> if(sz == 0)
    >> sz = 1;
    >> answer = malloc(sz);
    >> if(!answer)
    >> {
    >> fprintf(stderr, "Can't allocate %d byte%c\n", sz, sz == 1 ? ' ',
    >> 's'); exit(EXIT_FAILURE);
    >> }
    >> return answer;
    >> }
    >>
    >> as a solution to the malloc() problem.

    >
    > What malloc() problem? And this "solution" suffers from the same problem
    > as any other "solution" of the same kind - it's no earthly use in a
    > library, and libraries are where malloc calls belong.
    >


    It could be enhanced by providing a (cough) global failure function, say
    xmalloc_failure, that gets called when malloc returns NULL:

    void* default_xmalloc_failure(size_t s) {
    assert(!"Can't allocate memory"); return NULL;}

    void* (*xmalloc_failure)(size_t) = &default_xmalloc_failure;

    The xmalloc becomes

    void *xmalloc(size_t sz)
    {
    void *answer = malloc( sz == 0 ? 1 : sz );
    if(answer == NULL)
    {
    xmalloc_failure(sz);
    }
    return answer;
    }

    Which gives the user control over what happens when malloc fails.

    --
    Ian Collins.
     
    Ian Collins, Jun 23, 2007
    #10
  11. Ian Collins said:

    <snip>

    > It could be enhanced by providing a (cough) global failure function,
    > say xmalloc_failure, that gets called when malloc returns NULL:
    >
    > void* default_xmalloc_failure(size_t s) {
    > assert(!"Can't allocate memory"); return NULL;}
    >
    > void* (*xmalloc_failure)(size_t) = &default_xmalloc_failure;
    >
    > The xmalloc becomes
    >
    > void *xmalloc(size_t sz)
    > {
    > void *answer = malloc( sz == 0 ? 1 : sz );
    > if(answer == NULL)
    > {
    > xmalloc_failure(sz);
    > }
    > return answer;
    > }
    >
    > Which gives the user control over what happens when malloc fails.


    It's an improvement, but it's still not good enough as a general
    solution. Consider this code:

    foo *foo_create(size_t n)
    {
    static const foo fblank;
    foo *new = malloc(sizeof *new); /* A */
    if(new != NULL)
    {
    *new = fblank;
    new->bar = malloc(n * sizeof *new->bar); /* B */
    if(new->bar == NULL)
    {
    free(new);
    new = NULL;
    }
    else
    {
    static const t_bar bblank;
    while(n--)
    {
    new->bar[n] = bblank;
    }
    }
    }
    return new;
    }

    If we were to rewrite this to use your suggestion, that would be fine
    for the malloc at A, but if the malloc at B fails, we have to clean up,
    and that means that the cleanup code needs access to new. Your solution
    doesn't address that problem.

    --
    Richard Heathfield <http://www.cpax.org.uk>
    Email: -www. +rjh@
    Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
    "Usenet is a strange place" - dmr 29 July 1999
     
    Richard Heathfield, Jun 23, 2007
    #11
  12. "Richard Heathfield" <> wrote in message
    news:...
    > Ian Collins said:
    >
    > <snip>
    >
    >> It could be enhanced by providing a (cough) global failure function,
    >> say xmalloc_failure, that gets called when malloc returns NULL:
    >>
    >> void* default_xmalloc_failure(size_t s) {
    >> assert(!"Can't allocate memory"); return NULL;}
    >>
    >> void* (*xmalloc_failure)(size_t) = &default_xmalloc_failure;
    >>
    >> The xmalloc becomes
    >>
    >> void *xmalloc(size_t sz)
    >> {
    >> void *answer = malloc( sz == 0 ? 1 : sz );
    >> if(answer == NULL)
    >> {
    >> xmalloc_failure(sz);
    >> }
    >> return answer;
    >> }
    >>
    >> Which gives the user control over what happens when malloc fails.

    >
    > It's an improvement, but it's still not good enough as a general
    > solution. Consider this code:
    >
    > foo *foo_create(size_t n)
    > {
    > static const foo fblank;
    > foo *new = malloc(sizeof *new); /* A */
    > if(new != NULL)
    > {
    > *new = fblank;
    > new->bar = malloc(n * sizeof *new->bar); /* B */
    > if(new->bar == NULL)
    > {
    > free(new);
    > new = NULL;
    > }
    > else
    > {
    > static const t_bar bblank;
    > while(n--)
    > {
    > new->bar[n] = bblank;
    > }
    > }
    > }
    > return new;
    > }
    >
    > If we were to rewrite this to use your suggestion, that would be fine
    > for the malloc at A, but if the malloc at B fails, we have to clean up,
    > and that means that the cleanup code needs access to new. Your solution
    > doesn't address that problem.
    >

    How about this

    void *xmalloc(size_t sz)
    {
    void *answer;

    if(sz == 0)
    sz = 1;
    while( !(answer = malloc(sz))
    (*malloc_failure(sz));

    return answer;
    }
    --
    Free games and programming goodies.
    http://www.personal.leeds.ac.uk/~bgy1mm
     
    Malcolm McLean, Jun 23, 2007
    #12
  13. Malcolm McLean

    Ian Collins Guest

    Richard Heathfield wrote:
    > Ian Collins said:
    >
    > <snip>
    >
    >> It could be enhanced by providing a (cough) global failure function,
    >> say xmalloc_failure, that gets called when malloc returns NULL:
    >>
    >> void* default_xmalloc_failure(size_t s) {
    >> assert(!"Can't allocate memory"); return NULL;}
    >>
    >> void* (*xmalloc_failure)(size_t) = &default_xmalloc_failure;
    >>
    >> The xmalloc becomes
    >>
    >> void *xmalloc(size_t sz)
    >> {
    >> void *answer = malloc( sz == 0 ? 1 : sz );
    >> if(answer == NULL)
    >> {
    >> xmalloc_failure(sz);
    >> }
    >> return answer;
    >> }
    >>
    >> Which gives the user control over what happens when malloc fails.

    >
    > It's an improvement, but it's still not good enough as a general
    > solution. Consider this code:
    >
    > foo *foo_create(size_t n)
    > {
    > static const foo fblank;
    > foo *new = malloc(sizeof *new); /* A */
    > if(new != NULL)
    > {
    > *new = fblank;
    > new->bar = malloc(n * sizeof *new->bar); /* B */
    > if(new->bar == NULL)
    > {
    > free(new);
    > new = NULL;
    > }
    > else
    > {
    > static const t_bar bblank;
    > while(n--)
    > {
    > new->bar[n] = bblank;
    > }
    > }
    > }
    > return new;
    > }
    >
    > If we were to rewrite this to use your suggestion, that would be fine
    > for the malloc at A, but if the malloc at B fails, we have to clean up,
    > and that means that the cleanup code needs access to new. Your solution
    > doesn't address that problem.
    >

    Then you have to turn up the complexity another notch an introduce a
    cleanup handler stack, which just about takes us back to checking each
    return of malloc()!

    This situation is one of the reasons I like that unmentionable in these
    parts cousin of C with its exceptions and destructors!

    --
    Ian Collins.
     
    Ian Collins, Jun 24, 2007
    #13
  14. On Sat, 23 Jun 2007 16:30:12 -0400, Eric Sosman wrote:
    > Not all that strange. Half the programmers in the world
    >are below-average.


    which is at least half-true.

    > But if you follow Malcolm's Maxims, even the above-average
    >programmers will be helpless. Programs that deal with malloc()
    >failure in a significant way may be a minority, but those that
    >do it *need* to to do it. Malcolm argues, in essence, that
    >such programs should not be written in C.


    The discussion basically boils down to one question: Is OOM an error
    that reasonably can and should be handled by the application or is it
    a fatal error?
    The 'fatal error' advocates have already shown how they tackle the
    problem. Now it's time for the other camp to demonstrate how OOM can
    be consistently _handled_ throughout the program ('return NULL;' is
    not enough).


    --
    Roland Pibinger
    "The best software is simple, elegant, and full of drama" - Grady Booch
     
    Roland Pibinger, Jun 24, 2007
    #14
  15. On Sat, 23 Jun 2007 22:16:11 +0100, "Malcolm McLean" wrote:
    >Normally if you've written the program in such a way that it makes a lot of
    >trival allocations, there is nothing you can do on failure, short of calling
    >a back-up routine that uses fixed buffers.
    >So you handle fail conditions for the few large legitimate allocations,
    >which might run the computer out of memory, and abort on the rest.


    The main cause of OOM are memory leaks, i.e. bugs, that cannot be
    handled anyway. abort() is more dramatic than exit(). It indicates
    that there is a problem and some action ought to be performed now.


    --
    Roland Pibinger
    "The best software is simple, elegant, and full of drama" - Grady Booch
     
    Roland Pibinger, Jun 24, 2007
    #15
  16. (Roland Pibinger) writes:
    [...]
    > The discussion basically boils down to one question: Is OOM an error
    > that reasonably can and should be handled by the application or is it
    > a fatal error?
    > The 'fatal error' advocates have already shown how they tackle the
    > problem. Now it's time for the other camp to demonstrate how OOM can
    > be consistently _handled_ throughout the program ('return NULL;' is
    > not enough).


    Who says it can be handled *consistently*?

    --
    Keith Thompson (The_Other_Keith) <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."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, Jun 24, 2007
    #16
  17. "Keith Thompson" <> wrote in message
    news:...
    > (Roland Pibinger) writes:
    > [...]
    >> The discussion basically boils down to one question: Is OOM an error
    >> that reasonably can and should be handled by the application or is it
    >> a fatal error?
    >> The 'fatal error' advocates have already shown how they tackle the
    >> problem. Now it's time for the other camp to demonstrate how OOM can
    >> be consistently _handled_ throughout the program ('return NULL;' is
    >> not enough).

    >
    > Who says it can be handled *consistently*?
    >

    That is part of the issue.
    malloc() return NULL. The if condition following the malloc() is like a
    yippy dog chasing a car. Now it's got an error, what is it going to do with
    it?


    --
    Free games and programming goodies.
    http://www.personal.leeds.ac.uk/~bgy1mm
     
    Malcolm McLean, Jun 24, 2007
    #17
  18. Malcolm McLean

    Eric Sosman Guest

    Roland Pibinger wrote:
    > On Sat, 23 Jun 2007 16:30:12 -0400, Eric Sosman wrote:
    >> Not all that strange. Half the programmers in the world
    >> are below-average.

    >
    > which is at least half-true.
    >
    >> But if you follow Malcolm's Maxims, even the above-average
    >> programmers will be helpless. Programs that deal with malloc()
    >> failure in a significant way may be a minority, but those that
    >> do it *need* to to do it. Malcolm argues, in essence, that
    >> such programs should not be written in C.

    >
    > The discussion basically boils down to one question: Is OOM an error
    > that reasonably can and should be handled by the application or is it
    > a fatal error?
    > The 'fatal error' advocates have already shown how they tackle the
    > problem. Now it's time for the other camp to demonstrate how OOM can
    > be consistently _handled_ throughout the program ('return NULL;' is
    > not enough).


    Already mentioned a few times in this thread:

    buff = malloc(image_size);
    if (buff == NULL) {
    fprintf (stderr, "Image too large (%lu) to paste\n",
    (unsigned long)image_size);
    return;
    }
    /* read image into buff, insert in current document */

    Here's another I think has been referred to:

    must_have_mem = malloc(how_much);
    if (must_have_mem == NULL) {
    fprintf (stderr, "Out of memory; shutting down\n");
    save_snapshot(snapshot_file);
    exit (EXIT_FAILURE);
    }
    /* store "must have" data in allocated memory */

    And here's still another (I don't remember whether it's
    cropped up in this thread yet):

    void *getmem(size_t bytes) {
    void *new = malloc(bytes);
    if (new == NULL && bytes > 0) {
    fprintf (stderr, "Failed to allocate %lu bytes\n",
    (unsigned long)bytes);
    free (emergency_stash);
    emergency_stash = NULL;
    new = malloc(bytes);
    if (new == NULL) {
    fprintf (stderr, "You were warned ...!\n");
    exit (EXIT_FAILURE);
    }
    fprintf (stderr, "Running on fumes: save your work "
    "and exit soon!\n");
    }
    return NULL;
    }

    Even if out-of-memory is a "fatal error," it does not follow
    that the program should have no opportunity to "die with dignity."
    Have you made a will, Roland? If so, should the fact that many
    people die intestate invalidate your will? If not, I certainly
    don't want your intestacy to invalidate my will!

    --
    Eric Sosman
    lid
     
    Eric Sosman, Jun 24, 2007
    #18
  19. Malcolm McLean

    Eric Sosman Guest

    Malcolm McLean wrote:
    >
    > "Roland Pibinger" <> wrote in message
    > news:...
    >> On Sat, 23 Jun 2007 18:47:44 +0000, Richard Heathfield wrote:
    >>> The second problem is also quite easy to fix, and it's this - the
    >>> function calls exit(), thus taking the decision to quit the program out
    >>> of the hands of the programmer. Therefore, the programmer won't call
    >>> this function himself, and won't be able to call any other function in
    >>> the library that /does/ call it.

    >>
    >> You can often see programs that check the return value of malloc but
    >> you hardly (practically never) see any code that actually handles the
    >> OOM (out-of-memory) condition (e.g. frees some pre-allocated memory).
    >> Strange, isn't it?
    >> Moreover, C libraries usually crash when the caller provides
    >> insufficient resources or invalid arguments. In that sense abort() is
    >> preferable to exit() for OOM.
    >>

    > Normally if you've written the program in such a way that it makes a lot
    > of trival allocations, there is nothing you can do on failure, short of
    > calling a back-up routine that uses fixed buffers.
    > So you handle fail conditions for the few large legitimate allocations,
    > which might run the computer out of memory, and abort on the rest.


    That is so bass-ackwards it boggles the mind.

    Here's the question: Does your program have in-memory state
    that is valuable? If so, the proximate cause of the program's
    trouble is irrelevant; what matters is the preservation of that
    valuable state.

    > You can declare a global "world" pointer, and then in atexit try to save
    > the state of the program, but there are two problems.


    ... one being that abort doesn't run atexit callbacks ...

    > Firstly if the OS
    > won't give you a few hundred bytes, it might well not open files for you
    > either. Secondly, all the code has to be very carefully written so that
    > an allocation failure never causes the state to lose coherence.
    > Otherwise you will crash when you try to write a null pointer, or worse,
    > write an image that looks right but is in fact subtly corrupted.
    >
    > So there really isn't an easy answer.


    Let's avoid all hard problems; they demoralize the weak.

    > So if you use recursive subroutines you've already got one potential
    > source of crashes on malicious or excessive input. That reduces the
    > motivation to fix malloc(), because you can't guarantee correct
    > operation anyway.


    And the janitor might unplug the computer to plug in the
    floor polisher, or the fire sprinklers might open up and flood
    the motherboard, or a meteorite might smash the computer to
    tiny bits, or the Borg might teleport it away for assimilation.
    Does the existence of Failure Mode A dissuade you from taking
    any precautions against Failure Mode B? "I don't wear seat
    belts in cars, because avian flu might kill me anyhow." Pfui!
    --
    Eric Sosman
    lid
     
    Eric Sosman, Jun 24, 2007
    #19
  20. Malcolm McLean

    SM Ryan Guest

    (Roland Pibinger) wrote:
    # On Sat, 23 Jun 2007 16:30:12 -0400, Eric Sosman wrote:
    # > Not all that strange. Half the programmers in the world
    # >are below-average.
    #
    # which is at least half-true.

    Presumes a normal distribution. While many processes show normal
    distribution of results, many processes also do not. Human
    populations which are deliberately selected for some trait can
    be signficantly skewed.

    Hidden assumptions--the claymore mines of programming.

    --
    SM Ryan http://www.rawbw.com/~wyrmwif/
    I love the smell of commerce in the morning.
     
    SM Ryan, Jun 24, 2007
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jeff

    [OT?] xmalloc

    Jeff, Jul 1, 2003, in forum: C Programming
    Replies:
    10
    Views:
    1,241
    Dan Pop
    Jul 7, 2003
  2. Kelsey Bjarnason

    xmalloc string functions

    Kelsey Bjarnason, Jan 27, 2008, in forum: C Programming
    Replies:
    240
    Views:
    3,532
  3. xmalloc.c - my xmalloc

    , Feb 15, 2008, in forum: C Programming
    Replies:
    11
    Views:
    925
  4. Tim Hollingsworth

    Multi-threaded C++, new, and xmalloc

    Tim Hollingsworth, Nov 23, 2006, in forum: Ruby
    Replies:
    4
    Views:
    126
    Paul Brannan
    Nov 24, 2006
Loading...

Share This Page