如何检测handler是否是ASIO strand wrap并通过strand调用它?

时间:2016-12-12 17:36:37

标签: c++ boost-asio

如果采用某种处理程序的通用方法:

template< typename HandlerType >
void Register( HandlerType && handler )
{
    m_handler( std::forward< HandlerType >( handler ) );
}

并且该处理程序将在未来的某个时间点通过io_service调用:

void SomeEvent( )
{
    // compute someParameter

    m_IOService.post( std::bind( m_handler , someParameter ) );
}

如果Register()的来电者通过strand包裹的内容,如何检测到,如:

m_strand( m_IOService );

// ...

Register( m_strand.wrap( []( /*something*/ ){ /*...*/ } ) );

如何更改SomeEvent()以便在这种情况下通过链发布处理程序?

修改

当我问这个问题时,我没有仔细阅读io_service::strand::wrap docs的麻烦,更具体地说是:{/ p>

  

(...)给定一个带签名的函数对象:

R f(A1 a1, ... An an);
     

如果将此函数对象传递给wrap函数,如下所示:

strand.wrap(f);
     

然后返回值是具有签名

的函数对象
void g(A1 a1, ... An an);
     

当被调用时,执行相当于:

的代码      

strand.dispatch(boost :: bind(f,a1,... an));

我所需要的只是这个 - 我可以将m_handler声明为适当的std::function<>,只需将其发布到io_service中的SomeEvent()

我在阅读了@Arunmu的答案后意识到这一点,因此我接受了它。不过@Richard Hodges&#39;答案对ASIO的执行器逻辑及其在独立版本中的改进方式有一些好处。

2 个答案:

答案 0 :(得分:2)

对于提升 asio我认为这个模板函数的答案是:

namespace boost_asio_handler_cont_helpers {

template <typename Context>
inline bool is_continuation(Context& context)
{
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
  return false;
#else
  using boost::asio::asio_handler_is_continuation;
  return asio_handler_is_continuation(
      boost::asio::detail::addressof(context));
#endif
}

} // namespace boost_asio_handler_cont_helpers

如果我正确读取它,则用于检测是否存在要执行处理程序的“上下文”(即strandio_service)。

然后,reactor服务中的代码根据结果进行切换,或者在已有的上下文中执行。

独立中,asio的情况有所改变。

现在有一个函数可以检测处理程序的上下文(如果有的话)。我在咨询了作者后写了这段代码。

相关的行是:

auto ex = asio::get_associated_executor(handler, this->get_io_service().get_executor());

和..

            asio::dispatch(ex, [handler = std::move(handler), future = std::move(future)]() mutable
                 {
// call the user-supplied handler
        });

这是来自“长时间运行任务”执行服务的生产代码:

    template<class Task, class Handler>
    void async_execute(implementation& impl, Task&& task, Handler&& handler)
    {
        VALUE_DEBUG_TRACE(module) << method(__func__, this);

        using task_type = std::decay_t<Task>;
        static_assert(is_callable_t<task_type, long_running_task_context>(), "");
        using result_type = std::result_of_t<task_type(long_running_task_context)>;
        using promise_type = std::promise<result_type>;
        using future_type = std::future<result_type>;
        using handler_type = std::decay_t<Handler>;
        static_assert(is_callable_t<handler_type, future_type>(), "");
        using handler_result_type = std::result_of<handler_type(future_type)>;

        auto ex = asio::get_associated_executor(handler, this->get_io_service().get_executor());

        if (not impl)
        {
            post(ex, [handler = std::forward<Handler>(handler)]() mutable
                 {
                     promise_type promise;
                     promise.set_exception(std::make_exception_ptr(system_error(errors::null_handle)));
                     handler(promise.get_future());
                 });
            return;
        }

        auto handler_work = make_work(ex);
        auto& ios = get_io_service();
        auto impl_ptr = impl.get();
        auto async_handler = [this,
                              &ios,
                              impl_ptr,
                              handler_work, ex,
                              handler = std::forward<Handler>(handler)]
        (detail::long_running_task_op::identifier ident,
         auto future) mutable
        {
            assert(impl_ptr);
            VALUE_DEBUG_TRACE(module) << method("async_execute::async_handler", this, ident);
            asio::dispatch(ex, [handler = std::move(handler), future = std::move(future)]() mutable
                 {
                     VALUE_DEBUG_TRACE(module) << method("async_execute::completion_handler");
                     handler(std::move(future));
                 });
            assert(impl_ptr);
            impl_ptr->remove_op(ident);
        };

        using async_handler_type = decltype(async_handler);
        static_assert(is_callable_t<async_handler_type, detail::long_running_task_op::identifier, future_type>(), "");




        auto op = detail::long_running_task_op(std::forward<Task>(task), std::move(async_handler));
        auto ident = op.get_identifier();
        impl->add_op(ident);

        auto lock = lock_type(this->_queue_mutex);
        _ops.emplace(ident, op);
        lock.unlock();

        this->post_execute();
    }

答案 1 :(得分:1)

如果我清楚地理解了您的要求,如果实施如下所示,您不必做任何事情(阅读代码中的注释以获得解释):

   #include <iostream>
    #include <type_traits>
    #include <thread>
    #include <memory>
    #include <asio.hpp>

    template <typename Handler>
    class GenHandler
    {
    public:
      GenHandler(Handler&& h): hndler_(std::forward<Handler>(h))
      {}

      template <typename... Args>
      void operator()(Args&&... args)
      {
        std::cout << "GenHandler called" << std::endl;
        hndler_();
      }
    private:
      Handler hndler_;
    };

    template<typename HandlerType>
    GenHandler<std::decay_t<HandlerType>> create_handler(HandlerType&& h)
    {
      return {std::forward<HandlerType>(h)};
    }

    template <typename Handler>
    void SomeEvent(asio::io_service& ios, Handler& h)
    {
      ios.post([=] ()mutable { h(); });
    }

    int main() {
      asio::io_service ios;
      asio::io_service::strand strand{ios};
      auto work = std::make_unique<asio::io_service::work>(ios);
      std::thread t([&]() { ios.run(); });

      // This creates a regular handler which when called by the 
      // io_context would first execute GenHandler::operator()
      // and inside of which it would call the lambda passed below.
      auto hndl = create_handler([] { 
                        std::cout << "Regular Handle" << std::endl; 
                   });
      SomeEvent(ios, hndl);

      ///-------- Example 2 ---------////

      // This creates a handler just like above, but instead wraps a
      // strand handler i.e when GenHandler::operator() gets called
      // it will execute the lambda passed to the wrap in the execution context
      // of the strand.
      auto hndl2 = create_handler(
                       strand.wrap([] { 
                          std::cout << "Strand handler-depth 2" << std::endl; 
                       }));

      // This is a regular strand wrap which is passed to the 
      // io_service execution context. The lambda passed in the strand::wrap
      // would be excuted the execution context of the strand.
      auto str_handler = strand.wrap([=]() mutable { 
                            std::cout <<"strand\n"; 
                            hndl2();  
                          });
      SomeEvent(ios, str_handler);
      work.reset();

      t.join();
      return 0;
    }

在第二个示例中,处理程序按以下顺序调用:

  1. io_service已通过strand::wrapped_handler。因此,wrapped_handler持有的处理程序在strand内执行。
  2. {li> hndl2 GenHandler持有另一个strand::wrapped_handler也称为内部。
  3. 调用GenHandler::operator()时,它也会执行保留的strand::wrapped_handler。这是通过将strand::wrapped_handler持有的内部处理程序分派给strand。
  4. 来完成的

    注意:由于我不熟悉的原因strand::wrap已被弃用。作者希望人们使用bind_executor代替。