diff --git a/examples/Jamfile.v2 b/examples/Jamfile.v2 index a2f8a091..a36feb35 100644 --- a/examples/Jamfile.v2 +++ b/examples/Jamfile.v2 @@ -37,6 +37,7 @@ exe ping_pong : ping_pong.cpp ; exe priority : priority.cpp ; exe segmented_stack : segmented_stack.cpp ; exe simple : simple.cpp ; +exe when_stuff : when_stuff.cpp ; exe asio/daytime_client : asio/daytime_client.cpp ; exe asio/daytime_client2 : asio/daytime_client2.cpp ; diff --git a/examples/when_stuff.cpp b/examples/when_stuff.cpp new file mode 100644 index 00000000..152d4e68 --- /dev/null +++ b/examples/when_stuff.cpp @@ -0,0 +1,103 @@ +// Copyright Nat Goodspeed 2015. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include // std::shared_ptr +#include +#include + +/***************************************************************************** +* Done +*****************************************************************************/ +// 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 ptr; + + void wait() + { + std::unique_lock lock(mutex); + while (! ready) + cond.wait(lock); + } + + void notify() + { + { + std::unique_lock lock(mutex); + ready = true; + } // release mutex + cond.notify_one(); + } +}; + +/***************************************************************************** +* when_any, simple completion +*****************************************************************************/ +// Degenerate case: when there are no functions to wait for, return +// immediately. +void when_any_simple_impl(Done::ptr) +{} + +// When there's at least one function to wait for, launch it and recur to +// process the rest. +template +void when_any_simple_impl(Done::ptr done, Fn && function, Fns&& ... functions) +{ + boost::fibers::fiber([done, function](){ + function(); + done->notify(); + }).detach(); + when_any_simple_impl(done, std::forward(functions)...); +} + +// interface function: instantiate Done, launch tasks, wait for Done +template < typename... Fns > +void when_any_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()); + when_any_simple_impl(done, std::forward(functions)...); + done->wait(); +} + +/***************************************************************************** +* example task functions +*****************************************************************************/ +void sleeper(const std::string& desc, int ms) +{ + std::cout << " " << desc << "() start" << std::endl; + boost::this_fiber::sleep_for(std::chrono::milliseconds(ms)); + std::cout << " " << desc << "() stop" << std::endl; +} + +/***************************************************************************** +* driving logic +*****************************************************************************/ +int main( int argc, char *argv[]) { + + std::cout << "when_any_simple() start" << std::endl; + when_any_simple([](){ sleeper("long", 150); }, + [](){ sleeper("medium", 100); }, + [](){ sleeper("short", 50); }); + std::cout << "when_any_simple() stop" << std::endl; + + // We've detached several fibers. We can't join() them; they're detached. + // We have to detach them because the whole point of when_any_simple() is + // to resume as soon as the FIRST one completes. But having those fibers + // terminate after main() exits is a bit problematic. Just give them some + // time to finish. + boost::this_fiber::sleep_for(std::chrono::milliseconds(200)); + + return EXIT_SUCCESS; +}