2011-01-09 5 views
13

Ich schreibe ein Cross-Plattform-Server-Programm in C++ mit Boost.Asio. Nach dem HTTP-Server-Beispiel unter this page, möchte ich eine Benutzerbeendigungsanforderung behandeln, ohne implementierungsspezifische APIs zu verwenden. Ich habe ursprünglich versucht, die Standard-C-Signalbibliothek zu verwenden, konnte aber kein für Asio geeignetes Designmuster finden. Das Windows example's Design scheint der am nächsten gelegenen Signalbibliothek zu ähneln, aber es gibt eine Race-Bedingung, bei der der Console-Ctrl-Handler aufgerufen werden konnte, nachdem das Server-Objekt zerstört wurde. Ich versuche, undefiniertes Verhalten zu vermeiden, wie es der C++ Standard vorgibt.Standard Weg, um ein sauberes Herunterfahren mit Boost.Asio

Gibt es eine standardmäßige (und korrekte) Möglichkeit, den Server zu stoppen?

Probleme zu veranschaulichen mit der C-Signal-Bibliothek:

#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 
} 
+0

@Timothy fragen Sie, wie Sie einen Signal-Handler für ein Signal wie SIGINT installieren? Oder, was in diesem Signalhandler zu tun, um Ihren HTTP-Server herunterzufahren? –

+0

@Sam hinzugefügt ein csignal Design-Beispiel – Timothy003

+0

@Timothy Sie können 'io_service :: stop' aus Ihrem Signal-Handler nicht sicher aufrufen, selbst wenn es in eine' boost :: -Funktion eingepackt ist. Dies führt zu undefiniertem Verhalten. Sehen Sie [meine Antwort] (http://stackoverflow.com/questions/4639909/standard-way-to-perform-a-clean-shutdown-with-boost-asio/4639977#4639977) für eine Liste von Funktionen, die Sie können Aufruf von innerhalb eines Signalhandlers. –

Antwort

15

Version 1.5.3 von Boost.Asio (in der kommenden Boost 1.47 Release integriert werden?) Die signal_set Klasse:

#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

Jetzt included in Boost-Version 1.47

+0

Interessant; Asio verwendet statische Variablen, um auf Daten von einem Signal-Handler zuzugreifen. Ist das nicht UB? – Timothy003

+3

[Undefiniertes Verhalten] (http://stackoverflow.com/q/2766731/501771) – Timothy003

+0

@ Timothy003 Thx – CharlesB

5

Der posix example HTTP-Server sauber Abschaltung ist ein guter Weg. Ein Thread ruft io_service::run auf, während ein anderer auf ein Signal mit sigwait wartet.

Alternativ können Sie einen Signal-Handler installieren, aber das ist etwas kniffliger. Es gibt eine very small list async-signalsichere Funktionen, die Sie aus einem Signal-Handler aufrufen können.

muss die Routine Handler vorsichtig, sehr, da an anderer Stelle der Verarbeitung an einem beliebigen Punkt unterbrochen wurde. POSIX hat das Konzept der "sicheren Funktion". Wenn ein Signal eine unsichere Funktion unterbricht und der Handler eine unsichere Funktion aufruft, ist das Verhalten undefined. Sichere Funktionen sind in den verschiedenen Normen explizit aufgeführt .

Das POSIX.1-2003 Liste ist

_Exit() _exit() abort() accept() den Zugang() aio_error() aio_return() aio_suspend() Alarm() bind() cfgetispeed() cfgetospeed() cfsetispeed () cfsetospeed() chdir() chmod() chown() uhr_gettime() schließen() 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() -Rohr() poll() posix_trace_event() pSelect() erhöhung() read() Readlink() recv() recvfrom() recvmsg() Umbenennen() 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() socket() stat() symlink() sysconf() tcdrain() tcflow() tcflush()) tcgetattr() tcgetpgrp() tcsendbreak() tcsetattr() tcsetpgrp() Zeit() timer_getoverrun() timer_gettime() timer_settime() mal() Umask() uname() unlink() utime() Warten () waitpid() write().

+2

Ich habe eine Liste von Funktionen auf [dieser Seite gefunden.] (Http://linux.die.net/man/2/signal) Es ist jedoch POSIX-spezifisch. Sagt der C-Standard etwas anderes über dieses Zeug? – Timothy003

+0

@Timothy das ist der richtige Link, ich habe meine Antwort bearbeitet. Ich bin mit dem C-Standard nicht allzu vertraut, obwohl ich nie Probleme hatte, nur die Funktionen zu verwenden, die auf der Signalman-Seite in einem Signal-Handler aufgelistet sind. –