I'm writing a cross-platform server program in C++ using Boost.Asio. Following the HTTP Server example on this page, I'd like to handle a user termination request without using implementation-specific APIs. I've initially attempted to use the standard C signal library, but have been unable to find a design pattern suitable for Asio. The Windows example's design seems to resemble the signal library closest, but there's a race condition where the console ctrl handler could be called after the server object has been destroyed. I'm trying to avoid undefined behavior as specified by the C++ standard.
Is there a standard (and correct) way to stop the server?
To illustrate problems with using the C signal library:
#include <csignal>
#include <functional>
#include <boost/asio.hpp>
using std::signal;
using boost::asio::io_service;
namespace
{
std::function<void ()> sighandler;
}
extern "C"
{
static void handle_signal(int);
}
void handle_signal(int)
{
// error - undefined behavior
sighandler();
}
int main()
{
io_service s;
sighandler = std::bind(&io_service::stop, &s);
auto old_sigint = signal(SIGINT, &handle_signal);
if (old_sigint == SIG_IGN)
// race condition? raise SIGINT before I can set ignore back
signal(SIGINT, SIG_IGN);
auto old_sigterm = signal(SIGTERM, &handle_signal);
if (old_sigterm == SIG_IGN)
// race condition? raise SIGTERM before I can set ignore back
signal(SIGTERM, SIG_IGN);
s.run();
// reset signals so I can clear reference to io_service
if (old_sigterm != SIG_IGN)
signal(SIGTERM, SIG_DFL);
if (old_sigint != SIG_IGN)
signal(SIGINT, SIG_DFL);
// clear reference to io_service, but can I be sure that handle_signal
// isn't being executed?
sighandler = nullptr;
// io_service is destroyed
}
Version 1.5.3 of Boost.Asio (to be integrated in upcoming boost 1.47 release?) has the signal_set
class:
#include <boost/asio/signal_set.hpp>
// Register signal handlers so that the daemon may be shut down. You may
// also want to register for other signals, such as SIGHUP to trigger a
// re-read of a configuration file.
boost::asio::signal_set signals(io_service, SIGINT, SIGTERM);
signals.async_wait(
boost::bind(&boost::asio::io_service::stop, &io_service));
EDIT
Now included in Boost version 1.47
The posix example HTTP server is a good way to cleanly shutdown. One thread invokes io_service::run
while another waits for a signal with sigwait
.
Alternatively, you can install a signal handler but that it slightly trickier. There's a very small list of async-signal-safe functions you can invoke from within a signal handler.
The routine handler must be very
careful, since processing elsewhere
was interrupted at some arbitrary
point. POSIX has the concept of "safe
function". If a signal interrupts an
unsafe function, and handler calls an
unsafe function, then the behavior is
undefined. Safe functions are listed
explicitly in the various standards.
The POSIX.1-2003 list is
_Exit() _exit() abort() accept() access() aio_error() aio_return()
aio_suspend() alarm() bind()
cfgetispeed() cfgetospeed()
cfsetispeed() cfsetospeed() chdir()
chmod() chown() clock_gettime()
close() connect() creat() dup() dup2()
execle() execve() fchmod() fchown()
fcntl() fdatasync() fork() fpathconf()
fstat() fsync() ftruncate() getegid()
geteuid() getgid() getgroups()
getpeername() getpgrp() getpid()
getppid() getsockname() getsockopt()
getuid() kill() link() listen()
lseek() lstat() mkdir() mkfifo()
open() pathconf() pause() pipe()
poll() posix_trace_event() pselect()
raise() read() readlink() recv()
recvfrom() recvmsg() rename() rmdir()
select() sem_post() send() sendmsg()
sendto() setgid() setpgid() setsid()
setsockopt() setuid() shutdown()
sigaction() sigaddset() sigdelset()
sigemptyset() sigfillset()
sigismember() signal() sigpause()
sigpending() sigprocmask() sigqueue()
sigset() sigsuspend() sleep() socket()
socketpair() stat() symlink()
sysconf() tcdrain() tcflow() tcflush()
tcgetattr() tcgetpgrp() tcsendbreak()
tcsetattr() tcsetpgrp() time()
timer_getoverrun() timer_gettime()
timer_settime() times() umask()
uname() unlink() utime() wait()
waitpid() write().