Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > exit, signals and exceptions

Reply
Thread Tools

exit, signals and exceptions

 
 
Siemel Naran
Guest
Posts: n/a
 
      05-12-2004
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! ]
 
Reply With Quote
 
 
 
 
=?iso-8859-1?q?Markus_B._Kr=FCger?=
Guest
Posts: n/a
 
      05-12-2004
"Siemel Naran" <(E-Mail Removed)> 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: http://www.velocityreviews.com/forums/(E-Mail Removed) 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! ]
 
Reply With Quote
 
 
 
 
Jeff Schwab
Guest
Posts: n/a
 
      05-12-2004
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! ]
 
Reply With Quote
 
Volker Glave
Guest
Posts: n/a
 
      05-13-2004
"Siemel Naran" <(E-Mail Removed)> wrote in message news:<Sxioc.81656$(E-Mail Removed)>...
> 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! ]
 
Reply With Quote
 
Ben Hutchings
Guest
Posts: n/a
 
      05-13-2004
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! ]
 
Reply With Quote
 
Siemel Naran
Guest
Posts: n/a
 
      05-13-2004
"Markus B. Krüger" <(E-Mail Removed)> wrote in message
> "Siemel Naran" <(E-Mail Removed)> 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! ]
 
Reply With Quote
 
Siemel Naran
Guest
Posts: n/a
 
      05-13-2004
"Jeff Schwab" <(E-Mail Removed)> 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:perator new to get memory and store
this memory in the registry. In FileStream: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! ]
 
Reply With Quote
 
kanze@gabi-soft.fr
Guest
Posts: n/a
 
      05-13-2004
Jeff Schwab <(E-Mail Removed)> wrote in message
news:<(E-Mail Removed)>...
> 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: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! ]
 
Reply With Quote
 
=?iso-8859-1?q?Markus_B._Kr=FCger?=
Guest
Posts: n/a
 
      05-13-2004
"Siemel Naran" <(E-Mail Removed)> writes:

> "Markus B. Krüger" <(E-Mail Removed)> 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: (E-Mail Removed) 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! ]
 
Reply With Quote
 
llewelly
Guest
Posts: n/a
 
      05-14-2004
"Siemel Naran" <(E-Mail Removed)> 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! ]
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Exceptions + Performance on path without exceptions gratch06@gmail.com C++ 3 04-16-2007 08:52 PM
Checked exceptions vs unchecked exceptions Ahmed Moustafa Java 5 07-14-2004 01:46 PM
exit, signals and exceptions Siemel Naran C++ 12 05-16-2004 10:28 AM
Custom exceptions -- inherit from exceptions.Exception? Paul Miller Python 3 11-12-2003 09:24 AM
python2.2: signals and exceptions: interrupted system call Jakub Moscicki Python 2 10-04-2003 10:16 AM



Advertisments