2011-01-09 17 views
13

Estoy escribiendo un programa de servidor multiplataforma en C++ usando Boost.Asio. Siguiendo el ejemplo del Servidor HTTP en this page, me gustaría gestionar una solicitud de terminación de usuario sin utilizar API específicas de la implementación. Inicialmente intenté usar la biblioteca de señales C estándar, pero no he podido encontrar un patrón de diseño adecuado para Asio. El diseño Windows example's parece asemejarse a la biblioteca de señal más cercana, pero hay una condición de carrera donde se podría llamar al controlador ctrl de consola después de que el objeto del servidor haya sido destruido. Estoy tratando de evitar el comportamiento indefinido según lo especificado por el estándar de C++.manera estándar de realizar un apagado limpio con Boost.Asio

¿Existe una forma estándar (y correcta) para detener el servidor?

Para ilustrar los problemas con el uso de la biblioteca señal C:

#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 ¿Estás preguntando cómo instalar un manejador de señal para una señal como SIGINT? O bien, ¿qué hacer en ese controlador de señal para apagar su servidor HTTP? –

+0

@Sam agregó un ejemplo de diseño de la firma – Timothy003

+0

@Timothy no se puede invocar '' io_service :: stop' de forma segura desde su manejador de señal, incluso si está envuelto en una función 'boost ::. Hacerlo da como resultado un comportamiento indefinido. Mire [mi respuesta] (http://stackoverflow.com/questions/4639909/standard-way-to-perform-a-clean-shutdown-with-boost-asio/4639977#4639977) para obtener una lista de funciones que puede llamar desde dentro de un manejador de señal. –

Respuesta

15

Versión 1.5.3 de Boost.Asio (Se ha de integrar en la próxima versión 1.47 impulso?) Tiene la clase signal_set:

#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)); 

EDITAR

Ahora included en la versión 1.47 Boost

+0

Interesante; Asio usa variables estáticas para acceder a los datos de un manejador de señal. ¿No es eso UB? – Timothy003

+3

[Comportamiento no definido] (http://stackoverflow.com/q/2766731/501771) – Timothy003

+0

@ Timothy003 Thx – CharlesB

5

El servidor HTTP posix example es una buena manera de forma limpia de apagado. Un hilo invoca io_service::run mientras que otro espera una señal con sigwait.

Alternativamente, puede instalar un manejador de señal pero que es un poco más complicado. Hay un very small list de funciones de seguridad de señal asíncrona que puede invocar desde un manejador de señal.

El controlador de rutina debe ser muy cuidado, ya que el procesamiento en otra parte fue interrumpido en algún arbitraria punto. POSIX tiene el concepto de "función de seguridad ". Si una señal interrumpe una función insegura y el controlador invoca una función insegura , el comportamiento es indefinido. Las funciones seguras se enumeran explícitamente en las diversas normas.

El POSIX.1-2003 lista es

_exit() _exit() abortar() aceptar() de acceso() aio_error() aio_return() aio_suspend() de alarma() bind() cfgetispeed() cfgetospeed() cfsetispeed () cfsetospeed() chdir() chmod() chown() clock_gettime() close() connect() creat() dup() dup2() execle() execve() fchmod() fchown() fcntl() fdatasync() tenedor() fpathconf() fstat() fsync() ftruncate() getegid() geteuid() getgid() getgroups() getpeername() getpgrp() getpid() getppid() getsockname() getsockopt() getuid() kill() enlace() escucha() lseek() lstat() mkdir() mkfifo() abierta() pathconf pause()() tubería() poll() posix_trace_event() pselect() aumento() read() readlink() recv() recvfrom() recvmsg() cambiar el nombre de() rmdir() select() sem_post() send() sendmsg() sendto() setgid() setpgid() setsid() setsockopt() setuid() apagado() sigaction() sigaddset() sigdelset() sigemptyset() sigfillset() sigismember() señal() sigpause() sigpending() sigprocmask() sigqueue() sigset() sigsuspend() del sueño() socket() socketpair() stat() symlink() sysconf() tcdrain() tcflow() tcflush () tcgetattr() tcgetpgrp() tcsendbreak() tcsetattr() Tiempo de tcsetpgrp()() timer_getoverrun() timer_gettime() timer_settime() veces() umask() uname() unlink() utime() esperar () waitpid() write().

+2

Encontré una lista de funciones en [esta página.] (Http://linux.die.net/man/2/signal) Sin embargo, es específico de POSIX. ¿El estándar C dice algo diferente acerca de esto? – Timothy003

+0

@Timothy ese es el enlace correcto, he editado mi respuesta. No estoy demasiado familiarizado con el estándar C, aunque nunca he tenido problemas para usar solo las funciones enumeradas en la página del hombre de la señal desde un manejador de señal. –