Search code examples
c++cexceptionstack-unwindingsetjmp

C/C++ implementations where longjmp unwinds?


Are there major C/C++ implementations where the longjmp function "unwinds", i.e. where it interacts with destructors for automatic-storage objects, __attribute__((__cleanup__(...))), POSIX threads cancellation handlers, etc. rather than just restoring the register context saved by setjmp? I'm particularly interested in the existence (or non-existence) of POSIX implementations with this property, but C/C++ in general are also interesting.

For the bounty, I'm looking for a POSIX-conforming or at least POSIX-like system, as opposed to Windows which has already been mentioned.


Solution

  • Interix (SUA) by default does not call destructors, but in x86 mode, does have an option for it.

    Taking this test program, saved as test.cc:

    #include <stdio.h>
    #include <setjmp.h>
    
    struct A {
      ~A() { puts("~A"); }
    };
    
    jmp_buf buf;
    
    void f() {
      A a;
      longjmp(buf, 1);
    }
    
    int main() {
      if (setjmp (buf))
        return 0;
    
      f();
    }
    

    here's how Interix behaves. For brevity, I've omitted the required proper setup of PATH.

    $ cc -mx86 test.cc && ./a.out
    $ cc -mx86 -X /EHa test.cc && ./a.out
    cl : Command line warning D9025 : overriding '/EHs' with '/EHa'
    ~A
    $ cc -mamd64 test.cc && ./a.out
    $ cc -mamd64 -X /EHa test.cc && ./a.out
    cl : Command line warning D9025 : overriding '/EHs' with '/EHa'
    $ 
    

    The comments suggest that cc -X /EHa does not conform to POSIX, for example because /EHa would catch signals. That is not entirely true:

    $ cat test.cc
    #include <signal.h>
    int main() {
      try {
        raise(SIGFPE);
      } catch (...) {
        // ignore
      }
    }
    $ cc -mx86 -X /EHa test.cc && ./a.out
    cl : Command line warning D9025 : overriding '/EHs' with '/EHa'
    Floating point exception (core dumped)
    

    If I change raise(SIGFPE) to a division by zero, I do indeed see that the exception handler catches it, but neither POSIX nor C++ requires any particular behaviour for that, so that doesn't affect conformance. It is also not the case that all asynchronous signals are caught: for this program:

    #include <stdio.h>
    #include <stdlib.h>
    #include <signal.h>
    void sigint(int signal) {
      puts("sigint");
      exit(0);
    }
    int main() {
      signal(SIGINT, sigint);
      try {
        for (;;) ;
      } catch (...) {
        // ignore
      }
    }
    

    "sigint" is printed after Ctrl-C, as expected. I don't see a reason for claiming that this implementation fails to meet POSIX requirements.