
Gennadiy Rozental wrote:
SIGNAL catching is optional and doesn't constitute portabiltiy issues per se, whether to use it's up to you.
Out of curiousity, what is a good (portable?) way of dealing with signals in C++? C signal handling seems to force globals on you, which is a pain especially when signal handling is a late requirement. Doing a bit of online research recently I read that some people prefer to translate signals to exceptions. As a co-worker of mine has pointed out this is how python deals with it (with python exceptions that is).
From what I've read though this is not portable, and, besides, is often not the control flow I would desire.
Further reading suggested the use of singletons for registration of handlers. This (glorified global) seemed to make sense to me and I came up with the attached code for this. However, I do refer to statics here, which also may be non-portable. It does work with msvc-7.1 though. (Is it the case of multi-processors that makes this non-portable?) (Perhaps this should move to comp.lang.c++.moderated? This code is welcomed to be used by the boost community though. Distributed under boost software licence and all that...) Please feel free to critique in discussing this issue as I'm fairly inexperienced with dealing with signals outside of C. If possible I'd like to see a solution like this that works on boost's supported platforms and released as part of boost. -Ryan /// Singleton class to register and dispatch to handlers for c-style signals. /// @note Because the signal_registry::dispatch handler refers to the static /// instance of this class, which is not of type sig_atomic_t, this /// class is technically incurring undefined behavior. In practice /// compiler implementation support this action (at least better so /// that throwing an exception from the handler). /// class signal_registry : boost::noncopyable { private: typedef boost::signal<void (int)> signal_type; typedef boost::shared_ptr<signal_type> signal_pointer; typedef std::map<int, signal_pointer> signal_id_to_handler_map_type; public: /// Access to the singleton instance. /// @return The singleton instance. static signal_registry& instance() { static signal_registry meyers_singleton_instance; return meyers_singleton_instance; } /// Register a new handler to call on the given signal. /// When signaled, the handlers are called in the reverse order registered. /// @note previous handlers are not replaced. /// @param signal_id The id of the signal for which to register the handler /// (e.g. SIGINT). /// @param handler The callback for handling the given signal. It's /// argument will be the signal_id. boost::signals::connection register_handler(int signal_id, boost::function<void (int)> const& handler) { if(m_signals.count(signal_id) == 0) { std::signal(signal_id, &signal_registry::dispatch); m_signals[signal_id] = signal_pointer(new signal_type()); } return m_signals[signal_id]->connect(handler, boost::signals::at_front); } /// Dispatch the given signal to the stored handlers, if any. /// @param signal_id The id of the signal raised (e.g. SIGINT). /// @retval true If a handler was called. /// @retval false Otherwise. static void dispatch(int signal_id) { instance().dispatch_impl(signal_id); } private: void dispatch_impl(int signal_id) const { signal_id_to_handler_map_type::const_iterator handler_iter = m_signals.find(signal_id); if(handler_iter != m_signals.end() && !((handler_iter->second)->empty())) { (*(handler_iter->second))(signal_id); } } signal_id_to_handler_map_type m_signals; ~signal_registry() {} };

Am Dienstag, den 09.05.2006, 19:22 +0000 schrieb Ryan Gallagher:
Gennadiy Rozental wrote:
SIGNAL catching is optional and doesn't constitute portabiltiy issues per se, whether to use it's up to you.
Out of curiousity, what is a good (portable?) way of dealing with signals in C++? C signal handling seems to force globals on you, which is a pain especially when signal handling is a late requirement.
You might want to use a UNIX pipe for that (by calling pipe(2)). Just write(2) into a pipe (less than PIPE_BUF bytes) and select(2) on it. This is thread-safe and signal-safe and everything. Just not too speedy.
[snip]
If possible I'd like to see a solution like this that works on boost's supported platforms and released as part of boost.
Are signals sensible on Windows? I thought they were important on UNIX only. Might err though.
-Ryan
- Aristid

Aristid Breitkreuz wrote:
Am Dienstag, den 09.05.2006, 19:22 +0000 schrieb Ryan Gallagher:
Gennadiy Rozental wrote:
SIGNAL catching is optional and doesn't constitute portabiltiy issues per se, whether to use it's up to you. Out of curiousity, what is a good (portable?) way of dealing with signals in C++? C signal handling seems to force globals on you, which is a pain especially when signal handling is a late requirement.
You might want to use a UNIX pipe for that (by calling pipe(2)). Just write(2) into a pipe (less than PIPE_BUF bytes) and select(2) on it. This is thread-safe and signal-safe and everything. Just not too speedy.
I think semaphores are a widespread way to communicate from signal handlers back to a running program. They are reentrant, and their use involves less system calls than pipes, I believe. I don't think thinking of portable signal handling mechanisms is sensible, as signals themselves are (mostly) non-portable. Regards, Stefan

Am Dienstag, den 09.05.2006, 16:39 -0400 schrieb Stefan Seefeld:
Aristid Breitkreuz wrote:
Am Dienstag, den 09.05.2006, 19:22 +0000 schrieb Ryan Gallagher:
Gennadiy Rozental wrote:
SIGNAL catching is optional and doesn't constitute portabiltiy issues per se, whether to use it's up to you. Out of curiousity, what is a good (portable?) way of dealing with signals in C++? C signal handling seems to force globals on you, which is a pain especially when signal handling is a late requirement.
You might want to use a UNIX pipe for that (by calling pipe(2)). Just write(2) into a pipe (less than PIPE_BUF bytes) and select(2) on it. This is thread-safe and signal-safe and everything. Just not too speedy.
I think semaphores are a widespread way to communicate from signal handlers back to a running program. They are reentrant, and their use involves less system calls than pipes, I believe.
The huge advantage of pipes is that with select, you can poll multiple event sources at once. Multiple sem_wait must be placed into multiple threads, which is probably more overkill for those rare signals than the selfpipe-trick (as they call it).
I don't think thinking of portable signal handling mechanisms is sensible, as signals themselves are (mostly) non-portable.
In itself, such a mechanism is not too sensible.
Regards, Stefan _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Aristid Breitkreuz wrote:
The huge advantage of pipes is that with select, you can poll multiple event sources at once. Multiple sem_wait must be placed into multiple threads, which is probably more overkill for those rare signals than the selfpipe-trick (as they call it).
You are right. For event-driven programs that already run a select/poll loop, adding just another file descriptor is the best thing to do. But for all other cases the two require an equivalent efford. Regards, Stefan

Stefan Seefeld <seefeld <at> sympatico.ca> writes:
Aristid Breitkreuz wrote:
Am Dienstag, den 09.05.2006, 19:22 +0000 schrieb Ryan Gallagher:
Out of curiousity, what is a good (portable?) way of dealing with signals in C++? C signal handling seems to force globals on you, which is a pain especially when signal handling is a late requirement.
You might want to use a UNIX pipe for that (by calling pipe(2)). Just write(2) into a pipe (less than PIPE_BUF bytes) and select(2) on it. This is thread-safe and signal-safe and everything. Just not too speedy.
I think semaphores are a widespread way to communicate from signal handlers back to a running program. They are reentrant, and their use involves less system calls than pipes, I believe.
Thanks for the replies and ideas, Aristid and Stefan. I'll definitely be looking into them more when I have the chance. Actually, reading dinkumware's C++ reference on sig_atomic_t seems to suggest a semaphore-like approach. ( http://tinyurl.com/rbb8y )
I don't think thinking of portable signal handling mechanisms is sensible, as signals themselves are (mostly) non-portable.
Yes, based upon my limited research, I have to agree with you on this. I definitely agree if you replace your first "portable" with "standard-compliant". (Portable is too vague a term I guess.) It doesn't seem like there is much you can count upon accross systems. i.e. certain system calls can't be called within a handler; exceptions can't been thrown (portably); static's not of type volatile sig_atomic_t shouldn't be referenced and those of that type only with atomic operations... I do like being able to write: signal_registry::instance().register_handler(SIGINT, bind(&database_test_monitor::handle_interruption_and_exit, cref(*this), _1)); which does work with msvc-7.1 (others?). It just seems more straight-forward than polling a semaphore, especially in a program that isn't already designed with this in mind. (But I can see an argument for having these explicit signal handling points.) Anyhow, what I like doesn't make it portable. I guess I'll have to test and think about cases where this approach doesn't work before using it on those system. I can always fall back to semaphores or pipes. I'd still be interested in hearing cases where the code I've presented (with some modifications) isn't portable in practice. I don't really imagine that it would work on all systems and would be interested in learning from cases where it doesn't. (In the very least I think that this code would need a lock for the signal map to make it thread-safe.) Thanks again for the replies based upon your experiences, -Ryan

On Tue, May 09, 2006 at 10:23:53PM +0000, Ryan Gallagher wrote:
I do like being able to write:
signal_registry::instance().register_handler(SIGINT, bind(&database_test_monitor::handle_interruption_and_exit, cref(*this), _1));
My ramblings on signals are on http://libcw.sourceforge.net/kernel/signals.html Did you read the first paragraph of that (2.1)? What I try to say there is that you can't expect to DO anything when a signal occurs. At most you can increment an atomic counter, and that's it. So, if you want an interface that allows call-backs (which I assume &database_test_monitor::handle_interruption_and_exit is), then that needs to be called later, when the application returns to the main loop. As a result, you can't think about making a (portable) interface for signals unless you provide that main loop - and that means, include timing and I/O for files, sockets, pipes etc (which is what libcw is). Anything simpler will make no sense imho. -- Carlo Wood <carlo@alinoe.com>

Carlo Wood <carlo <at> alinoe.com> writes:
My ramblings on signals are on http://libcw.sourceforge.net/kernel/signals.html Did you read the first paragraph of that (2.1)?
This does look vaguely familiar. I might have ran into this in my earlier (brief) online research. It's very interesting; I'll review it in more depth soon.
What I try to say there is that you can't expect to DO anything when a signal occurs. At most you can increment an atomic counter, and that's it.
Yes this seems to basically agree with dinkumware. I don't think that this point had sunk in well enough earlier. When I worked on this, I was mainly just worried about closing a bug report on one platform. However, I always prefer something that works just as well for more platforms though. I just hadn't found anything. Googling "C++ signals" came up with many solutions (some of which are inspiration for mine) that do much more than this simple increment. Many throw exceptions or do similar operations to mine with call-backs. While not portable to all platforms, I wonder to what platforms these types of operations can or can't be performed upon. I guess the question is "how portable are they?".
So, if you want an interface that allows call-backs (which I assume &database_test_monitor::handle_interruption_and_exit is), then that needs to be called later, when the application returns to the main loop.
Good point. I'll definitely be looking closer at libcw. The call-back scheme was based upon the idea that it was up to the user to make sure those call-backs didn't do anything that their system couldn't handle. (I have to admit though that writing to a remote database as my example handler has done does seem to be a little much.) My goal for the signal_registry class itself was to violate as few portability requirements as possible (and minor ones at that). Another goal was to have the least amount to change (my application did not have a main loop and the database handle was not global). Anyhow, thanks for the reply and the link. It seems though that boost is standard compliance + workarounds for portability so having a library that's based upon the answer to "how portable are they?" doesn't make sense for it. Cheers, -Ryan
participants (4)
-
Aristid Breitkreuz
-
Carlo Wood
-
Ryan Gallagher
-
Stefan Seefeld