3

The code included in this question shows a chained set of callbacks using boost::asio. In this case the code is very clear because the length of the chain is short.

In real world applications the chain of callbacks is often much longer, and must include callbacks for timeouts and code to handle errors or badly formatted messages. This quickly becomes very complex, analogous to a 1960s design with too many gotos.

Some of the complexity can be factored out by splitting the code into layers, but since the design is asynchronous, a chain of callbacks of some sort is inevitable somewhere.

Or is it? Are there ways of reducing the complexity of an asynchronous design? (Obviously in some cases using threads would help, but I'm looking for a single-threaded solution.)

Community
  • 1
  • 1
Simon Elliott
  • 2,087
  • 4
  • 30
  • 39

1 Answers1

3

The typical response to that would be to use co-routines.

Boost Asio has two flavours of them:

  • Stackless Coroutines

    These are entirely cooperative, don't allow switching stackes. Instead they employ an ingenious hack with switch statements and a handful macros (yield, reenter, fork).

    A downside to this is that coroutines are functors in this design, and the functor needs to be copyable. This invites choices involving shared_ptrs just in order to make it convenient.

    Shared pointers come with their own performance overhead, which may or may not affect your application.

  • Stackful Coroutines

    These are still cooperative, but they leverage Boost Context (through the Boost Coroutine library) to actually switch stacks. This removes quite a bit of the red-tape involved mentioned before, but introduces another other trade-offs:

    • it might introduce a slight inefficiency; compared to "flat" asynchrony on a single thread, it does introduce context switching, which ironically makes it resemble multi-threading, albeit without the threads
    • it introduces dependency on non-header only libraries Boost Context and Boost Coroutines, which are not supported across all the target platforms that Boost libraries supports

    Stackful coroutines are typically initiated using boost::asio::spawn

I like to think of Stackful Coroutines as a cooperative multi-tasking abstraction that can run inside a full threading eco-system supplied by the OS.

Boost Asio features samples of both styles of Coroutines

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks for such a complete answer. That fits the bill nicely. I've used coroutines in other languages but haven't yet in c++, and I wasn't aware of the boost implementation. – Simon Elliott Sep 24 '14 at 16:57