Bogus longjmp clobber warning?

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

 



Apologies in advance for the length of this post.


BACKGROUND

I'm working on a multi-threaded server program, and I've been tearing my
hair out over error logging.  My goal is to be able to use a construct
such as:

    if ((pt_errno = pthread_function(...)) != 0)
    {
        FOO_LOG(FOO_ERROR, "Error in pthread_function: %s",
                FOO_MSG(pt_errno));
        return -1;
    }

Almost as important, I'd like to minimize the number of errors that can
occur in the error-logging code itself.  My initial attempt to write a
thread-safe strerror equivalent got derailed by this; it had so many
pthread calls, each of which could fail, which required logging, etc.,
etc.

I thought that I had found a rather clever solution, using a combination
of macros and setjmp/longjmp:


extern void foo_log_fn(int pri, const char *format, ...);

#define BUFFER_CHUNK	128

const char *foo_msg_fn(int buf_size, char *buf, int errnum,
        jmp_buf env)
{
    if (buf_size == 0)
    {
        do
        {
            assert(buf_size <= INT_MAX - BUFFER_CHUNK);
            buf_size += BUFFER_CHUNK;
	
            {
                char	test_buf[buf_size];
		
                if (strerror_r(errnum, buf, buf_size) == 0)
                    return buf_size;
            }
        }
        while (errno == ERANGE);
	
        if (errno == EINVAL)
            return "INTERNAL ERROR: Invalid error number";
        else
            return "INTERNAL ERROR: Unexpected strerror_r error";
    }
    else
    {
        if (strerror_r(errnum, buf, buf_size) == 0)
            return buf;
        else
            FOO_UNREACHABLE;
    }
}

#define FOO_MSG(errnum)     foo_msg_fn(buf_size, buf, (errnum), env)

#define FOO_LOG(pri, ...)                                              \
                                                                       \
    do                                                                 \
    {                                                                  \
        int         save_errno = errno;                                \
        int         buf_size;                                          \
        jmp_buf     env;                                               \
                                                                       \
        if ((buf_size = setjmp(env)) == 0)                             \
        {                                                              \
            char *buf = NULL;                  /* suppress warning */  \
            foo_log_fn((pri), __VA_ARGS__);                            \
            buf = NULL;                        /* suppress warning */  \
        }                                                              \
        else                                                           \
        {                                                              \
            char buf[buf_size];                                        \
            errno = save_errno;                                        \
            foo_log_fn((pri), __VA_ARGS__);                            \
            buf[0] = '\0';                    /* suppress warning */   \
        }                                                              \
                                                                       \
        errno = save_errno;                                            \
    }                                                                  \
    while (0)                                                          \


THE PROBLEM

Here is a self-contained usage example, with the macros expanded:


#include <stddef.h> #include <setjmp.h> #include <errno.h>

extern void foo_log_fn(int pri, const char *format, ...);
extern int foo_msg_fn(int buf_size, char *buf, int errnum, jmp_buf env);

extern int pthread_rwlock_rdlock(void);
extern int pthread_rwlock_unlock(void);

extern int foo_exit_flag;

int foo_set_exit_flag(void)
{
    int     rv, pt_errno;

    if ((pt_errno = pthread_rwlock_rdlock()) != 0)
    {
        do
        {
            int     	    save_errno = errno;
            int     	    buf_size;
            jmp_buf 	    env;
	
            if ((buf_size = setjmp(env)) == 0)
            {
                char *buf = NULL;
                foo_log_fn(0, "Error: %s",
                        foo_msg_fn(buf_size, buf, pt_errno, env));
                buf = NULL;
            }
            else
            {
                char buf[buf_size];
                errno = save_errno;
                foo_log_fn(0, "Error: %s",
                        foo_msg_fn(buf_size, buf, pt_errno, env));
                buf[0] = '\0';
            }
	
            errno = save_errno;
        }
        while (0);
	
        return -1;
    }

    rv = foo_exit_flag;

    if ((pt_errno = pthread_rwlock_unlock()) != 0)  /* **** */
        return -1;                                  /* **** */

    return rv;
}

If this is compiled without optimization, no warnings are emitted.  If
any optimization is turned on, I get a warning that pt_errno might be
clobbered by longjmp.  Interestingly, if the second usage of pt_errno
(the lines marked with "/* **** */") is commented out, no warning is
emitted, even with optimization turned on.

The description of longjmp on The Open Group's web site states:

  All accessible objects have values, and all other components of the
  abstract machine have state (for example, floating-point status flags
  and open files), as of the time longjmp() was called, except that
  values of objects of automatic storage duration are unspecified if
  they meet the following conditions:

    * They are local to the function containing the corresponding
      setjmp() invocation.

    * They do not have volatile-qualified type.

    * They are changed between the setjmp() invocation and longjmp()
      call.

pt_errno definitely meets the first two criteria, but it just as
definitely doesn't meet the third.  So is this:

  1.  buggy code,

  2.  code that the GCC optimizer just can't handle, or

  3.  a spurious warning?

If it's #1, what's the bug?  If it's #3, is there a code change to
suppress it or a switch to turn this particular warning off?

If you got all the way down here, thanks!

--
========================================================================
Ian Pilcher                                        i.pilcher@xxxxxxxxxxx
========================================================================


[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux