Checking return values for errors, a matter of style?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Morris Dovey

    #61
    Re: Checking return values for errors, a matter of style?

    Keith Thompson (in lnac6o3zar.fsf@ nuthaus.mib.org) said:

    | Out of curiosity, how often do real-world programs really do
    | something fancy in response to a malloc() failure?

    I don't a definitive answer to this one; but will guess that there are
    more than one would be inclined to guess - about a third of the
    systems I've worked on over the last 20 years have had recovery
    strategies for dealing with this problem.

    | The simplest solution, as you say, is to immediately abort the
    | program (which is far better than ignoring the error). The next
    | simplest solution is to do some cleanup (print a coherent error
    | message, flush buffers, close files, release resources, log the
    | error, etc.) and *then* abort the program.

    | I've seen suggestions that, if a malloc() call fails, the program
    | can fall back to an alternative algorithm that uses less memory.
    | How realistic is this? If there's an algorithm that uses less
    | memory, why not use it in the first place? (The obvious answer:
    | because it's slower.) Do programmers really go to the effort of
    | implementing two separate algorithms, one of which will be used
    | only on a memory failure (and will therefore not be tested as
    | thoroughly as the primary algorithm)?

    I'm not sure about "alternativ e algorithms"; but I have an application
    that efficiently "recycles" the malloc()ed allocations, rather than
    free()ing them. If it decides it's using too much of the available
    memory (or if the workload exceeds a threshold), the application
    presents a request to a supervisory node for cloning (starting same
    application on a new node) and transfers a portion of its database to
    be handled by the new node.

    Some systems are designed to operate in multiple modes - for example,
    it's not unusual for launch vehicles to have a "boost" mode, in which
    processing essential to sub-orbital flight is given highest priority
    for all available resources, an "orbital" mode in which data
    acquisition is given the highest priority, a "dump" mode in which
    activities related to data transmission are given highest priority,
    and perhaps "diagnostic " and "maintenanc e" modes. Resource
    allocation/retention strategy is (re)configured by a mode change.

    Other systems incorporate selective degradation in which priorities
    are managed over a spectrum and resource allocations are made (and
    retained) according to the current priority. A low-priority processing
    unit may be asked to surrender a portion (or all) the resouces it
    currently "owns".

    Choice of approach pretty much depends on the functional requirements.

    --
    Morris Dovey
    DeSoto Solar
    DeSoto, Iowa USA



    Comment

    • Dave Vandervies

      #62
      Re: Checking return values for errors, a matter of style?

      In article <PM-dnQLIFpsC5FLZRV nyuA@bt.com>,
      Richard Heathfield <invalid@invali d.invalidwrote:
      >The response to a failure depends on the situation. I've covered this in
      >some detail in my one-and-only contribution to "the literature", so I'll
      >just bullet-point some possible responses here:
      >
      >* abort the program. The "student solution" - suitable only for high school
      students and, perhaps, example programs (with a big red warning flag).
      >* break down the memory requirement into two or more sub-blocks.
      >* use less memory!
      >* point to a fixed-length buffer instead (and remember not to free it!)
      >* allocate an emergency reserve at the beginning of the program
      >* use virtual memory on another machine networked to this one (this
      is a lot of work, but it may be worth it on super-huge projects)
      * Leave the module's internal structures in a sane state and return the
      partial results you've built so far

      For the system I work with at my day job, memory allocation failure is
      treated as a can't-happen. Any computer we can buy that will provide
      enough CPU cycles per unit time will have between a binary order of
      magnitude and a decimal order of magnitude more memory than our worst-case
      use, so we don't worry too much about how we handle allocation failures;
      if malloc starts failing, it's a symptom of much worse problems - both
      the inputs to and whatever happens to the outputs from anything that
      uses dynamically allocated memory are likely to be wrong, so there's
      not much point in doing anything other than leaving things in a state
      we can recover from if the rest of the system recovers without a reboot.

      But we still don't write code that crashes if malloc fails.

      You wouldn't omit
      >a check on fopen
      I've seen allegedly-stable code that did exactly that.


      dave

      --
      Dave Vandervies dj3vande@csclub .uwaterloo.ca
      Where is the /benefit/ in turning good C code into bad C code just
      so that it can also be bad C++ code?
      --Richard Heathfield in comp.lang.c

      Comment

      • v4vijayakumar

        #63
        Re: Checking return values for errors, a matter of style?





        Johan Tibell wrote:
        I've written a piece of code that uses sockets a lot (I know that
        sockets aren't portable C, this is not a question about sockets per
        se). Much of my code ended up looking like this:
        >
        if (function(socke t, args) == -1) {
        perror("functio n");
        exit(EXIT_FAILU RE);
        }
        >
        I feel that the ifs destroy the readability of my code. Would it be
        better to declare an int variable (say succ) and use the following
        structure?
        >
        int succ;
        >
        succ = function(socket , args);
        if (succ == -1) {
        perror("functio n");
        exit(EXIT_FAILU RE);
        }
        >
        What's considered "best practice" (feel free to substitute with: "what
        do most good programmers use")?


        all you are going to do is print error message and exit, then you can
        as well do this from "function(socke t, args)" itself. This means that
        you know failure of "function(socke t, args)" can not be properly
        handled by its users (programmers?!) . Returning -1 means that giving a
        chance to them to handle it.

        following example can be useful.

        function(socket , args) sets proper values for global variable "errno2"
        in case of any failures. Then, another function, say, rerrq (report
        error and quit) reports error and quits from program. like,

        function(socket , args);
        rerrq("function error ");

        rerrq might look like this,

        void rerrq(const char *msg)
        {
        if(errno) /* any system errors? */
        perror(msg);
        else if(errno2) /* any other errors */
        printf("%s: %s\n", msg, errmsg());
        else
        return; /* no errors */

        exit(1);
        }

        'errmesg()' returns proper string for errno2.

        Comment

        • av

          #64
          Re: Checking return values for errors, a matter of style?

          On 2 Aug 2006 11:46:47 -0700, goose wrote:
          >#define FMALLOC(ptr,siz e) (ptr=malloc (size) ? ptr : exit (-1))
          >...
          for sure i don't understand well
          But is it not "void exit(int);"? Did the above compile?
          i think it is better

          void* fmalloc(int size)
          {void *ptr;
          if(size<0 || (ptr=malloc(siz e))==0)
          {printf("\nMemo ry error: exit...\n");
          if( eventualy_some_ thing_to_do_if_ ungly_error()== 0 )
          goto la;
          exit(1);
          }
          la:;
          return ptr;
          }

          use fmalloc() for normal use
          use malloc() if fmalloc() can not handle something
          >char *p; FMALLOC(ptr, sizeof *p * q)
          >...
          >Tracking UB is a bloody nightmare for the maintainer!!!
          >Being unable to reproduce the bug is morale-killer.
          >
          >[1] When your 0.02% or whatever finally comes up.
          >
          >goose,

          Comment

          • Chris Torek

            #65
            Re: Checking return values for errors, a matter of style?

            In article <1154543241.450 670.228700@m79g 2000cwm.googleg roups.com>
            goose <ruse@webmail.c o.zawrote:
            >... here is a slightly modified (incomplete) function
            >that I wrote (during the course of play:) in the last 30 minutes.
            >
            >The macros ERROR and DIAGNOSTIC are (currently) identical
            >and merely print the message to screen (with filename, line
            >number and function name):
            >
            >----------------------
            >#define TEST_INPUT ("test.in")
            >#define TOK_FOPEN (1)
            >#define TOK_FERROR (2)
            >#define TOK_INIT (3)
            >bool test_token (void)
            >{
            FILE *in = fopen (TEST_INPUT, "r");
            jmp_buf handler;
            int e, c, counter;
            >
            /* All errors caught and handled here */
            if ((e = setjmp (handler))!=0) {
            switch (e) {
            Technically this (assignment to "e") is not valid in ANSI C. You
            can simply do:

            switch (setjmp(handler )) {

            and make "case 0" handle the "normal, no-error" return.

            Note, however, that if you use setjmp(), you must make some of
            your local variables "volatile" (any that are changed after the
            setjmp() call, and referred to after longjmp() returns to the
            setjmp()).

            ["case"s snipped; end of if/setjmp sequence here:]
            }
            /* Meat of function */
            if (!in) longjmp (handler, TOK_FOPEN);
            This, of course, could just be a simple "goto", since the
            target of the goto (longjmp is just a heavy-duty goto) is
            the current function. Why not just write:

            if (!in) goto tok_fopen_error ;

            ?

            [more snippage]
            if (!token_init ()) longjmp (handler, TOK_INIT);
            [snip stuff that calls functions]
            if (ferror (in)) {
            longjmp (handler, TOK_FERROR);
            }
            Again, these could just be ordinary local-only "goto"s. You only
            need to use longjmp when the idea is to "goto" out of some nested
            function -- some function that has been called from here, or called
            from something called from here, etc -- back to the original
            "setjmp". By eliminating the "local goto" cases from the original
            switch, you could probably simplify the whole thing to:

            if (setjmp(handler )) {
            ... handle error from subroutine call ...
            }

            Not only are "local-only gotos" (much) easier to read and debug,
            they are also generally more efficient (not that Standard C ever
            makes any efficiency promises). So avoiding longjmp() as much
            as possible seems like good idea.
            --
            In-Real-Life: Chris Torek, Wind River Systems
            Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
            email: forget about it http://web.torek.net/torek/index.html
            Reading email is like searching for food in the garbage, thanks to spammers.

            Comment

            • goose

              #66
              Re: Checking return values for errors, a matter of style?

              Chris Torek wrote:
              In article <1154543241.450 670.228700@m79g 2000cwm.googleg roups.com>
              goose <ruse@webmail.c o.zawrote:
              <snipped>
              if ((e = setjmp (handler))!=0) {
              switch (e) {
              >
              Technically this (assignment to "e") is not valid in ANSI C.
              Why not? I honestly thought that it was valid.

              <snipped everything I agree with>
              >
              Again, these could just be ordinary local-only "goto"s. You only
              need to use longjmp when the idea is to "goto" out of some nested
              function -- some function that has been called from here, or called
              from something called from here, etc -- back to the original
              "setjmp". By eliminating the "local goto" cases from the original
              switch, you could probably simplify the whole thing to:
              >
              if (setjmp(handler )) {
              ... handle error from subroutine call ...
              }
              >
              Not only are "local-only gotos" (much) easier to read and debug,
              they are also generally more efficient (not that Standard C ever
              makes any efficiency promises). So avoiding longjmp() as much
              as possible seems like good idea.
              True, although it was all local jumps in this example, I
              normally tend to use set/longjmp for when I need to
              break out of deeply nested function calls without having
              to explicitly release resources at every step of the
              way.

              goose,

              Comment

              • Chris Torek

                #67
                Re: Checking return values for errors, a matter of style?

                >>In article <1154543241.450 670.228700@m79g 2000cwm.googleg roups.com>
                >>goose <ruse@webmail.c o.zawrote:
                >> if ((e = setjmp (handler))!=0) {
                >Chris Torek wrote:
                >>Technically this (assignment to "e") is not valid in ANSI C.
                In article <1154947183.788 247.34050@m73g2 000cwd.googlegr oups.com>
                goose <ruse@webmail.c o.zawrote:
                >Why not?
                The Standard says (about setjmp):

                Environmental restriction

                [#4] An invocation of the setjmp macro shall appear only in
                one of the following contexts:

                - the entire controlling expression of a selection or
                iteration statement;

                - one operand of a relational or equality operator with
                the other operand an integer constant expression, with
                the resulting expression being the entire controlling
                expression of a selection or iteration statement;

                - the operand of a unary ! operator with the resulting
                expression being the entire controlling expression of a
                selection or iteration statement; or

                - the entire expression of an expression statement
                (possibly cast to void).

                [#5] If the invocation appears in any other context, the
                behavior is undefined.

                The list above excludes ordinary assignment.

                For the rationale, you would have to ask committee members. My best
                guess is that they were worried about stack-based implementations .

                (It would be nice if the C standard raised the bar on implementors
                here, saying "setjmp and longjmp have to Just Work", none of this
                "can only use setjmp's return value in very particular ways" and
                "must use volatile qualifier on local variables" business. GCC
                manages the latter by doing actual strcmp()s on function calls, to
                see if you have called setjmp(), and if so, jiggle registers around
                so that you do not have to mark things "volatile". In fact, the
                set of functions handled specially includes "setjmp", "sigsetjmp" ,
                and "savectx" -- so if you name your own function "savectx", it has
                weird registers-to-stack behavior that slows it down, but makes it
                work[%] even if savectx() acts like setjmp().)

                [% Actually there are some bugs here, at least in some versions of
                gcc: I had to add "asm" lines to mark the SPARC "%l" registers as
                "clobbered" in my kernel, for the SMP implementation fo BSD/OS on
                the SPARC.]
                --
                In-Real-Life: Chris Torek, Wind River Systems
                Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
                email: forget about it http://web.torek.net/torek/index.html
                Reading email is like searching for food in the garbage, thanks to spammers.

                Comment

                Working...