exit, signals and exceptions

Discussion in 'C++' started by Siemel Naran, May 12, 2004.

  1. Siemel Naran

    Siemel Naran Guest

    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++.moderated 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 "application-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(int sig)
    {
    if (global_s != -1)
    {
    closesocket(global_s);
    global_s = -1;
    }
    WSACleanup();
    if (sig) exit(sig);
    }

    class CloseSockets
    {
    public:
    CloseSockets() { WSADATA wsa; WSAStartup(MAKEWORD(1, 1), &wsa); }
    ~CloseSockets() { closesockets(0); }
    private:
    CloseSockets(const 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_error("fail socket");

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

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

    hostent * myhost = gethostbyname(myname);
    if (!myhost) throw std::runtime_error("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++.moderated. First time posters: Do this! ]
    Siemel Naran, May 12, 2004
    #1
    1. Advertising

  2. "Siemel Naran" <> writes:

    > 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?


    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: WWW: http://www.pvv.org/~markusk/ '
    )-------------------------------------------------------------------(

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
    =?iso-8859-1?q?Markus_B._Kr=FCger?=, May 12, 2004
    #2
    1. Advertising

  3. Siemel Naran

    Jeff Schwab Guest

    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++.moderated. First time posters: Do this! ]
    Jeff Schwab, May 12, 2004
    #3
  4. Siemel Naran

    Volker Glave Guest

    "Siemel Naran" <> wrote in message news:<Sxioc.81656$>...
    > 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++.moderated 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 "application-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(int sig)
    > {
    > if (global_s != -1)
    > {
    > closesocket(global_s);
    > global_s = -1;
    > }
    > WSACleanup();
    > if (sig) exit(sig);
    > }
    >
    > class CloseSockets
    > {
    > public:
    > CloseSockets() { WSADATA wsa; WSAStartup(MAKEWORD(1, 1), &wsa); }
    > ~CloseSockets() { closesockets(0); }
    > private:
    > CloseSockets(const 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_error("fail socket");
    >
    > global_s = s;
    > signal(SIGINT , &closesockets);
    > signal(SIGTERM, &closesockets);
    > signal(SIGABRT, &closesockets);
    >
    > char myname[256];
    > int m = gethostname(myname, sizeof(myname));
    >
    > hostent * myhost = gethostbyname(myname);
    > if (!myhost) throw std::runtime_error("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
    > }
    > }


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

    static bool return_from_server = false;

    extern "C" void signalhandler(int) {
    return_from_server = true;
    }

    struct WSASystem {
    WSASystem() { WSADATA wsa; WSAStartup(MAKEWORD(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_INET, 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(SIGINT, &signalhandler);
    std::signal(SIGTERM, &signalhandler);
    std::signal(SIGABRT, &signalhandler);

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

    hostent* const myhost(gethostbyname(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_server) {
    return;
    }
    /* call to accept which creates a new socket */

    for (;;) {
    if (return_from_server) {
    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++.moderated. First time posters: Do this! ]
    Volker Glave, May 13, 2004
    #4
  5. Siemel Naran wrote:
    > 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++.moderated 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).


    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 SetConsoleCtrlHandler functions instead. Note that console
    handlers run in a separate thread, unlike signal handlers.

    > 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.


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

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

    <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++.moderated. First time posters: Do this! ]
    Ben Hutchings, May 13, 2004
    #5
  6. Siemel Naran

    Siemel Naran Guest

    "Markus B. Krüger" <> wrote in message
    > "Siemel Naran" <> writes:


    > > 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.


    > 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.


    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?

    > 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.


    Sorry, I don't understand. Can you elaborate?

    > 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.


    What do you mean by "interrupted system calls"? Also, my signal.h does not
    have sigaction and SA_RESTART.

    > 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.


    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++.moderated. First time posters: Do this! ]
    Siemel Naran, May 13, 2004
    #6
  7. Siemel Naran

    Siemel Naran Guest

    "Jeff Schwab" <> wrote in message
    news:q9mdnYMdOZ3elT_dRVn-

    > 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.


    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_DETACH.)

    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::eek:perator new to get memory and store
    this memory in the registry. In FileStream::eek:perator delete we free the
    memory and remove it from the registry.

    But what if someone creates a Fuile


    class FileStream {
    public:
    class FailOpen;
    FileStream(const 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_back(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<FileStream *> s_list;
    static void cleanup() {
    for (each element in s_list) delete element;
    s_list.clear();
    }
    FileStream(const FileStream&);
    FileStream& operator=(const FileStream&);
    };

    std::atexit(&FileStream::cleanup);


    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
    Siemel Naran, May 13, 2004
    #7
  8. Siemel Naran

    Guest

    Jeff Schwab <> wrote in message
    news:<>...
    > 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.


    This isn't usually possible. You cannot call free() in a signal
    handler, for example. Nor fclose, nor I would imagine
    std::eek:fstream::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 objektorientierter Datenverarbeitung
    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++.moderated. First time posters: Do this! ]
    , May 13, 2004
    #8
  9. "Siemel Naran" <> writes:

    > "Markus B. Krüger" <> wrote in message
    >
    > > 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. [...]

    >
    > 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__); }


    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_INTERRUPTABLE {
    #define END_INTERRUPTABLE } \
    if (g_signal) throw Signal(__FILE__, __LINE__);

    and use these macros to start and end blocks that should be
    interruptable by shutdown.

    > [...] what is the deal with sig_atomic_t? What's so special about
    > it?


    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.

    > > 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.

    >
    > Sorry, I don't understand. Can you elaborate?


    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.)

    > What do you mean by "interrupted system calls"?


    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.

    > Also, my signal.h does not have sigaction and SA_RESTART.


    sigaction() is part of POSIX.1. If your platform is not POSIX.1
    compatible, this function might not be available to you.

    > Why is mixing longjmp and RAAI bad?


    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: WWW: http://www.pvv.org/~markusk/ '
    )-------------------------------------------------------------------(

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
    =?iso-8859-1?q?Markus_B._Kr=FCger?=, May 14, 2004
    #9
  10. Siemel Naran

    llewelly Guest

    "Siemel Naran" <> writes:
    [snip]
    > Why is mixing longjmp and RAAI bad?


    RAII.

    > 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?


    longjmp does not call destructors.

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
    llewelly, May 14, 2004
    #10
  11. Siemel Naran

    David Olsen Guest

    Siemel Naran wrote:
    > "Markus B. Krüger" <> wrote in message
    > > 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.

    >
    > 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.


    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<Foo> 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.

    > Perhaps one could use macros
    >
    > #define } if (g_signal) throw Signal(__FILE__, __LINE__); }


    No. You can only #define identifiers (with a few caveats and
    exceptions). You cannot #define arbitrary symbols.

    > Why is mixing longjmp and RAAI bad?


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

    --
    David Olsen



    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
    David Olsen, May 14, 2004
    #11
  12. Siemel Naran

    Siemel Naran Guest

    "Ben Hutchings" <> wrote in message
    news:slrnca5ejb.cvo.do-

    > 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.


    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++.moderated. First time posters: Do this! ]
    Siemel Naran, May 14, 2004
    #12
  13. Hi,

    Jeff Schwab wrote:
    > 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.


    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::MyArray(int size)
    : array_(new int[size]), // signal occurs after this line
    size_(size)
    {
    }
    MyArray::~MyArray()
    {
    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
    ++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++.moderated. First time posters: Do this! ]
    Ingolf Steinbach, May 14, 2004
    #13
  14. Hi,

    Siemel Naran wrote:
    > 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);
    > }


    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
    ++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++.moderated. First time posters: Do this! ]
    Ingolf Steinbach, May 17, 2004
    #14
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?Utf-8?B?U2FuZHk=?=

    Code to Exit Web App and Exit Internet Explorer

    =?Utf-8?B?U2FuZHk=?=, Aug 3, 2005, in forum: ASP .Net
    Replies:
    7
    Views:
    7,864
    =?Utf-8?B?U2FuZHk=?=
    Aug 5, 2005
  2. Joe Smith
    Replies:
    4
    Views:
    65,769
    sandeep1976
    Nov 8, 2006
  3. Siemel Naran

    exit, signals and exceptions

    Siemel Naran, May 7, 2004, in forum: C++
    Replies:
    12
    Views:
    3,982
    Paavo Helde
    May 16, 2004
  4. Jakub Moscicki
    Replies:
    2
    Views:
    538
    Jakub Moscicki
    Oct 4, 2003
  5. Replies:
    2
    Views:
    436
    Jeff Epler
    May 31, 2005
Loading...

Share This Page