| Home | Libraries | People | FAQ | More

#### when_any, simple completion

The simplest case is when you only need to know that the first of a set of asynchronous tasks has completed — but you don't need to obtain a return value, and you're confident that they will not throw exceptions.

For this we introduce a `Done` class to wrap a `bool` variable with a `condition_variable` and a `mutex`:

    // Wrap canonical pattern for condition_variable + bool flag
    struct Done {
    private:
        boost::fibers::condition_variable   cond;
        boost::fibers::mutex                mutex;
        bool                                ready = false;

    public:
        typedef std::shared_ptr< Done >     ptr;

        void wait() {
            std::unique_lock< boost::fibers::mutex > lock( mutex);
            cond.wait( lock, [this](){ return ready; });
        }

        void notify() {
            {
                std::unique_lock< boost::fibers::mutex > lock( mutex);
                ready = true;
            } // release mutex
            cond.notify_one();
        }
    };

The pattern we follow throughout this section is to pass a `std::shared_ptr<>` to the relevant synchronization object to the various tasks' fiber functions. This eliminates nagging questions about the lifespan of the synchronization object relative to the last of the fibers.

`wait_first_simple()` uses that tactic for `Done`:

    template< typename ... Fns >
    void wait_first_simple( Fns && ... functions) {
        // Use shared_ptr because each function's fiber will bind it separately,
        // and we're going to return before the last of them completes.
        auto done( std::make_shared< Done >() );
        wait_first_simple_impl( done, std::forward< Fns >( functions) ... );
        done->wait();
    }

`wait_first_simple_impl()` is an ordinary recursion over the argument pack, capturing `Done::ptr` for each new fiber:

    // Degenerate case: when there are no functions to wait for, return
    // immediately.
    void wait_first_simple_impl( Done::ptr) {
    }

    // When there's at least one function to wait for, launch it and recur to
    // process the rest.
    template< typename Fn, typename ... Fns >
    void wait_first_simple_impl( Done::ptr done, Fn && function, Fns && ... functions) {
        boost::fibers::fiber( [done, function](){
                                  function();
                                  done->notify();
                              }).detach();
        wait_first_simple_impl( done, std::forward< Fns >( functions) ... );
    }

The body of the fiber's lambda is extremely simple, as promised: call the function, notify `Done` when it returns. The first fiber to do so allows `wait_first_simple()` to return — which is why it's useful to have `std::shared_ptr<Done>` manage the lifespan of our `Done` object rather than declaring it as a stack variable in `wait_first_simple()`.

This is how you might call it:

    wait_first_simple(
            [](){ sleeper("wfs_long",   150); },
            [](){ sleeper("wfs_medium", 100); },
            [](){ sleeper("wfs_short",   50); });

In this example, control resumes after `wait_first_simple()` when `sleeper("wfs_short", 50)` completes — even though the other two `sleeper()` fibers are still running.