gnu-arch-users
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Gnu-arch-users] Nit


From: Robin Farine
Subject: Re: [Gnu-arch-users] Nit
Date: 22 Oct 2003 14:49:55 +0200
User-agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.2

>>>>> "Tom" == Tom Lord <address@hidden> writes:

    Tom> 1) the original motivation for all this:  what kind of conding
    Tom>    conventions to decide on for libhackerlab and for a possible
    Tom>    librefacation of libarch.

OK, I almost forgot the original goal.

[non-local exits are a no-go when programming in a language without
 descent built-in support]

Yeah, the net advantage of this kind of "feature" is a very good rate
of resources leakage in long-term applications.

    Tom>    One thing _missing_ from this thread, other than the intial chat
    Tom>    about GError, is disucssion about the _pattern_matching_ aspect of
    Tom>    error handling.  GError offers us domain/code which didn't really
    Tom>    strike me as compelling.

Since the idea is to check for errors at the call site, I think like
Andrew that a simple integer is sufficient. The values can be split
in two sets, a range from 0 to K-1 for common errors and the remaining
K..2^N-1 for library specific errors. Again, since errors are checked
at the call site, the caller knows the library the failing function
belongs to and thus can translate library specific codes into text
messages (e.g. by calling a per-library function that maps codes to
messages).

This can be combined with an error logging mechanism to provide the
ability to follow a sequence of correlated error messages for
diagnostic purpose. The UDI project offers a nice solution: a status
code consists of a 32 bits value split in a 16 bits correlation
number, one bit to differentiate between common errors and UDI
meta-language (library|module|domain|...) specific errors, and a 15
bits field for the actual error code.

The idea is that whenever an error occurs and a message is logged, the
logging function receives a reference to the status code and, iff the
correlation number is 0, it assigns the next available correlation
number to the status code. Then, it logs the message along with the
correlation number. Non-logging code is allowed to change the error
code but not the correlation number (except when initializing a newly
declared status code variable).

[...]

>From a previous post:

    Tom>        bar (error_t * caller_err, ...)
    Tom>         {
    Tom>           error_t err = 0;

    Tom>           foo (&err, ...);

    Tom>           if (is_error_i_handle (err))
    Tom>             handle it;
    Tom>           else if (is_error_i_forward)
    Tom>             forward_error (caller_err, err);
    Tom>           else
    Tom>             invariant (!err);
    Tom>         }

    Tom> (With the understanding that `forward_err', if passed a NULL first 
    Tom> argument, turns into an abort.)

If bar() is a generic usage library function then (1) it should not
decide by itself to just abort in presence of an error and (2) it
should not handle and hide most of the errors (cf. "screen saver" vs
"nuclear power plant" or "caching memory allocator" examples). So this
basically leaves us with:

        bar (error_t * caller_err, ...)
        {
          error_t err = 0;

          foo (&err, ...);

          if (caller_err)
            forward_error (caller_err, err);
          else
            abort();
        }

But foo() also respects the error protocol so bar() can just pass
caller_err to foo(), and so on. Such functions only needs to check
error returned from nested calls in order to cleanup locally allocated
resource and propagate the error upwards. A generic usage pattern
would look like

        bool error(error_t * err) { return err && err->code; }

        bar (error_t * err, ...)
        {
          ...
          foo1 (err);
          if (error (err))
            goto foo1_cleanup;
          foo2 (err);
          if (error (err)) {
            goto foo2_cleanup;
            log_error (err, "libzebu::bar() failed to foo2");
          }
          ret = baz ();
          if (ret == -1) {
            if (err) {
              set_error (err, BAZERR /* not bizarre! */);
              log_error (err, "libzebu::bar(): foobar in baz");
              goto baz_cleanup;
            } else
              abort();
          }
          ...
          goto success;
        baz_cleanup:
          ...
        foo2_cleanup:
          ...
        success:
          return ...;
        }


On the other hand, if bar() is a top-level application function, then
it would probably not take an error argument but rather declare it
locally and check and handle errors returned from nested library calls
or just pass them 0.

I also think that "is_error_i_handle (err)" would not in practice be
very useful. You mentioned violation of abstractions barriers in a
previous post. For a caller to be able to know whether it is good for
him that a callee handles an error or not implies that the caller
knows about the internals of nested functions in the call path. This
seems unrealistic and to imply a violation of abstraction barriers.

-- 
rnf




reply via email to

[Prev in Thread] Current Thread [Next in Thread]