5

Since latest versions of boost, asio comes up with its new executors and provides with asio::strand<Executor>. So it is now perfectly possible to use asio::strand<asio::io_context::executor_type> instead of io_context::strand. But they cannot be used interchangeably.

  • Could someone explain what is the difference in usage with examples?
  • Their advantage/inconvenient compared to the other?
ropieur
  • 120
  • 1
  • 7

1 Answers1

4

io_context::strand is "legacy". I assumed it exists for interface compatibility with code that still uses boost::asio::io_service (also deprecated).

As the comments reflect I've since found out that io_context::strand is not actually deprecated, although I see no reason why this is the case, and close reading of the implementation leaves me with the conclusion that

  • asio::strand<Executor> is strictly better

  • mixing both strand services is not the best idea. In fact both services are documented with the same tag line:

     // Default service implementation for a strand.
    

    I can't help feeling there should be only one default :)

Modern strands don't reference an execution context, but wrap an executor.

While being technically different, it is conceptually the same.

The usage is the same for posting tasks:

post(s, task); // where s is either legacy or modern 
defer(s, task);
dispatch(s, task);

In fact you may have tasks with an associated executor, see:

You can no longer use the legacy strand to construct IO service objects (like tcp::socket or steady_timer). That's because the legacy strand cannot be type-erased into any_io_executor:

using tcp = boost::asio::ip::tcp;
boost::asio::io_context ioc;

auto modern = make_strand(ioc.get_executor());
tcp::socket sock(modern);

boost::asio::io_context::strand legacy(ioc);
tcp::socket sock(legacy); // COMPILE ERROR

If you really want you can force it by not using any_io_executor:

boost::asio::basic_stream_socket<tcp, decltype(legacy)> sock(legacy);
sock.connect({{}, 8989});
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Fixed a few accuracy issues and added relevant examples. – sehe Mar 24 '21 at 00:07
  • Hi @sehe, thank you for your clear answer. Just cross checked again the very latest ASIO documentation (both boost 1.75 and standalone 1.18.1), I don't see io_context::strand as deprecated. Anyway, good to know that I'd better go for strand version. – ropieur Mar 24 '21 at 07:29
  • Oh my. I keep thinking I know Asio. And then I'm proving myself wrong. I'm surprised that inner type `strand` isn't deprecated like innert type `work` (`make_strand` and `strand` seemed similar to `make_work_guard` and `executor_work_guard` so I connected them in my mind). Surprisingly the legacy strand (I'll keep referring to it as that) has it's own _separate_ IO service implementation backing it (`strand_service`), which 99% duplicates the newer `strand_executor_service`. The latter is strictly more generic and flexible (w.r.t composed/modified executors e.g.) – sehe Mar 24 '21 at 12:21
  • 1
    The only functional difference I see is that one can no longer force a different number of strand "buckets" (~the number of unique mutex instances) (using `BOOST_ASIO_STRAND_IMPLEMENTATIONS` as [explained in this easily underrated answer](https://stackoverflow.com/a/38669846/85371) by @TannerSansbury. And note that effectively, when using both strand services, you're effectively doubling the number of strand implementations. I find it surprising that these both exist without one being deprecated. – sehe Mar 24 '21 at 12:26
  • (Fixed my mistaken claims in the answer text) – sehe Mar 24 '21 at 12:31
  • Well, when I read all your contributions in SO, for sure you know asio better than me, and your support is much valuable and appreciated!! – ropieur Mar 25 '21 at 13:22