Стандартный способ выполнить чистое завершение работы с Boost.Asio

Я пишу кросс-платформенную серверную программу на С++, используя Boost.Asio. Следуя примеру HTTP-сервера на этой страницы, Я бы хотел обработать запрос на завершение работы пользователя без использования API-интерфейсов для реализации, Сначала я попытался использовать стандартную библиотеку сигналов C, но не смог найти шаблон дизайна, подходящий для Asio. Пример проекта в стиле Windows, похоже, напоминает библиотека сигналов ближе всего, но есть условие гонки, в котором консольный обработчик ctrl может быть вызван после уничтожения объекта сервера. Я пытаюсь избежать поведения undefined, как указано в стандарте С++.

Есть ли стандартный (и правильный) способ остановить сервер?

Чтобы проиллюстрировать проблемы с использованием библиотеки сигналов 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
}

Ответ 1

Версия 1.5.3 Boost.Asio(для интеграции в предстоящую версию 1.47 release?) имеет класс 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));

ИЗМЕНИТЬ

Теперь включен в Boost версии 1.47

Ответ 2

пример posix HTTP-сервер - хороший способ для чистого отключения. Один поток вызывает io_service::run, в то время как другой ожидает сигнал с sigwait.

В качестве альтернативы вы можете установить обработчик сигналов, но это немного сложнее. Там очень маленький список для функций с безопасным сигналом, который вы можете вызывать из обработчика сигнала.

Обработчик процедур должен быть очень осторожно, поскольку обработка в другом месте был прерван на произвольном точка. POSIX имеет понятие "безопасный функции". Если сигнал прерывает небезопасная функция, а обработчик вызывает небезопасной функции, тогда поведение undefined. Включены безопасные функции явно в различных стандартах.

Список POSIX.1-2003 -

_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() ссылка() 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() setockopt() 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().