4

The behavior of strand::wrap() is defined such that it creates a functor that will execute strand::dispatch() when invoked. I have recently come across a bug in one of our applications that performs the following sequence:

my_great_function(..., s.wrap(a), s.wrap(b));

The application guaranteed that the functor created by s.wrap(a) was invoked before s.wrap(b). However, there is a race condition such that the first functor is invoked outside the strand, and therefore deferred for invocation whereas the second functor is invoked inside the strand and executed immediately. This violated the application's ordering assumption of a before b and resulted in undefined behavior.

Using strand::post() instead of strand::dispatch() is one way to resolve this, but there's no easy way to accomplish this like using strand.wrap(). I could create helper functions to post through the strand, I'm wondering if there's an easier way?

4

1 回答 1

2

关于为什么不存在strand.wrap等价物的纯粹猜测:strand.post

  • 我找不到任何人正式提出strand.wrap()在功能请求中需要等效项的案例。
  • 基于strand.wrap()最常见的用法,它可能是strand.dispatch()为了优化组合操作的中间处理程序而实现的。用户的完成处理程序可以在满足完成条件后立即调用,而不必为延迟调用发布完成处理程序。

最简单的解决方案可能是将链传递给my_great_function旁边ab处理程序。如果my_great_function需要特定的处理程序调用顺序,那么保证顺序似乎是可以接受的,my_great_function而不是将责任转嫁给调用者,这可能会忽略必要的顺序。另一方面,如果my_great_function是相当通用的,处理程序之间需要特定的调用顺序,则考虑将处理程序一起传递到直接或间接暗示排序的结构中,例如std::tuple.

虽然这些解决方案都没有提供通用的可重用解决方案,但它可能是最简单的解决方案。官方支持的解决方案是使用自定义处理程序类型,此外还提供asio_handler_invoke通过ADL可用的函数来说明操作的中间和完成处理程序。这是一个主要基于detail/wrapped_handler.hpp的完整示例:

#include <iostream>

#include <boost/asio.hpp>

/// @brief Custom handler wrapper type that will post into its dispatcher.
template <typename Dispatcher,
          typename Handler>
class post_handler
{
public:
  typedef void result_type;

  post_handler(Dispatcher dispatcher, Handler handler)
    : dispatcher_(dispatcher),
      handler_(handler)
  {}

  void operator()()
  {
    dispatcher_.post(handler_);
  }

  template <typename Arg1>
  void operator()(Arg1 arg1)
  {
    dispatcher_.post(boost::bind(handler_, arg1));
  }

  template <typename Arg1, typename Arg2>
  void operator()(Arg1 arg1, Arg2 arg2)
  {
    dispatcher_.post(boost::bind(handler_, arg1, arg2));
  }

  Dispatcher dispatcher_;
  Handler handler_;
};

// Custom invocation hooks for post_handler.  These must be declared in 
// post_handler's associated namespace for proper resolution.

template <typename Function, typename Dispatcher, typename Handler>
inline void asio_handler_invoke(Function& function,
    post_handler<Dispatcher, Handler>* this_handler)
{
  this_handler->dispatcher_.post(
      boost::asio::detail::rewrapped_handler<Function, Handler>(
        function, this_handler->handler_));
}

template <typename Function, typename Dispatcher, typename Handler>
inline void asio_handler_invoke(const Function& function,
    post_handler<Dispatcher, Handler>* this_handler)
{
  this_handler->dispatcher_.post(
      boost::asio::detail::rewrapped_handler<Function, Handler>(
        function, this_handler->handler_));
}

/// @brief Factory function used to create handlers that post through the
///        dispatcher.
template <typename Dispatcher, typename Handler>
post_handler<Dispatcher, Handler>
wrap_post(Dispatcher dispatcher, Handler handler)
{
  return post_handler<Dispatcher, Handler>(dispatcher, handler);
}

/// @brief Convenience factory function used to wrap handlers created from
///        strand.wrap.
template <typename Dispatcher, typename Handler>
post_handler<Dispatcher, 
             boost::asio::detail::wrapped_handler<Dispatcher, Handler> >
wrap_post(boost::asio::detail::wrapped_handler<Dispatcher, Handler> handler)
{
  return wrap_post(handler.dispatcher_, handler);
}

boost::asio::io_service io_service;
boost::asio::strand strand(io_service);
boost::asio::deadline_timer timer(io_service);

void a() { std::cout << "a" << std::endl; }
void b() { std::cout << "b" << std::endl; }
void c() { std::cout << "c" << std::endl; }
void d() { std::cout << "d" << std::endl; }
void noop() {}

void my_great_function()
{
  std::cout << "++my_great_function++" << std::endl;
  // Standard dispatch.
  strand.dispatch(&a);

  // Direct wrapping.
  wrap_post(strand, &b)();

  // Convenience wrapping.
  wrap_post(strand.wrap(&c))();

  // ADL hooks.
  timer.async_wait(wrap_post(strand.wrap(boost::bind(&d))));
  timer.cancel();
  std::cout << "--my_great_function--" << std::endl;
}

int main()
{
  // Execute my_great_function not within a strand.  The noop
  // is used to force handler invocation within strand.
  io_service.post(&my_great_function);
  strand.post(&noop);
  io_service.run();
  io_service.reset();

  // Execute my_great_function within a strand.
  std::cout << std::endl;
  io_service.post(strand.wrap(&my_great_function));
  strand.post(&noop);
  io_service.run();
}

产生以下输出:

++my_great_function++
--my_great_function--
一个
b
C
d

++my_great_function++
一个
--my_great_function--
b
C
d

取决于实现细节的稍微简单的解决方案是调整detail::wrapped_handlerDispatcher类型参数。这种方法允许在 Boost.Asio 的其余部分中透明地使用wrapped_handler具有适应类型的 s 。Dispatcher

/// @brief Class used to adapter the wrapped_handler's Dispatcher type
///        requirement to post handlers instead of dispatching handlers.
template <typename Dispatcher>
struct post_adapter
{
  post_adapter(Dispatcher& dispatcher)
    : dispatcher_(dispatcher)
  {}

  template <typename Handler>
  void dispatch(const Handler& handler)
  {
    dispatcher_.post(handler);
  }

  Dispatcher dispatcher_;
};

/// @brief Factory function used to create handlers that post through an
///        adapted dispatcher.
template <typename Dispatcher, typename Handler>
boost::asio::detail::wrapped_handler<post_adapter<Dispatcher>, Handler>
wrap_post(Dispatcher& dispatcher, Handler handler)
{
  typedef post_adapter<Dispatcher> adapter_type;
  return boost::asio::detail::wrapped_handler<
    adapter_type, Handler>(adapter_type(dispatcher), handler);
}

/// @brief Convenience factory function used to wrap handlers created from
///        strand.wrap.
template <typename Dispatcher, typename Handler>
boost::asio::detail::wrapped_handler<
  post_adapter<Dispatcher>, 
  boost::asio::detail::wrapped_handler<Dispatcher, Handler> >
wrap_post(boost::asio::detail::wrapped_handler<Dispatcher, Handler> handler)
{
  return wrap_post(handler.dispatcher_, handler);
}

两种解决方案都可能在定义的处理程序调用顺序wrap_post方面引入一定程度的复杂性。

于 2013-04-30T02:16:49.173 回答