简体   繁体   English

Boost不能使io_service保持运行状态的asio处理程序

[英]Boost asio handler that does not keep the io_service running

I want to add a signal handler to my boost io_service, allowing the application to shut down cleanly when the user presses Ctrl-C. 我想在boost io_service中添加一个信号处理程序,以使应用程序在用户按下Ctrl-C时完全关闭。 This is of course easily done by stopping the loop, something like this: 当然,这可以通过停止循环轻松完成,如下所示:

boost::asio::io_service service;
boost::asio::signal_set signals{ service, SIGINT, SIGTERM };

signals.async_wait(std::bind(&boost::asio::io_service::stop, &service));

This stops the loop normally, allowing the destructors to do their routine clean-up behaviour. 这通常会停止循环,从而使析构函数可以执行其常规清理行为。

The problem is, once the application runs out of work it does not stop because the signal handler still has a handler registered and thus the io_service never stops running. 问题是,一旦应用程序用完了,它就不会停止,因为信号处理程序仍然注册了一个处理程序,因此io_service永远不会停止运行。

I have not found a clean way around this. 我还没有找到一种解决方法。 I could of course do the signal handling myself and then just stop the loop, but this kind of defeats the idea of using boost (portability). 我当然可以自己处理信号,然后停止循环,但是这种方法违反了使用boost(可移植性)的想法。

In the following code, http_server has a "listening socket" to accept multiple connections. 在以下代码中, http_server具有一个“侦听套接字”以接受多个连接。 The listening socket constantly runs async_accept so the io_service never runs out of work. 侦听套接字始终运行async_accept因此io_service永远不会耗尽工作。 The http_server.shutdown() function closes the listening socket and all open connections, so the io_service has no more work and stops running: http_server.shutdown()函数关闭监听套接字和所有打开的连接,因此io_service不再工作并停止运行:

void handle_stop(ASIO_ERROR_CODE const&, // error,
                 int, // signal_number,
                 http_server_type& http_server)
{
  std::cout << "Shutting down" << std::endl;
  http_server.shutdown();
}

...

ASIO::io_service io_service;
http_server_type http_server(io_service);

...


// The signal set is used to register termination notifications
ASIO::signal_set signals_(io_service);
signals_.add(SIGINT);
signals_.add(SIGTERM);
#if defined(SIGQUIT)
signals_.add(SIGQUIT);
#endif // #if defined(SIGQUIT)

// register the handle_stop callback
signals_.async_wait([&http_server]
  (ASIO_ERROR_CODE const& error, int signal_number)
{ handle_stop(error, signal_number, http_server); });

...

io_service.run();
std::cout << "io_service.run complete, shutdown successful" << std::endl;

This method also works for thread pools, see: thread_pool_http_server.cpp 此方法也适用于线程池,请参见: thread_pool_http_server.cpp

I am probably going to hell for this, but I found a workaround to get a handler that doesn't coun't towards the number of running handlers. 为此,我可能会下地狱,但是我找到了一个变通办法,以获取一个处理程序,该处理程序与正在运行的处理程序的数量不相上下。 It seriously abuses both the work_guard boost provides, calls destructors by hand and misuses placement new, but it works. 它严重滥用了work_guard boost提供的功能,手动调用了析构函数,并且滥用了new的放置,但是它可以正常工作。

#pragma once

#include <boost/asio/io_service.hpp>
#include <utility>
#include <memory>

template <typename HANDLER>
class unwork
{
    public:
        unwork(boost::asio::io_service &service, HANDLER &&handler) :
            _work_guard(std::make_unique<boost::asio::io_service::work>(service)),
            _handler(std::forward<HANDLER>(handler))
        {
            // wait for the handler to be installed
            service.post([work_guard = _work_guard.operator->()]() {
                // remove the work guard and the handler that has now been installed
                work_guard->~work();
                work_guard->~work();
            });
        }

        unwork(const unwork &that) :
            unwork(that._work_guard->get_io_service(), that._handler)
        {}

        unwork(unwork &&that) :
            _work_guard(std::move(that._work_guard)),
            _handler(std::move(that._handler))
        {}

        ~unwork()
        {
            // was the work guard not moved out?
            if (_work_guard) {
                // add the work guard reference and the handler reference again
                new (_work_guard.operator->()) boost::asio::io_service::work{ _work_guard->get_io_service() };
                new (_work_guard.operator->()) boost::asio::io_service::work{ _work_guard->get_io_service() };
            }
        }

        template <class ...Arguments>
        auto operator()(Arguments ...parameters)
        {
            return _handler(std::forward<Arguments>(parameters)...);
        }
    private:
        std::unique_ptr<boost::asio::io_service::work>  _work_guard;
        HANDLER                                         _handler;
};

// maker function, for c++ < c++17
template <typename HANDLER>
unwork<HANDLER> make_unwork(boost::asio::io_service &service, HANDLER &&handler)
{
    // create the new unwork wrapper
    return { service, std::forward<HANDLER>(handler) };
}

It is used by wrapping your handler in a make_unwork() call if you are using c++14. 如果您使用的是c ++ 14,则通过将您的处理程序包装在make_unwork()调用中来使用。 In c++17 the constructor can be used directly. 在c ++ 17中,可以直接使用构造函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM