exit, signals and exceptions

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Siemel Naran

    exit, signals and exceptions

    Hi. I posted this question to comp.lang.c++, but am rephrasing it a bit
    from what I learned and posting to comp.lang.c++.m oderated for more insight.

    So how do I solve my problem? I want it so that when the user presses
    Ctrl-C or eds the task from task manager (or the kill command in UNIX) then
    the system should shutdown gracefully. This means it should call the
    destructors of all objects, which means freeing dynamic memory, closing
    sockets, closing file handles, close SQL connections, etc.

    My approach was to set up a signal handler using std::signal. If the signal
    handler does nothing the program resumes execution at the point where the
    signal was raised (kind of like the RESUME command in basic).

    So one of my thoughts was to throw an exception in the signal handler, and
    after returning control to the point in the function where the signal was
    raised, the program would detect the exception and perform stack unwinding.
    But we are not allowed to throw exceptions in signal functions. On my
    compiler, Borland C++ 6 I get the message: << Project.exe faulted with
    message "applicatio n-defined exception". Process Stopped. Use Step or Run to
    continue. >>

    My other thought was to call std::exit in the signal handler. But
    unfortunately this function not destroy local objects in all stacks.

    So what else can we do? This seems to be a common problem, and I'm sure
    people have put much thought into it. Maybe some use of longjmp could help?

    Here's my current solution, which seems to work in this special case in that
    all sockets are closed, but there is no dynamic memory to release, file
    handles to close, etc.


    int global_s = -1;

    void closesockets(in t sig)
    {
    if (global_s != -1)
    {
    closesocket(glo bal_s);
    global_s = -1;
    }
    WSACleanup();
    if (sig) exit(sig);
    }

    class CloseSockets
    {
    public:
    CloseSockets() { WSADATA wsa; WSAStartup(MAKE WORD(1, 1), &wsa); }
    ~CloseSockets() { closesockets(0) ; }
    private:
    CloseSockets(co nst CloseSockets&); // not implemented
    CloseSockets& operator=(const CloseSockets&); // not implemented
    };


    void server()
    {
    CloseSockets _closesockets;

    int s = socket(AF_INET, SOCK_STREAM, 0);
    if (s == -1) throw std::runtime_er ror("fail socket");

    global_s = s;
    signal(SIGINT , &closesocket s);
    signal(SIGTERM, &closesocket s);
    signal(SIGABRT, &closesocket s);

    char myname[256];
    int m = gethostname(myn ame, sizeof(myname)) ;

    hostent * myhost = gethostbyname(m yname);
    if (!myhost) throw std::runtime_er ror("no host");

    // call to bind
    // call to listen

    while (true)
    {
    /* call to accept which creates a new socket */

    while (true)
    {
    /* call to recv */
    }

    // call to closesocket
    }
    }


    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.m oderated. First time posters: Do this! ]
  • Markus B. Krüger

    #2
    Re: exit, signals and exceptions

    "Siemel Naran" <SiemelNaran@RE MOVE.att.net> writes:
    [color=blue]
    > I want it so that when the user presses Ctrl-C or eds the task from
    > task manager (or the kill command in UNIX) then the system should
    > shutdown gracefully. This means it should call the destructors of
    > all objects, which means freeing dynamic memory, closing sockets,
    > closing file handles, close SQL connections, etc.
    >
    > My approach was to set up a signal handler using std::signal. If
    > the signal handler does nothing the program resumes execution at the
    > point where the signal was raised (kind of like the RESUME command
    > in basic).
    >
    > So one of my thoughts was to throw an exception in the signal
    > handler, [...] But we are not allowed to throw exceptions in signal
    > functions. [...]
    >
    > My other thought was to call std::exit in the signal handler. But
    > unfortunately this function not destroy local objects in all stacks.
    >
    > So what else can we do? This seems to be a common problem, and I'm
    > sure people have put much thought into it. Maybe some use of
    > longjmp could help?[/color]

    Signal handlers need to use the POSIX functions sigsetjmp() and
    siglongjmp() instead of setjmp() and longjmp(). Even so, destructors
    for local instances won't be called. Mixing longjmp() and RAAI is
    generally a bad idea.

    One solution is to let the signal handler set a global variable
    (declared as volatile sig_atomic_t), and check this variable regularly
    inside all loops of long duration. If the variable is set, shutdown
    can be initialized by throwing an exception or stopping the loop
    normally. The disadvantage of this approach is that you have to do it
    in *every* loop that might hang for some time, e.g. socket-reading
    loops; you cannot restrict the shutdown implementation to the signal
    handler. Also, you must be able to asynchronously stop or specify
    timeouts for third-party functions that may be running when the signal
    is received, since you probably won't be able to add shutdown checks
    to loops contained in them.

    Also, some Unix systems automatically restart interrupted system calls
    if the signal handler returns. You can disable this behaviour by
    using sigaction() instead of signal() and clearing the SA_RESTART
    flag, in order to avoid that the system hangs in a restarted read()
    call it was processing when Ctrl-C was received.

    --
    ,------------------- Markus Bjartveit Krüger ---------------------.
    ' `
    ` E-mail: markusk@pvv.org WWW: http://www.pvv.org/~markusk/ '
    )-------------------------------------------------------------------(

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.m oderated. First time posters: Do this! ]

    Comment

    • Jeff Schwab

      #3
      Re: exit, signals and exceptions

      Siemel Naran wrote:

      <summary>

      When my program receives a KILL signal from the environment, how do I
      make sure all "live" objects are destructed properly?

      When the signal arrives, my handler function is entered. If I don't
      exit the program explicitly from the handler, program execution will
      resume wherever it was interrupted. I can't throw an exception from
      the handler.

      </summary>

      The most common idiom seems to be the setting of a global variable,
      which is then checked at strategic points in your program. Of course,
      this is royally painful. An alternative would be having every resource
      in the program allocated in a way that would allow explicit deallocation
      from the signal handler; for example, there could be custom allocators
      for memory, and factories for objects managing other resources.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.m oderated. First time posters: Do this! ]

      Comment

      • Volker Glave

        #4
        Re: exit, signals and exceptions

        "Siemel Naran" <SiemelNaran@RE MOVE.att.net> wrote in message news:<Sxioc.816 56$Xj6.1370054@ bgtnsc04-news.ops.worldn et.att.net>...[color=blue]
        > Hi. I posted this question to comp.lang.c++, but am rephrasing it a bit
        > from what I learned and posting to comp.lang.c++.m oderated for more insight.
        >
        > So how do I solve my problem? I want it so that when the user presses
        > Ctrl-C or eds the task from task manager (or the kill command in UNIX) then
        > the system should shutdown gracefully. This means it should call the
        > destructors of all objects, which means freeing dynamic memory, closing
        > sockets, closing file handles, close SQL connections, etc.
        >
        > My approach was to set up a signal handler using std::signal. If the signal
        > handler does nothing the program resumes execution at the point where the
        > signal was raised (kind of like the RESUME command in basic).
        >
        > So one of my thoughts was to throw an exception in the signal handler, and
        > after returning control to the point in the function where the signal was
        > raised, the program would detect the exception and perform stack unwinding.
        > But we are not allowed to throw exceptions in signal functions. On my
        > compiler, Borland C++ 6 I get the message: << Project.exe faulted with
        > message "applicatio n-defined exception". Process Stopped. Use Step or Run to
        > continue. >>
        >
        > My other thought was to call std::exit in the signal handler. But
        > unfortunately this function not destroy local objects in all stacks.
        >
        > So what else can we do? This seems to be a common problem, and I'm sure
        > people have put much thought into it. Maybe some use of longjmp could help?
        >
        > Here's my current solution, which seems to work in this special case in that
        > all sockets are closed, but there is no dynamic memory to release, file
        > handles to close, etc.
        >
        >
        > int global_s = -1;
        >
        > void closesockets(in t sig)
        > {
        > if (global_s != -1)
        > {
        > closesocket(glo bal_s);
        > global_s = -1;
        > }
        > WSACleanup();
        > if (sig) exit(sig);
        > }
        >
        > class CloseSockets
        > {
        > public:
        > CloseSockets() { WSADATA wsa; WSAStartup(MAKE WORD(1, 1), &wsa); }
        > ~CloseSockets() { closesockets(0) ; }
        > private:
        > CloseSockets(co nst CloseSockets&); // not implemented
        > CloseSockets& operator=(const CloseSockets&); // not implemented
        > };
        >
        >
        > void server()
        > {
        > CloseSockets _closesockets;
        >
        > int s = socket(AF_INET, SOCK_STREAM, 0);
        > if (s == -1) throw std::runtime_er ror("fail socket");
        >
        > global_s = s;
        > signal(SIGINT , &closesocket s);
        > signal(SIGTERM, &closesocket s);
        > signal(SIGABRT, &closesocket s);
        >
        > char myname[256];
        > int m = gethostname(myn ame, sizeof(myname)) ;
        >
        > hostent * myhost = gethostbyname(m yname);
        > if (!myhost) throw std::runtime_er ror("no host");
        >
        > // call to bind
        > // call to listen
        >
        > while (true)
        > {
        > /* call to accept which creates a new socket */
        >
        > while (true)
        > {
        > /* call to recv */
        > }
        >
        > // call to closesocket
        > }
        > }[/color]

        Maybe I miss something, but I do not see the need for
        exceptions or the need for exit().

        static bool return_from_ser ver = false;

        extern "C" void signalhandler(i nt) {
        return_from_ser ver = true;
        }

        struct WSASystem {
        WSASystem() { WSADATA wsa; WSAStartup(MAKE WORD(1, 1), &wsa); }
        ~WSASystem() { WSACleanup(); }
        };

        class Socket {
        const int x_;
        public:
        Socket(int x) : x_(x) {}
        ~Socket() { if (x_ != -1) closesocket(x_) ; }
        int c_int() const { return x_; }
        };

        void server() {
        const WSASystem wsa;

        const Socket s(socket(AF_INE T, SOCK_STREAM, 0));
        if (s.c_int() == -1) {
        /* TODO: Give notice "fail socket" to some appropriate
        information channel or up to the caller.
        Although socket() claims it to be an error, for us the
        situation is quite normal - we expect it to happen now
        and then -, it is not an error nor an exception. */
        return;
        }

        std::signal(SIG INT, &signalhandler) ;
        std::signal(SIG TERM, &signalhandler) ;
        std::signal(SIG ABRT, &signalhandler) ;

        char myname[256];
        const int m(gethostname(m yname, sizeof(myname)) );

        hostent* const myhost(gethostb yname(myname));
        if (myhost == 0) {
        /* TODO: Give notice "no host" to some appropriate
        information channel or up to the caller.
        This situation is quite normal - we expect it to happen
        now and then -, it is not an error nor an exception. */
        return;
        }

        // call to bind
        // call to listen

        for (;;) {
        if (return_from_se rver) {
        return;
        }
        /* call to accept which creates a new socket */

        for (;;) {
        if (return_from_se rver) {
        return;
        }
        /* call to recv */
        }
        }
        }

        Of course it is not complete at all (restoration of
        signal handlers missing, ...).

        Volker

        [ See http://www.gotw.ca/resources/clcm.htm for info about ]
        [ comp.lang.c++.m oderated. First time posters: Do this! ]

        Comment

        • Ben Hutchings

          #5
          Re: exit, signals and exceptions

          Siemel Naran wrote:[color=blue]
          > Hi. I posted this question to comp.lang.c++, but am rephrasing it a bit
          > from what I learned and posting to comp.lang.c++.m oderated for more insight.
          >
          > So how do I solve my problem? I want it so that when the user presses
          > Ctrl-C or eds the task from task manager (or the kill command in UNIX) then
          > the system should shutdown gracefully. This means it should call the
          > destructors of all objects, which means freeing dynamic memory, closing
          > sockets, closing file handles, close SQL connections, etc.
          >
          > My approach was to set up a signal handler using std::signal. If the signal
          > handler does nothing the program resumes execution at the point where the
          > signal was raised (kind of like the RESUME command in basic).[/color]

          std::signal is not very useful due to the implementation-defined
          behaviour when a second signal arrives, and IIRC "End Process" in
          Windows does not cause a SIGINT. Look at the POSIX sigsetaction and
          Win32 SetConsoleCtrlH andler functions instead. Note that console
          handlers run in a separate thread, unlike signal handlers.
          [color=blue]
          > So one of my thoughts was to throw an exception in the signal handler, and
          > after returning control to the point in the function where the signal was
          > raised, the program would detect the exception and perform stack unwinding.
          > But we are not allowed to throw exceptions in signal functions.[/color]

          Correct. Signals are in general *asynchronous*; they can interrupt
          anything, just like a switch to another thread.

          <snip>[color=blue]
          > My other thought was to call std::exit in the signal handler. But
          > unfortunately this function not destroy local objects in all stacks.[/color]
          <snip>

          It also results in undefined behaviour.

          However, since you're writing a network server which waits on
          accept(), there is a solution - or rather, two similar solutions for
          the two platforms I know. In the signal handler, you set a flag (of
          type volatile sig_atomic_t under Unix, or using one of the interlocked
          functions under Windows) and close() or closesocket() the listening
          socket, causing accept() to fail. In error-handling for accept() you
          check this flag (using an interlocked function under Windows). POSIX
          guarantees that close() is safe in a signal handler (usually it is a
          system call and so atomic w.r.t. signals). Under Windows I believe
          the WinSock functions are all thread-safe.

          [ See http://www.gotw.ca/resources/clcm.htm for info about ]
          [ comp.lang.c++.m oderated. First time posters: Do this! ]

          Comment

          • Siemel Naran

            #6
            Re: exit, signals and exceptions

            "Markus B. Krüger" <markusk@pvv.or g> wrote in message[color=blue]
            > "Siemel Naran" <SiemelNaran@RE MOVE.att.net> writes:[/color]
            [color=blue][color=green]
            > > I want it so that when the user presses Ctrl-C or eds the task from
            > > task manager (or the kill command in UNIX) then the system should
            > > shutdown gracefully. This means it should call the destructors of
            > > all objects, which means freeing dynamic memory, closing sockets,
            > > closing file handles, close SQL connections, etc.[/color][/color]
            [color=blue]
            > One solution is to let the signal handler set a global variable
            > (declared as volatile sig_atomic_t), and check this variable regularly
            > inside all loops of long duration. If the variable is set, shutdown
            > can be initialized by throwing an exception or stopping the loop
            > normally. The disadvantage of this approach is that you have to do it
            > in *every* loop that might hang for some time, e.g. socket-reading
            > loops; you cannot restrict the shutdown implementation to the signal
            > handler.[/color]

            Yes, this was the advice I received in my original post to comp.lang.c++.
            It's quite error-prone though because you might forget to put these checks
            as you write the code. As you write your code, you add new loops,
            re-arrange code, etc, and inserting these checks is the last thing on your
            mind.

            Perhaps one could use macros

            #define } if (g_signal) throw Signal(__FILE__ , __LINE__); }

            The above tells the pre-processor that it should replace } with a check on
            the global variable followed by the real }. But could this be a misuse of
            macros? Anyway, not sure if the macro expansion is legal -- ie. not sure if
            we can #define } or {. We could always use

            #define END if (g_signal) throw Signal(__FILE__ , __LINE__); }

            Second, what is the deal with sig_atomic_t? What's so special about it?
            [color=blue]
            > Also, you must be able to asynchronously stop or specify
            > timeouts for third-party functions that may be running when the signal
            > is received, since you probably won't be able to add shutdown checks
            > to loops contained in them.[/color]

            Sorry, I don't understand. Can you elaborate?
            [color=blue]
            > Also, some Unix systems automatically restart interrupted system calls
            > if the signal handler returns. You can disable this behaviour by
            > using sigaction() instead of signal() and clearing the SA_RESTART
            > flag, in order to avoid that the system hangs in a restarted read()
            > call it was processing when Ctrl-C was received.[/color]

            What do you mean by "interrupte d system calls"? Also, my signal.h does not
            have sigaction and SA_RESTART.
            [color=blue]
            > Signal handlers need to use the POSIX functions sigsetjmp() and
            > siglongjmp() instead of setjmp() and longjmp(). Even so, destructors
            > for local instances won't be called. Mixing longjmp() and RAAI is
            > generally a bad idea.[/color]

            Why is mixing longjmp and RAAI bad? In C++ style we usually declare
            variables as we need them, initializing them at the same time. So a call to
            longjmp may skip the initialization of objects or initialize the same object
            twice. As long as we declare all variables at the top of the function and
            then call setjmp, as in the C style, it should be fine to use longjmp. Is
            this correct?


            [ See http://www.gotw.ca/resources/clcm.htm for info about ]
            [ comp.lang.c++.m oderated. First time posters: Do this! ]

            Comment

            • Siemel Naran

              #7
              Re: exit, signals and exceptions

              "Jeff Schwab" <jeffplus@comca st.net> wrote in message
              news:q9mdnYMdOZ 3elT_dRVn-
              [color=blue]
              > The most common idiom seems to be the setting of a global variable,
              > which is then checked at strategic points in your program. Of course,
              > this is royally painful. An alternative would be having every resource
              > in the program allocated in a way that would allow explicit deallocation
              > from the signal handler; for example, there could be custom allocators
              > for memory, and factories for objects managing other resources.[/color]

              This idea of overloading new operators is very interesting. So then you'd
              store the address of the object in a global registry, and at program
              termination you free the objects in the registries? Maybe you could
              register the registries with atexit (or with an object that is registered
              with atexit). (For DLLs you could register with DLLMain where the reason is
              DLL_PROCESS_DET ACH.)

              Has anyone actually done this? What is involved, how difficult is it to
              implement, what burderns does it impose on developers using the framework,
              what are the performance costs?

              As QoI, we'd expect the compiler implementation to free all dynamic memory
              at program termination. How many actually do this? And how many close file
              handles and sockets?

              Anyway, we could for example overload global operator new and delete, and
              force users to use these functions to acquire and free memory. They
              shouldn't use std::malloc directly as this is not overloadable. Right?

              Similarly we could overload FileStream::ope rator new to get memory and store
              this memory in the registry. In FileStream::ope rator delete we free the
              memory and remove it from the registry.

              But what if someone creates a Fuile


              class FileStream {
              public:
              class FailOpen;
              FileStream(cons t char * filename) { d_file = fopen(filename) ; if (!file)
              throw FailOpen(); }
              virtual ~FileStream() { fclose(d_file); }
              static void * operator new(size_t n) {
              s_list.push_bac k(NULL); // create space in list first, as this itself
              may throw
              FileStream * ptr = std::malloc(n); // create memory
              d_list.back() = ptr; // store memory in list
              }
              static void operator delete(void * v, size_t n) {
              find 'v' in s_list;
              erase from s_list;
              }
              private:
              FILE * d_file;
              static std::list<FileS tream *> s_list;
              static void cleanup() {
              for (each element in s_list) delete element;
              s_list.clear();
              }
              FileStream(cons t FileStream&);
              FileStream& operator=(const FileStream&);
              };

              std::atexit(&Fi leStream::clean up);


              [ See http://www.gotw.ca/resources/clcm.htm for info about ]
              [ comp.lang.c++.m oderated. First time posters: Do this! ]

              Comment

              • kanze@gabi-soft.fr

                #8
                Re: exit, signals and exceptions

                Jeff Schwab <jeffplus@comca st.net> wrote in message
                news:<q9mdnYMdO Z3elT_dRVn-ug@comcast.com> ...[color=blue]
                > Siemel Naran wrote:[/color]
                [color=blue]
                > <summary>[/color]
                [color=blue]
                > When my program receives a KILL signal from the environment, how do
                > I make sure all "live" objects are destructed properly?[/color]
                [color=blue]
                > When the signal arrives, my handler function is entered. If I
                > don't exit the program explicitly from the handler, program
                > execution will resume wherever it was interrupted. I can't throw
                > an exception from the handler.[/color]
                [color=blue]
                > </summary>[/color]
                [color=blue]
                > The most common idiom seems to be the setting of a global variable,
                > which is then checked at strategic points in your program. Of course,
                > this is royally painful. An alternative would be having every
                > resource in the program allocated in a way that would allow explicit
                > deallocation from the signal handler; for example, there could be
                > custom allocators for memory, and factories for objects managing other
                > resources.[/color]

                This isn't usually possible. You cannot call free() in a signal
                handler, for example. Nor fclose, nor I would imagine
                std::ofstream:: close(). Nor exit(). According to the standard, you can
                in fact do very little. Most implementations guarantee more, but the
                examples I just gave are forbidden by Posix as well.

                In a multithreaded application under Unix, you can set things up so that
                the signal is handled by a special thread, rather than in a traditional
                signal handler. This thread can then call pthread_cancel on the other
                threads; on some implementations , at least, this does do a stack
                walkback on the thread.

                But that it is all very system specific.

                --
                James Kanze GABI Software
                Conseils en informatique orientée objet/
                Beratung in objektorientier ter Datenverarbeitu ng
                9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                [ See http://www.gotw.ca/resources/clcm.htm for info about ]
                [ comp.lang.c++.m oderated. First time posters: Do this! ]

                Comment

                • Markus B. Krüger

                  #9
                  Re: exit, signals and exceptions

                  "Siemel Naran" <SiemelNaran@RE MOVE.att.net> writes:
                  [color=blue]
                  > "Markus B. Krüger" <markusk@pvv.or g> wrote in message
                  >[color=green]
                  > > One solution is to let the signal handler set a global variable
                  > > (declared as volatile sig_atomic_t), and check this variable
                  > > regularly inside all loops of long duration. [...][/color]
                  >
                  > Yes, this was the advice I received in my original post to
                  > comp.lang.c++. It's quite error-prone though because you might
                  > forget to put these checks as you write the code. As you write your
                  > code, you add new loops, re-arrange code, etc, and inserting these
                  > checks is the last thing on your mind.
                  >
                  > Perhaps one could use macros
                  >
                  > #define } if (g_signal) throw Signal(__FILE__ , __LINE__); }[/color]

                  This makes it impossible to write loops that *shouldn't* be
                  interrupted by shutdown. Perhaps a solution would be a couple of
                  macros

                  #define BEGIN_INTERRUPT ABLE {
                  #define END_INTERRUPTAB LE } \
                  if (g_signal) throw Signal(__FILE__ , __LINE__);

                  and use these macros to start and end blocks that should be
                  interruptable by shutdown.
                  [color=blue]
                  > [...] what is the deal with sig_atomic_t? What's so special about
                  > it?[/color]

                  Since a signal might arrive while you're reading from or writing to a
                  variable, you need a type that is guaranteed to be atomic with regard
                  to signal processing, so that you do not read partially updated data.
                  [color=blue][color=green]
                  > > Also, you must be able to asynchronously stop or specify timeouts
                  > > for third-party functions that may be running when the signal is
                  > > received, since you probably won't be able to add shutdown checks
                  > > to loops contained in them.[/color]
                  >
                  > Sorry, I don't understand. Can you elaborate?[/color]

                  Let's say you're using a third party library for retrieving URLs, and
                  that you call a function getUrl(). This function would then wrap up
                  the connection to the web server and reading from the socket. If a
                  signal arrives while getUrl() is running, you need a way to make the
                  third party library aware that it should stop processing, since you
                  cannot insert the shutdown checks into the third party library
                  yourself. (Unless, of course, you have access to the source code, or
                  want to dabble with reverse engineering.)
                  [color=blue]
                  > What do you mean by "interrupte d system calls"?[/color]

                  If a signal arrives while the system is processing a system call, such
                  as connect() or read(), the signal may interrupt the system call,
                  depending on your operating system. When this happens, your operating
                  system might restart the system call, return a partial success (for
                  instance, if some data had been read by read() when the signal
                  arrived), or let the system call return an error code and set errno to
                  EINTR.
                  [color=blue]
                  > Also, my signal.h does not have sigaction and SA_RESTART.[/color]

                  sigaction() is part of POSIX.1. If your platform is not POSIX.1
                  compatible, this function might not be available to you.
                  [color=blue]
                  > Why is mixing longjmp and RAAI bad?[/color]

                  Because destructors for objects currently on the stack will not be
                  called when you longjmp(). Simple code to illustrate the problem
                  follows; note that the destructor of instance a is not called as it
                  should be.

                  #include <csetjmp>
                  #include <iostream>

                  using namespace std;

                  class A
                  {
                  public:
                  A() { cout << "A::A() called" << endl; }
                  ~A() { cout << "A::~A() called" << endl; }
                  };

                  int main(int argc, char **argv)
                  {
                  jmp_buf env;
                  if (setjmp(env)) {
                  cout << "longjmp() called, terminating" << endl;
                  return 0;
                  }

                  // The destructor of the following instance should be called,
                  // but it isn't.
                  A a;

                  longjmp(env, 1);
                  }

                  --
                  ,------------------- Markus Bjartveit Krüger ---------------------.
                  ' `
                  ` E-mail: markusk@pvv.org WWW: http://www.pvv.org/~markusk/ '
                  )-------------------------------------------------------------------(

                  [ See http://www.gotw.ca/resources/clcm.htm for info about ]
                  [ comp.lang.c++.m oderated. First time posters: Do this! ]

                  Comment

                  • llewelly

                    #10
                    Re: exit, signals and exceptions

                    "Siemel Naran" <SiemelNaran@RE MOVE.att.net> writes:
                    [snip][color=blue]
                    > Why is mixing longjmp and RAAI bad?[/color]

                    RAII.
                    [color=blue]
                    > In C++ style we usually declare
                    > variables as we need them, initializing them at the same time. So a call to
                    > longjmp may skip the initialization of objects or initialize the same object
                    > twice. As long as we declare all variables at the top of the function and
                    > then call setjmp, as in the C style, it should be fine to use longjmp. Is
                    > this correct?[/color]

                    longjmp does not call destructors.

                    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
                    [ comp.lang.c++.m oderated. First time posters: Do this! ]

                    Comment

                    • David Olsen

                      #11
                      Re: exit, signals and exceptions

                      Siemel Naran wrote:[color=blue]
                      > "Markus B. Krüger" <markusk@pvv.or g> wrote in message[color=green]
                      > > One solution is to let the signal handler set a global variable
                      > > (declared as volatile sig_atomic_t), and check this variable regularly
                      > > inside all loops of long duration.[/color]
                      >
                      > Yes, this was the advice I received in my original post to comp.lang.c++.
                      > It's quite error-prone though because you might forget to put these checks
                      > as you write the code. As you write your code, you add new loops,
                      > re-arrange code, etc, and inserting these checks is the last thing on your
                      > mind.[/color]

                      If you want to guarantee that all resources get cleaned up properly,
                      then you really don't have much choice. You can't throw an exception
                      from an asynchronous signal handler. In the worst case, you may be in
                      the middle of a function prologue or epilogue, and the stack may be in
                      an inconsistent state such that it can't be unwound. Or the signal
                      could come at just the right time to cause a resource leak. For example:
                      std::auto_ptr<F oo> p(new Foo());
                      If the signal (and the resulting exception) happen after the Foo object
                      has been allocated and constructed, but before the auto_ptr constructor
                      gets started, then the Foo object will never be destroyed and its
                      resources will be leaked.

                      You need the global variable to translate an asynchronous signal into a
                      synchronous exception.
                      [color=blue]
                      > Perhaps one could use macros
                      >
                      > #define } if (g_signal) throw Signal(__FILE__ , __LINE__); }[/color]

                      No. You can only #define identifiers (with a few caveats and
                      exceptions). You cannot #define arbitrary symbols.
                      [color=blue]
                      > Why is mixing longjmp and RAAI bad?[/color]

                      Because destructors of objects will not be called when they go out of
                      scope as the result of a longjmp call.

                      --
                      David Olsen
                      qg4h9ykc5m@yaho o.com


                      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
                      [ comp.lang.c++.m oderated. First time posters: Do this! ]

                      Comment

                      • Siemel Naran

                        #12
                        Re: exit, signals and exceptions

                        "Ben Hutchings" <do-not-spam-benh@bwsint.com > wrote in message
                        news:slrnca5ejb .cvo.do-
                        [color=blue]
                        > However, since you're writing a network server which waits on
                        > accept(), there is a solution - or rather, two similar solutions for
                        > the two platforms I know. In the signal handler, you set a flag (of
                        > type volatile sig_atomic_t under Unix, or using one of the interlocked
                        > functions under Windows) and close() or closesocket() the listening
                        > socket, causing accept() to fail. In error-handling for accept() you
                        > check this flag (using an interlocked function under Windows). POSIX
                        > guarantees that close() is safe in a signal handler (usually it is a
                        > system call and so atomic w.r.t. signals). Under Windows I believe
                        > the WinSock functions are all thread-safe.[/color]

                        Yes, I discovered this feature by accident. If you open a socket, then
                        close it, then bind it the bind fails by returning -1 -- and my original
                        code throws an exception in this case.

                        int b = bind(...);
                        if (b == -1) throw ...;

                        Similarly recv and all other functions fail by returning -1.

                        So in my signal handler it is sufficient to just close the socket!

                        I think this idea is similar to the other one of setting a global variable
                        and then testing it throughout the code, except this time the global
                        variable is the socket state.

                        So for example if we wrote a program that outputted to cout a lot, then to
                        exit gracefully we could in the signal handler set cout to the failstate and
                        set it up to throw exceptions. Then return control to the caller, and at
                        the next cout << x the system will throw. Something like this:

                        void signal_handler( iint sig) {
                        cout.exceptions (0);
                        cout.clear(ios: :failbit);
                        cout.exceptions (all the flags);
                        }


                        [ See http://www.gotw.ca/resources/clcm.htm for info about ]
                        [ comp.lang.c++.m oderated. First time posters: Do this! ]

                        Comment

                        • Ingolf Steinbach

                          #13
                          Re: exit, signals and exceptions

                          Hi,

                          Jeff Schwab wrote:[color=blue]
                          > An alternative would be having every resource
                          > in the program allocated in a way that would allow explicit deallocation
                          > from the signal handler; for example, there could be custom allocators
                          > for memory, and factories for objects managing other resources.[/color]

                          You typically cannot do fancy things within the signal
                          handler as the interrupted program might not be in a consistent
                          state when the handler is invoked. For instance, the program
                          might be in the middle of memory allocation, so the allocator
                          cannot be used. Or think of the following:

                          MyArray::MyArra y(int size)
                          : array_(new int[size]), // signal occurs after this line
                          size_(size)
                          {
                          }
                          MyArray::~MyArr ay()
                          {
                          delete [] array_;
                          }

                          If the signal occurs after array_ is initialized (array_
                          being of type int*) but before the constructor has completed,
                          you would encounter a memory leak as the destructor won't
                          be called (even if throwing an exception from the signal
                          handler were supported).

                          Kind regards
                          Ingolf
                          --

                          Ingolf Steinbach Jena-Optronik GmbH
                          ingolf.steinbac h@jena-optronik.de ++49 3641 200-147
                          PGP: 0x7B3B5661 213C 828E 0C92 16B5 05D0 4D5B A324 EC04

                          [ See http://www.gotw.ca/resources/clcm.htm for info about ]
                          [ comp.lang.c++.m oderated. First time posters: Do this! ]

                          Comment

                          • Ingolf Steinbach

                            #14
                            Re: exit, signals and exceptions

                            Hi,

                            Siemel Naran wrote:[color=blue]
                            > So for example if we wrote a program that outputted to cout a lot, then to
                            > exit gracefully we could in the signal handler set cout to the failstate
                            > and
                            > set it up to throw exceptions. Then return control to the caller, and at
                            > the next cout << x the system will throw. Something like this:
                            >
                            > void signal_handler( iint sig) {
                            > cout.exceptions (0);
                            > cout.clear(ios: :failbit);
                            > cout.exceptions (all the flags);
                            > }[/color]

                            Again, I think that this won't work reliably as none of the
                            operations are guarantieed to be atomic (and probably for
                            other reasons aswell). Think of, for instance, the clear()
                            call which has to read, modify and write the bits. The
                            program might just run cout.clear(.... ) itself when the
                            signal is raised. So the change within the signal handler
                            will probably be immediately reset by the program.

                            Kind regards
                            Ingolf
                            --

                            Ingolf Steinbach Jena-Optronik GmbH
                            ingolf.steinbac h@jena-optronik.de ++49 3641 200-147
                            PGP: 0x7B3B5661 213C 828E 0C92 16B5 05D0 4D5B A324 EC04

                            [ See http://www.gotw.ca/resources/clcm.htm for info about ]
                            [ comp.lang.c++.m oderated. First time posters: Do this! ]

                            Comment

                            Working...