volatile in sigsetjmp and siglongjmp

conclusion

In postgresql, the error handling methods pg_try() + elog() + pg_catch() is implemented through sigsetjmp and siglongjmp. But the variables changed in pg_try() and used in pg_catch() must be declared as volatile. The reasons are below:

reason

  • The sigsetjmp call must store all the register value and the siglongjmp restore the registers from it. It’s very reasonable in the view of asm code.
  • If a variable is not declared as volatile, the assignment operation of it in pg_try() may only changes the variable in the register. And after jumping by the siglongjmp call, the value of variable is changed back to the point of sigsetjmp , which means the changes in pg_try() lost

example

Let’s consider an example:

void example_function() {
    int counter = 0;  // Should be "volatile int counter = 0;"

    PG_TRY();
    {
        counter = 42;
        // Some operation that might throw an error
        perform_risky_operation();
    }
    PG_CATCH();
    {
        // We expect counter to be 42 here
        elog(WARNING, "Operation failed, counter value: %d", counter);
    }
    PG_END_TRY();
}

Without volatile, the compiler might:

  1. Keep counter in a register
  2. Never actually write the value 42 back to memory
  3. When the error occurs and PG_CATCH executes after siglongjmp, the value of counter might not be 42 as expected

With volatile, the compiler is forced to:

  1. Actually write to memory whenever counter is modified
  2. Read from memory whenever counter is accessed
  3. Not reorder or optimize away these memory operations

This ensures that after the non-local jump, the variable has the expected value.

-Wclobbered is useless

FROM https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

`-Wclobbered`[](https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wclobbered)

Warn for variables that might be changed by `longjmp` or `vfork`. This warning is also enabled by -Wextra.

FROM pg source

 * Note: if a local variable of the function containing PG_TRY is modified
 * in the PG_TRY section and used in the PG_CATCH section, that variable
 * must be declared "volatile" for POSIX compliance.  This is not mere
 * pedantry; we have seen bugs from compilers improperly optimizing code
 * away when such a variable was not marked.  Beware that gcc's -Wclobbered
 * warnings are just about entirely useless for catching such oversights.