Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

when_any / when_all functionality

when_any
when_any, simple completion
when_any, return value
when_any, produce first outcome, whether result or exception
when_any, produce first success
when_any, heterogeneous types
when_any, a dubious alternative
when_all functionality
when_all, simple completion
when_all, return values
when_all until first exception
wait_all, collecting all exceptions
when_all, heterogeneous types

Overview

A bit of wisdom from the early days of computing still holds true today: prefer to model program state using the instruction pointer rather than with Boolean flags. In other words, if the program must do something and then do something almost the same, but with minor changes... perhaps parts of that something should be broken out as smaller separate functions, rather than introducing flags to alter the internal behavior of a monolithic function.

To that we would add: prefer to describe control flow using C++ native constructs such as function calls, if, while, for, do et al. rather than as chains of callbacks.

One of the great strengths of Boost.Fiber is the flexibility it confers on the coder to restructure an application from chains of callbacks to straightforward C++ statement sequence, even when code in that fiber is in fact interleaved with code running in other fibers.

There has been much recent discussion about the benefits of when_any and when_all functionality. When dealing with asynchronous and possibly unreliable services, these are valuable idioms. But of course when_any and when_all are closely tied to the use of chains of callbacks.

This section presents recipes for achieving the same ends, in the context of a fiber that wants to do something when one or more other independent activities have completed. Accordingly, these are wait_something() functions rather than when_something() functions. The expectation is that the calling fiber asks to launch those independent activities, then waits for them, then sequentially proceeds with whatever processing depends on those results.

The function names shown (e.g. wait_first_simple()) are for illustrative purposes only, because all these functions have been bundled into a single source file. Presumably, if (say) wait_first_success() best suits your application needs, you could introduce that variant with the name wait_any().

[Note] Note

The functions presented in this section accept variadic argument lists of task functions. Corresponding wait_something() functions accepting a container of task functions are left as an exercise for the interested reader. Those should actually be simpler. Most of the complexity would arise from overloading the same name for both purposes.

All the source code for this section is found in wait_stuff.cpp.

Example Task Function

We found it convenient to model an asynchronous task using this function:

template< typename T >
T sleeper_impl( T item, int ms, bool thrw = false) {
    std::ostringstream descb, funcb;
    descb << item;
    std::string desc( descb.str() );
    funcb << "  sleeper(" << item << ")";
    Verbose v( funcb.str() );

    boost::this_fiber::sleep_for( std::chrono::milliseconds( ms) );
    if ( thrw) {
        throw std::runtime_error( desc);
    }
    return item;
}

with type-specific sleeper() front ends for std::string, double and int.

Verbose simply prints a message to std::cout on construction and destruction.

Basically:

  1. sleeper() prints a start message;
  2. sleeps for the specified number of milliseconds;
  3. if thrw is passed as true, throws a string description of the passed item;
  4. else returns the passed item.
  5. On the way out, sleeper() produces a stop message.

This function will feature in the example calls to the various functions presented below.


PrevUpHomeNext