使用 Boost.Asio 执行干净关闭的标准方法

发布于 2024-10-11 06:17:37 字数 1792 浏览 4 评论 0原文

我正在使用 Boost.Asio 用 C++ 编写一个跨平台服务器程序。遵循此页面上的 HTTP 服务器示例, 我想在不使用特定于实现的 API 的情况下处理用户终止请求。我最初尝试使用标准 C 信号库,但一直无法找到适合 Asio 的设计模式。 Windows 示例的 设计似乎与最接近的信号库相似,但是存在一个竞争条件,即在服务器对象完成后可以调用控制台 ctrl 处理程序被摧毁了。我试图避免 C++ 标准指定的未定义行为。

是否有标准(且正确)的方法来停止服务器?

为了说明使用 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
}

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
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

愚人国度 2024-10-18 06:17:37

Boost.Asio 版本 1.5.3(将集成到即将发布的 boost 1.47 版本中?)具有 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 中

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

请远离我 2024-10-18 06:17:37

posix 示例 HTTP 服务器是干净关闭的好方法。一个线程调用 io_service::run,而另一个线程则使用 sigwait 等待信号。

或者,您可以安装信号处理程序,但它稍微棘手一些。您可以从信号中调用非常小的异步信号安全函数列表处理程序。

例程处理程序必须非常
小心,因为在别处处理
被任意打断
观点。 POSIX有“安全”的概念
功能”。如果信号中断
不安全的函数,并且处理程序调用
不安全的函数,那么行为是
不明确的。列出安全功能
明确在各种标准中。

POSIX.1-2003 列表是

_Exit() _exit() abort() 接受() access() aio_error() aio_return()
aio_suspend() 警报() 绑定()
cfgetispeed() cfgetospeed()
cfsetispeed() cfsetospeed() chdir()
chmod() chown() 时钟获取时间()
关闭()连接()创建()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() 暂停() pipeline()
poll() posix_trace_event() pselect()
提高() 读取() 读取链接() 接收()
recvfrom() recvmsg() 重命名() rmdir()
select() sem_post() 发送() sendmsg()
sendto() setgid() setpgid() setid()
setockopt() setuid() 关闭()
sigaction() sigaddset() sigdelset()
sigemptyset() sigfillset()
sigismember() 信号() sigpause()
sigpending() sigprocmask() sigqueue()
sigset() sigsuspend() 睡眠() 套接字()
socketpair() stat() 符号链接()
sysconf() tcdrain() tcflow() tcflush()
tcgetattr() tcgetpgrp() tcsendbreak()
tcsetattr() tcsetpgrp() 时间()
计时器_getoverrun() 计时器_gettime()
timer_settime() times() umask()
uname() 取消链接() utime() 等待()
waitpid() write()。

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().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文