|
Boost-Commit : |
Subject: [Boost-commit] svn:boost r57064 - in trunk: boost boost/thread libs/thread/doc libs/thread/test
From: anthony_at_[hidden]
Date: 2009-10-22 05:33:23
Author: anthonyw
Date: 2009-10-22 05:33:21 EDT (Thu, 22 Oct 2009)
New Revision: 57064
URL: http://svn.boost.org/trac/boost/changeset/57064
Log:
Added futures to boost.thread
Added:
trunk/boost/thread/future.hpp (contents, props changed)
trunk/libs/thread/doc/future_ref.qbk (contents, props changed)
trunk/libs/thread/doc/futures.qbk (contents, props changed)
trunk/libs/thread/test/test_futures.cpp (contents, props changed)
Text files modified:
trunk/boost/thread.hpp | 3 ++-
trunk/libs/thread/doc/changes.qbk | 6 +++++-
trunk/libs/thread/doc/thread.qbk | 1 +
trunk/libs/thread/test/Jamfile.v2 | 1 +
4 files changed, 9 insertions(+), 2 deletions(-)
Modified: trunk/boost/thread.hpp
==============================================================================
--- trunk/boost/thread.hpp (original)
+++ trunk/boost/thread.hpp 2009-10-22 05:33:21 EDT (Thu, 22 Oct 2009)
@@ -1,6 +1,6 @@
// Copyright (C) 2001-2003
// William E. Kempf
-// (C) Copyright 2008 Anthony Williams
+// (C) Copyright 2008-9 Anthony Williams
//
// 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)
@@ -21,5 +21,6 @@
#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>
#include <boost/thread/barrier.hpp>
+#include <boost/thread/future.hpp>
#endif
Added: trunk/boost/thread/future.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/thread/future.hpp 2009-10-22 05:33:21 EDT (Thu, 22 Oct 2009)
@@ -0,0 +1,1364 @@
+// (C) Copyright 2008-9 Anthony Williams
+//
+// 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)
+
+#ifndef BOOST_THREAD_FUTURE_HPP
+#define BOOST_THREAD_FUTURE_HPP
+#include <stdexcept>
+#include <boost/thread/detail/move.hpp>
+#include <boost/thread/thread_time.hpp>
+#include <boost/exception_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/type_traits/is_fundamental.hpp>
+#include <boost/type_traits/is_convertible.hpp>
+#include <boost/mpl/if.hpp>
+#include <boost/config.hpp>
+#include <algorithm>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/ref.hpp>
+#include <boost/scoped_array.hpp>
+#include <boost/utility/enable_if.hpp>
+#include <list>
+#include <boost/next_prior.hpp>
+#include <vector>
+
+namespace boost
+{
+ class future_uninitialized:
+ public std::logic_error
+ {
+ public:
+ future_uninitialized():
+ std::logic_error("Future Uninitialized")
+ {}
+ };
+ class broken_promise:
+ public std::logic_error
+ {
+ public:
+ broken_promise():
+ std::logic_error("Broken promise")
+ {}
+ };
+ class future_already_retrieved:
+ public std::logic_error
+ {
+ public:
+ future_already_retrieved():
+ std::logic_error("Future already retrieved")
+ {}
+ };
+ class promise_already_satisfied:
+ public std::logic_error
+ {
+ public:
+ promise_already_satisfied():
+ std::logic_error("Promise already satisfied")
+ {}
+ };
+
+ class task_already_started:
+ public std::logic_error
+ {
+ public:
+ task_already_started():
+ std::logic_error("Task already started")
+ {}
+ };
+
+ class task_moved:
+ public std::logic_error
+ {
+ public:
+ task_moved():
+ std::logic_error("Task moved")
+ {}
+ };
+
+ namespace future_state
+ {
+ enum state { uninitialized, waiting, ready, moved };
+ }
+
+ namespace detail
+ {
+ struct future_object_base
+ {
+ boost::exception_ptr exception;
+ bool done;
+ boost::mutex mutex;
+ boost::condition_variable waiters;
+ typedef std::list<boost::condition_variable_any*> waiter_list;
+ waiter_list external_waiters;
+ boost::function<void()> callback;
+
+ future_object_base():
+ done(false)
+ {}
+ virtual ~future_object_base()
+ {}
+
+ waiter_list::iterator register_external_waiter(boost::condition_variable_any& cv)
+ {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ do_callback(lock);
+ return external_waiters.insert(external_waiters.end(),&cv);
+ }
+
+ void remove_external_waiter(waiter_list::iterator it)
+ {
+ boost::lock_guard<boost::mutex> lock(mutex);
+ external_waiters.erase(it);
+ }
+
+ void mark_finished_internal()
+ {
+ done=true;
+ waiters.notify_all();
+ for(waiter_list::const_iterator it=external_waiters.begin(),
+ end=external_waiters.end();it!=end;++it)
+ {
+ (*it)->notify_all();
+ }
+ }
+
+ struct relocker
+ {
+ boost::unique_lock<boost::mutex>& lock;
+
+ relocker(boost::unique_lock<boost::mutex>& lock_):
+ lock(lock_)
+ {
+ lock.unlock();
+ }
+ ~relocker()
+ {
+ lock.lock();
+ }
+ };
+
+ void do_callback(boost::unique_lock<boost::mutex>& lock)
+ {
+ if(callback && !done)
+ {
+ boost::function<void()> local_callback=callback;
+ relocker relock(lock);
+ local_callback();
+ }
+ }
+
+
+ void wait(bool rethrow=true)
+ {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ do_callback(lock);
+ while(!done)
+ {
+ waiters.wait(lock);
+ }
+ if(rethrow && exception)
+ {
+ boost::rethrow_exception(exception);
+ }
+ }
+
+ bool timed_wait_until(boost::system_time const& target_time)
+ {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ do_callback(lock);
+ while(!done)
+ {
+ bool const success=waiters.timed_wait(lock,target_time);
+ if(!success && !done)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void mark_exceptional_finish_internal(boost::exception_ptr const& e)
+ {
+ exception=e;
+ mark_finished_internal();
+ }
+ void mark_exceptional_finish()
+ {
+ boost::lock_guard<boost::mutex> lock(mutex);
+ mark_exceptional_finish_internal(boost::current_exception());
+ }
+
+ bool has_value()
+ {
+ boost::lock_guard<boost::mutex> lock(mutex);
+ return done && !exception;
+ }
+ bool has_exception()
+ {
+ boost::lock_guard<boost::mutex> lock(mutex);
+ return done && exception;
+ }
+
+ template<typename F,typename U>
+ void set_wait_callback(F f,U* u)
+ {
+ callback=boost::bind(f,boost::ref(*u));
+ }
+
+ private:
+ future_object_base(future_object_base const&);
+ future_object_base& operator=(future_object_base const&);
+ };
+
+ template<typename T>
+ struct future_traits
+ {
+ typedef boost::scoped_ptr<T> storage_type;
+#ifdef BOOST_HAS_RVALUE_REFS
+ typedef T const& source_reference_type;
+ struct dummy;
+ typedef typename boost::mpl::if_<boost::is_fundamental<T>,dummy&,T&&>::type rvalue_source_type;
+ typedef typename boost::mpl::if_<boost::is_fundamental<T>,T,T&&>::type move_dest_type;
+#else
+ typedef T& source_reference_type;
+ typedef typename boost::mpl::if_<boost::is_convertible<T&,boost::detail::thread_move_t<T> >,boost::detail::thread_move_t<T>,T const&>::type rvalue_source_type;
+ typedef typename boost::mpl::if_<boost::is_convertible<T&,boost::detail::thread_move_t<T> >,boost::detail::thread_move_t<T>,T>::type move_dest_type;
+#endif
+
+ static void init(storage_type& storage,source_reference_type t)
+ {
+ storage.reset(new T(t));
+ }
+
+ static void init(storage_type& storage,rvalue_source_type t)
+ {
+ storage.reset(new T(static_cast<rvalue_source_type>(t)));
+ }
+
+ static void cleanup(storage_type& storage)
+ {
+ storage.reset();
+ }
+ };
+
+ template<typename T>
+ struct future_traits<T&>
+ {
+ typedef T* storage_type;
+ typedef T& source_reference_type;
+ struct rvalue_source_type
+ {};
+ typedef T& move_dest_type;
+
+ static void init(storage_type& storage,T& t)
+ {
+ storage=&t;
+ }
+
+ static void cleanup(storage_type& storage)
+ {
+ storage=0;
+ }
+ };
+
+ template<>
+ struct future_traits<void>
+ {
+ typedef bool storage_type;
+ typedef void move_dest_type;
+
+ static void init(storage_type& storage)
+ {
+ storage=true;
+ }
+
+ static void cleanup(storage_type& storage)
+ {
+ storage=false;
+ }
+
+ };
+
+ template<typename T>
+ struct future_object:
+ detail::future_object_base
+ {
+ typedef typename future_traits<T>::storage_type storage_type;
+ typedef typename future_traits<T>::source_reference_type source_reference_type;
+ typedef typename future_traits<T>::rvalue_source_type rvalue_source_type;
+ typedef typename future_traits<T>::move_dest_type move_dest_type;
+
+ storage_type result;
+
+ future_object():
+ result(0)
+ {}
+
+ void mark_finished_with_result_internal(source_reference_type result_)
+ {
+ future_traits<T>::init(result,result_);
+ mark_finished_internal();
+ }
+ void mark_finished_with_result_internal(rvalue_source_type result_)
+ {
+ future_traits<T>::init(result,static_cast<rvalue_source_type>(result_));
+ mark_finished_internal();
+ }
+
+ void mark_finished_with_result(source_reference_type result_)
+ {
+ boost::lock_guard<boost::mutex> lock(mutex);
+ mark_finished_with_result_internal(result_);
+ }
+ void mark_finished_with_result(rvalue_source_type result_)
+ {
+ boost::lock_guard<boost::mutex> lock(mutex);
+ mark_finished_with_result_internal(result_);
+ }
+
+ move_dest_type get()
+ {
+ wait();
+ return *result;
+ }
+
+ future_state::state get_state()
+ {
+ boost::lock_guard<boost::mutex> guard(mutex);
+ if(!done)
+ {
+ return future_state::waiting;
+ }
+ else
+ {
+ return future_state::ready;
+ }
+ }
+
+ private:
+ future_object(future_object const&);
+ future_object& operator=(future_object const&);
+ };
+
+ template<>
+ struct future_object<void>:
+ detail::future_object_base
+ {
+ future_object()
+ {}
+
+ void mark_finished_with_result_internal()
+ {
+ mark_finished_internal();
+ }
+
+ void mark_finished_with_result()
+ {
+ boost::lock_guard<boost::mutex> lock(mutex);
+ mark_finished_with_result_internal();
+ }
+
+ void get()
+ {
+ wait();
+ }
+
+ future_state::state get_state()
+ {
+ boost::lock_guard<boost::mutex> guard(mutex);
+ if(!done)
+ {
+ return future_state::waiting;
+ }
+ else
+ {
+ return future_state::ready;
+ }
+ }
+
+ private:
+ future_object(future_object const&);
+ future_object& operator=(future_object const&);
+ };
+
+ class future_waiter
+ {
+ struct registered_waiter
+ {
+ boost::shared_ptr<detail::future_object_base> future;
+ detail::future_object_base::waiter_list::iterator wait_iterator;
+ unsigned index;
+
+ registered_waiter(boost::shared_ptr<detail::future_object_base> const& future_,
+ detail::future_object_base::waiter_list::iterator wait_iterator_,
+ unsigned index_):
+ future(future_),wait_iterator(wait_iterator_),index(index_)
+ {}
+
+ };
+
+ struct all_futures_lock
+ {
+ unsigned count;
+ boost::scoped_array<boost::unique_lock<boost::mutex> > locks;
+
+ all_futures_lock(std::vector<registered_waiter>& futures):
+ count(futures.size()),locks(new boost::unique_lock<boost::mutex>[count])
+ {
+ for(unsigned i=0;i<count;++i)
+ {
+ locks[i]=boost::unique_lock<boost::mutex>(futures[i].future->mutex);
+ }
+ }
+
+ void lock()
+ {
+ boost::lock(locks.get(),locks.get()+count);
+ }
+
+ void unlock()
+ {
+ for(unsigned i=0;i<count;++i)
+ {
+ locks[i].unlock();
+ }
+ }
+ };
+
+ boost::condition_variable_any cv;
+ std::vector<registered_waiter> futures;
+ unsigned future_count;
+
+ public:
+ future_waiter():
+ future_count(0)
+ {}
+
+ template<typename F>
+ void add(F& f)
+ {
+ if(f.future)
+ {
+ futures.push_back(registered_waiter(f.future,f.future->register_external_waiter(cv),future_count));
+ }
+ ++future_count;
+ }
+
+ unsigned wait()
+ {
+ all_futures_lock lk(futures);
+ for(;;)
+ {
+ for(unsigned i=0;i<futures.size();++i)
+ {
+ if(futures[i].future->done)
+ {
+ return futures[i].index;
+ }
+ }
+ cv.wait(lk);
+ }
+ }
+
+ ~future_waiter()
+ {
+ for(unsigned i=0;i<futures.size();++i)
+ {
+ futures[i].future->remove_external_waiter(futures[i].wait_iterator);
+ }
+ }
+
+ };
+
+ }
+
+ template <typename R>
+ class unique_future;
+
+ template <typename R>
+ class shared_future;
+
+ template<typename T>
+ struct is_future_type
+ {
+ BOOST_STATIC_CONSTANT(bool, value=false);
+ };
+
+ template<typename T>
+ struct is_future_type<unique_future<T> >
+ {
+ BOOST_STATIC_CONSTANT(bool, value=true);
+ };
+
+ template<typename T>
+ struct is_future_type<shared_future<T> >
+ {
+ BOOST_STATIC_CONSTANT(bool, value=true);
+ };
+
+ template<typename Iterator>
+ typename boost::disable_if<is_future_type<Iterator>,void>::type wait_for_all(Iterator begin,Iterator end)
+ {
+ for(Iterator current=begin;current!=end;++current)
+ {
+ current->wait();
+ }
+ }
+
+ template<typename F1,typename F2>
+ typename boost::enable_if<is_future_type<F1>,void>::type wait_for_all(F1& f1,F2& f2)
+ {
+ f1.wait();
+ f2.wait();
+ }
+
+ template<typename F1,typename F2,typename F3>
+ void wait_for_all(F1& f1,F2& f2,F3& f3)
+ {
+ f1.wait();
+ f2.wait();
+ f3.wait();
+ }
+
+ template<typename F1,typename F2,typename F3,typename F4>
+ void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4)
+ {
+ f1.wait();
+ f2.wait();
+ f3.wait();
+ f4.wait();
+ }
+
+ template<typename F1,typename F2,typename F3,typename F4,typename F5>
+ void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5)
+ {
+ f1.wait();
+ f2.wait();
+ f3.wait();
+ f4.wait();
+ f5.wait();
+ }
+
+ template<typename Iterator>
+ typename boost::disable_if<is_future_type<Iterator>,Iterator>::type wait_for_any(Iterator begin,Iterator end)
+ {
+ detail::future_waiter waiter;
+ for(Iterator current=begin;current!=end;++current)
+ {
+ waiter.add(*current);
+ }
+ return boost::next(begin,waiter.wait());
+ }
+
+ template<typename F1,typename F2>
+ typename boost::enable_if<is_future_type<F1>,unsigned>::type wait_for_any(F1& f1,F2& f2)
+ {
+ detail::future_waiter waiter;
+ waiter.add(f1);
+ waiter.add(f2);
+ return waiter.wait();
+ }
+
+ template<typename F1,typename F2,typename F3>
+ unsigned wait_for_any(F1& f1,F2& f2,F3& f3)
+ {
+ detail::future_waiter waiter;
+ waiter.add(f1);
+ waiter.add(f2);
+ waiter.add(f3);
+ return waiter.wait();
+ }
+
+ template<typename F1,typename F2,typename F3,typename F4>
+ unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4)
+ {
+ detail::future_waiter waiter;
+ waiter.add(f1);
+ waiter.add(f2);
+ waiter.add(f3);
+ waiter.add(f4);
+ return waiter.wait();
+ }
+
+ template<typename F1,typename F2,typename F3,typename F4,typename F5>
+ unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5)
+ {
+ detail::future_waiter waiter;
+ waiter.add(f1);
+ waiter.add(f2);
+ waiter.add(f3);
+ waiter.add(f4);
+ waiter.add(f5);
+ return waiter.wait();
+ }
+
+ template <typename R>
+ class promise;
+
+ template <typename R>
+ class packaged_task;
+
+ template <typename R>
+ class unique_future
+ {
+ unique_future(unique_future & rhs);// = delete;
+ unique_future& operator=(unique_future& rhs);// = delete;
+
+ typedef boost::shared_ptr<detail::future_object<R> > future_ptr;
+
+ future_ptr future;
+
+ friend class shared_future<R>;
+ friend class promise<R>;
+ friend class packaged_task<R>;
+ friend class detail::future_waiter;
+
+ typedef typename detail::future_traits<R>::move_dest_type move_dest_type;
+
+ unique_future(future_ptr future_):
+ future(future_)
+ {}
+
+ public:
+ typedef future_state::state state;
+
+ unique_future()
+ {}
+
+ ~unique_future()
+ {}
+
+#ifdef BOOST_HAS_RVALUE_REFS
+ unique_future(unique_future && other)
+ {
+ future.swap(other.future);
+ }
+ unique_future& operator=(unique_future && other)
+ {
+ future=other.future;
+ other.future.reset();
+ return *this;
+ }
+#else
+ unique_future(boost::detail::thread_move_t<unique_future> other):
+ future(other->future)
+ {
+ other->future.reset();
+ }
+
+ unique_future& operator=(boost::detail::thread_move_t<unique_future> other)
+ {
+ future=other->future;
+ other->future.reset();
+ return *this;
+ }
+
+ operator boost::detail::thread_move_t<unique_future>()
+ {
+ return boost::detail::thread_move_t<unique_future>(*this);
+ }
+#endif
+
+ void swap(unique_future& other)
+ {
+ future.swap(other.future);
+ }
+
+ // retrieving the value
+ move_dest_type get()
+ {
+ if(!future)
+ {
+ throw future_uninitialized();
+ }
+
+ return future->get();
+ }
+
+ // functions to check state, and wait for ready
+ state get_state() const
+ {
+ if(!future)
+ {
+ return future_state::uninitialized;
+ }
+ return future->get_state();
+ }
+
+
+ bool is_ready() const
+ {
+ return get_state()==future_state::ready;
+ }
+
+ bool has_exception() const
+ {
+ return future && future->has_exception();
+ }
+
+ bool has_value() const
+ {
+ return future && future->has_value();
+ }
+
+ void wait() const
+ {
+ if(!future)
+ {
+ throw future_uninitialized();
+ }
+ future->wait(false);
+ }
+
+ template<typename Duration>
+ bool timed_wait(Duration const& rel_time) const
+ {
+ return timed_wait_until(boost::get_system_time()+rel_time);
+ }
+
+ bool timed_wait_until(boost::system_time const& abs_time) const
+ {
+ if(!future)
+ {
+ throw future_uninitialized();
+ }
+ return future->timed_wait_until(abs_time);
+ }
+
+ };
+
+ template <typename R>
+ class shared_future
+ {
+ typedef boost::shared_ptr<detail::future_object<R> > future_ptr;
+
+ future_ptr future;
+
+// shared_future(const unique_future<R>& other);
+// shared_future& operator=(const unique_future<R>& other);
+
+ friend class detail::future_waiter;
+ friend class promise<R>;
+ friend class packaged_task<R>;
+
+ shared_future(future_ptr future_):
+ future(future_)
+ {}
+
+ public:
+ shared_future(shared_future const& other):
+ future(other.future)
+ {}
+
+ typedef future_state::state state;
+
+ shared_future()
+ {}
+
+ ~shared_future()
+ {}
+
+ shared_future& operator=(shared_future const& other)
+ {
+ future=other.future;
+ return *this;
+ }
+#ifdef BOOST_HAS_RVALUE_REFS
+ shared_future(shared_future && other)
+ {
+ future.swap(other.future);
+ }
+ shared_future(unique_future<R> && other)
+ {
+ future.swap(other.future);
+ }
+ shared_future& operator=(shared_future && other)
+ {
+ future.swap(other.future);
+ other.future.reset();
+ return *this;
+ }
+ shared_future& operator=(unique_future<R> && other)
+ {
+ future.swap(other.future);
+ other.future.reset();
+ return *this;
+ }
+#else
+ shared_future(boost::detail::thread_move_t<shared_future> other):
+ future(other->future)
+ {
+ other->future.reset();
+ }
+// shared_future(const unique_future<R> &) = delete;
+ shared_future(boost::detail::thread_move_t<unique_future<R> > other):
+ future(other->future)
+ {
+ other->future.reset();
+ }
+ shared_future& operator=(boost::detail::thread_move_t<shared_future> other)
+ {
+ future.swap(other->future);
+ other->future.reset();
+ return *this;
+ }
+ shared_future& operator=(boost::detail::thread_move_t<unique_future<R> > other)
+ {
+ future.swap(other->future);
+ other->future.reset();
+ return *this;
+ }
+
+ operator boost::detail::thread_move_t<shared_future>()
+ {
+ return boost::detail::thread_move_t<shared_future>(*this);
+ }
+
+#endif
+
+ void swap(shared_future& other)
+ {
+ future.swap(other.future);
+ }
+
+ // retrieving the value
+ R get()
+ {
+ if(!future)
+ {
+ throw future_uninitialized();
+ }
+
+ return future->get();
+ }
+
+ // functions to check state, and wait for ready
+ state get_state() const
+ {
+ if(!future)
+ {
+ return future_state::uninitialized;
+ }
+ return future->get_state();
+ }
+
+
+ bool is_ready() const
+ {
+ return get_state()==future_state::ready;
+ }
+
+ bool has_exception() const
+ {
+ return future && future->has_exception();
+ }
+
+ bool has_value() const
+ {
+ return future && future->has_value();
+ }
+
+ void wait() const
+ {
+ if(!future)
+ {
+ throw future_uninitialized();
+ }
+ future->wait(false);
+ }
+
+ template<typename Duration>
+ bool timed_wait(Duration const& rel_time) const
+ {
+ return timed_wait_until(boost::get_system_time()+rel_time);
+ }
+
+ bool timed_wait_until(boost::system_time const& abs_time) const
+ {
+ if(!future)
+ {
+ throw future_uninitialized();
+ }
+ return future->timed_wait_until(abs_time);
+ }
+
+ };
+
+ template <typename R>
+ class promise
+ {
+ typedef boost::shared_ptr<detail::future_object<R> > future_ptr;
+
+ future_ptr future;
+ bool future_obtained;
+
+ promise(promise & rhs);// = delete;
+ promise & operator=(promise & rhs);// = delete;
+
+ void lazy_init()
+ {
+ if(!future)
+ {
+ future_obtained=false;
+ future.reset(new detail::future_object<R>);
+ }
+ }
+
+ public:
+// template <class Allocator> explicit promise(Allocator a);
+
+ promise():
+ future(),future_obtained(false)
+ {}
+
+ ~promise()
+ {
+ if(future)
+ {
+ boost::lock_guard<boost::mutex> lock(future->mutex);
+
+ if(!future->done)
+ {
+ future->mark_exceptional_finish_internal(boost::copy_exception(broken_promise()));
+ }
+ }
+ }
+
+ // Assignment
+#ifdef BOOST_HAS_RVALUE_REFS
+ promise(promise && rhs):
+ future_obtained(rhs.future_obtained)
+ {
+ future.swap(rhs.future);
+ }
+ promise & operator=(promise&& rhs)
+ {
+ future.swap(rhs.future);
+ future_obtained=rhs.future_obtained;
+ rhs.future.reset();
+ return *this;
+ }
+#else
+ promise(boost::detail::thread_move_t<promise> rhs):
+ future(rhs->future),future_obtained(rhs->future_obtained)
+ {
+ rhs->future.reset();
+ }
+ promise & operator=(boost::detail::thread_move_t<promise> rhs)
+ {
+ future=rhs->future;
+ future_obtained=rhs->future_obtained;
+ rhs->future.reset();
+ return *this;
+ }
+
+ operator boost::detail::thread_move_t<promise>()
+ {
+ return boost::detail::thread_move_t<promise>(*this);
+ }
+#endif
+
+ void swap(promise& other)
+ {
+ future.swap(other.future);
+ std::swap(future_obtained,other.future_obtained);
+ }
+
+ // Result retrieval
+ unique_future<R> get_future()
+ {
+ lazy_init();
+ if(future_obtained)
+ {
+ throw future_already_retrieved();
+ }
+ future_obtained=true;
+ return unique_future<R>(future);
+ }
+
+ void set_value(typename detail::future_traits<R>::source_reference_type r)
+ {
+ lazy_init();
+ boost::lock_guard<boost::mutex> lock(future->mutex);
+ if(future->done)
+ {
+ throw promise_already_satisfied();
+ }
+ future->mark_finished_with_result_internal(r);
+ }
+
+// void set_value(R && r);
+ void set_value(typename detail::future_traits<R>::rvalue_source_type r)
+ {
+ lazy_init();
+ boost::lock_guard<boost::mutex> lock(future->mutex);
+ if(future->done)
+ {
+ throw promise_already_satisfied();
+ }
+ future->mark_finished_with_result_internal(static_cast<typename detail::future_traits<R>::rvalue_source_type>(r));
+ }
+
+ void set_exception(boost::exception_ptr p)
+ {
+ lazy_init();
+ boost::lock_guard<boost::mutex> lock(future->mutex);
+ if(future->done)
+ {
+ throw promise_already_satisfied();
+ }
+ future->mark_exceptional_finish_internal(p);
+ }
+
+ template<typename F>
+ void set_wait_callback(F f)
+ {
+ lazy_init();
+ future->set_wait_callback(f,this);
+ }
+
+ };
+
+ template <>
+ class promise<void>
+ {
+ typedef boost::shared_ptr<detail::future_object<void> > future_ptr;
+
+ future_ptr future;
+ bool future_obtained;
+
+ promise(promise & rhs);// = delete;
+ promise & operator=(promise & rhs);// = delete;
+
+ void lazy_init()
+ {
+ if(!future)
+ {
+ future_obtained=false;
+ future.reset(new detail::future_object<void>);
+ }
+ }
+ public:
+// template <class Allocator> explicit promise(Allocator a);
+
+ promise():
+ future(),future_obtained(false)
+ {}
+
+ ~promise()
+ {
+ if(future)
+ {
+ boost::lock_guard<boost::mutex> lock(future->mutex);
+
+ if(!future->done)
+ {
+ future->mark_exceptional_finish_internal(boost::copy_exception(broken_promise()));
+ }
+ }
+ }
+
+ // Assignment
+#ifdef BOOST_HAS_RVALUE_REFS
+ promise(promise && rhs):
+ future_obtained(rhs.future_obtained)
+ {
+ future.swap(rhs.future);
+ }
+ promise & operator=(promise&& rhs)
+ {
+ future.swap(rhs.future);
+ future_obtained=rhs.future_obtained;
+ rhs.future.reset();
+ return *this;
+ }
+#else
+ promise(boost::detail::thread_move_t<promise> rhs):
+ future(rhs->future),future_obtained(rhs->future_obtained)
+ {
+ rhs->future.reset();
+ }
+ promise & operator=(boost::detail::thread_move_t<promise> rhs)
+ {
+ future=rhs->future;
+ future_obtained=rhs->future_obtained;
+ rhs->future.reset();
+ return *this;
+ }
+
+ operator boost::detail::thread_move_t<promise>()
+ {
+ return boost::detail::thread_move_t<promise>(*this);
+ }
+#endif
+
+ void swap(promise& other)
+ {
+ future.swap(other.future);
+ std::swap(future_obtained,other.future_obtained);
+ }
+
+ // Result retrieval
+ unique_future<void> get_future()
+ {
+ lazy_init();
+
+ if(future_obtained)
+ {
+ throw future_already_retrieved();
+ }
+ future_obtained=true;
+ return unique_future<void>(future);
+ }
+
+ void set_value()
+ {
+ lazy_init();
+ boost::lock_guard<boost::mutex> lock(future->mutex);
+ if(future->done)
+ {
+ throw promise_already_satisfied();
+ }
+ future->mark_finished_with_result_internal();
+ }
+
+ void set_exception(boost::exception_ptr p)
+ {
+ lazy_init();
+ boost::lock_guard<boost::mutex> lock(future->mutex);
+ if(future->done)
+ {
+ throw promise_already_satisfied();
+ }
+ future->mark_exceptional_finish_internal(p);
+ }
+
+ template<typename F>
+ void set_wait_callback(F f)
+ {
+ lazy_init();
+ future->set_wait_callback(f,this);
+ }
+
+ };
+
+ namespace detail
+ {
+ template<typename R>
+ struct task_base:
+ detail::future_object<R>
+ {
+ bool started;
+
+ task_base():
+ started(false)
+ {}
+
+ void run()
+ {
+ {
+ boost::lock_guard<boost::mutex> lk(this->mutex);
+ if(started)
+ {
+ throw task_already_started();
+ }
+ started=true;
+ }
+ do_run();
+ }
+
+ void owner_destroyed()
+ {
+ boost::lock_guard<boost::mutex> lk(this->mutex);
+ if(!started)
+ {
+ started=true;
+ this->mark_exceptional_finish_internal(boost::copy_exception(boost::broken_promise()));
+ }
+ }
+
+
+ virtual void do_run()=0;
+ };
+
+
+ template<typename R,typename F>
+ struct task_object:
+ task_base<R>
+ {
+ F f;
+ task_object(F const& f_):
+ f(f_)
+ {}
+ task_object(boost::detail::thread_move_t<F> f_):
+ f(f_)
+ {}
+
+ void do_run()
+ {
+ try
+ {
+ this->mark_finished_with_result(f());
+ }
+ catch(...)
+ {
+ this->mark_exceptional_finish();
+ }
+ }
+ };
+
+ template<typename F>
+ struct task_object<void,F>:
+ task_base<void>
+ {
+ F f;
+ task_object(F const& f_):
+ f(f_)
+ {}
+ task_object(boost::detail::thread_move_t<F> f_):
+ f(f_)
+ {}
+
+ void do_run()
+ {
+ try
+ {
+ f();
+ this->mark_finished_with_result();
+ }
+ catch(...)
+ {
+ this->mark_exceptional_finish();
+ }
+ }
+ };
+
+ }
+
+
+ template<typename R>
+ class packaged_task
+ {
+ boost::shared_ptr<detail::task_base<R> > task;
+ bool future_obtained;
+
+ packaged_task(packaged_task&);// = delete;
+ packaged_task& operator=(packaged_task&);// = delete;
+
+ public:
+ packaged_task():
+ future_obtained(false)
+ {}
+
+ // construction and destruction
+ template <class F>
+ explicit packaged_task(F const& f):
+ task(new detail::task_object<R,F>(f)),future_obtained(false)
+ {}
+ explicit packaged_task(R(*f)()):
+ task(new detail::task_object<R,R(*)()>(f)),future_obtained(false)
+ {}
+
+ template <class F>
+ explicit packaged_task(boost::detail::thread_move_t<F> f):
+ task(new detail::task_object<R,F>(f)),future_obtained(false)
+ {}
+
+// template <class F, class Allocator>
+// explicit packaged_task(F const& f, Allocator a);
+// template <class F, class Allocator>
+// explicit packaged_task(F&& f, Allocator a);
+
+
+ ~packaged_task()
+ {
+ if(task)
+ {
+ task->owner_destroyed();
+ }
+ }
+
+ // assignment
+#ifdef BOOST_HAS_RVALUE_REFS
+ packaged_task(packaged_task&& other):
+ future_obtained(other.future_obtained)
+ {
+ task.swap(other.task);
+ other.future_obtained=false;
+ }
+ packaged_task& operator=(packaged_task&& other)
+ {
+ packaged_task temp(static_cast<packaged_task&&>(other));
+ swap(temp);
+ return *this;
+ }
+#else
+ packaged_task(boost::detail::thread_move_t<packaged_task> other):
+ future_obtained(other->future_obtained)
+ {
+ task.swap(other->task);
+ other->future_obtained=false;
+ }
+ packaged_task& operator=(boost::detail::thread_move_t<packaged_task> other)
+ {
+ packaged_task temp(other);
+ swap(temp);
+ return *this;
+ }
+ operator boost::detail::thread_move_t<packaged_task>()
+ {
+ return boost::detail::thread_move_t<packaged_task>(*this);
+ }
+#endif
+
+ void swap(packaged_task& other)
+ {
+ task.swap(other.task);
+ std::swap(future_obtained,other.future_obtained);
+ }
+
+ // result retrieval
+ unique_future<R> get_future()
+ {
+ if(!task)
+ {
+ throw task_moved();
+ }
+ else if(!future_obtained)
+ {
+ future_obtained=true;
+ return unique_future<R>(task);
+ }
+ else
+ {
+ throw future_already_retrieved();
+ }
+ }
+
+
+ // execution
+ void operator()()
+ {
+ if(!task)
+ {
+ throw task_moved();
+ }
+ task->run();
+ }
+
+ template<typename F>
+ void set_wait_callback(F f)
+ {
+ task->set_wait_callback(f,this);
+ }
+
+ };
+
+}
+
+
+#endif
Modified: trunk/libs/thread/doc/changes.qbk
==============================================================================
--- trunk/libs/thread/doc/changes.qbk (original)
+++ trunk/libs/thread/doc/changes.qbk 2009-10-22 05:33:21 EDT (Thu, 22 Oct 2009)
@@ -5,7 +5,11 @@
http://www.boost.org/LICENSE_1_0.txt).
]
-[section:changes Changes since boost 1.35]
+[section:changes Changes since boost 1.40]
+
+The 1.41.0 release of Boost adds futures to the thread library. There are also a few minor changes.
+
+[heading Changes since boost 1.35]
The 1.36.0 release of Boost includes a few new features in the thread library:
Added: trunk/libs/thread/doc/future_ref.qbk
==============================================================================
--- (empty file)
+++ trunk/libs/thread/doc/future_ref.qbk 2009-10-22 05:33:21 EDT (Thu, 22 Oct 2009)
@@ -0,0 +1,968 @@
+[/
+ (C) Copyright 2008-9 Anthony Williams.
+ 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).
+]
+
+[section:reference Futures Reference]
+
+[section:future_state `state` enum]
+
+ namespace future_state
+ {
+ enum state {uninitialized, waiting, ready};
+ }
+
+[endsect]
+
+[section:unique_future `unique_future` class template]
+
+ template <typename R>
+ class unique_future
+ {
+ unique_future(unique_future & rhs);// = delete;
+ unique_future& operator=(unique_future& rhs);// = delete;
+
+ public:
+ typedef future_state::state state;
+
+ unique_future();
+ ~unique_future();
+
+ // move support
+ unique_future(unique_future && other);
+ unique_future& operator=(unique_future && other);
+
+ void swap(unique_future& other);
+
+ // retrieving the value
+ R&& get();
+
+ // functions to check state
+ state get_state() const;
+ bool is_ready() const;
+ bool has_exception() const;
+ bool has_value() const;
+
+ // waiting for the result to be ready
+ void wait() const;
+ template<typename Duration>
+ bool timed_wait(Duration const& rel_time) const;
+ bool timed_wait_until(boost::system_time const& abs_time) const;
+ };
+
+[section:default_constructor Default Constructor]
+
+ unique_future();
+
+[variablelist
+
+[[Effects:] [Constructs an uninitialized future.]]
+
+[[Postconditions:] [[unique_future_is_ready_link `this->is_ready`] returns `false`. [unique_future_get_state_link
+`this->get_state()`] returns __uninitialized__.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[section:destructor Destructor]
+
+ ~unique_future();
+
+[variablelist
+
+[[Effects:] [Destroys `*this`.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[section:move_constructor Move Constructor]
+
+ unique_future(unique_future && other);
+
+[variablelist
+
+[[Effects:] [Constructs a new future, and transfers ownership of the asynchronous result associated with `other` to `*this`.]]
+
+[[Postconditions:] [[unique_future_get_state_link `this->get_state()`] returns the value of `other->get_state()` prior to the
+call. `other->get_state()` returns __uninitialized__. If `other` was associated with an asynchronous result, that result is now
+associated with `*this`. `other` is not associated with any asynchronous result.]]
+
+[[Throws:] [Nothing.]]
+
+[[Notes:] [If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.]]
+
+]
+
+[endsect]
+
+[section:move_assignment Move Assignment Operator]
+
+ unique_future& operator=(unique_future && other);
+
+[variablelist
+
+[[Effects:] [Transfers ownership of the asynchronous result associated with `other` to `*this`.]]
+
+[[Postconditions:] [[unique_future_get_state_link `this->get_state()`] returns the value of `other->get_state()` prior to the
+call. `other->get_state()` returns __uninitialized__. If `other` was associated with an asynchronous result, that result is now
+associated with `*this`. `other` is not associated with any asynchronous result. If `*this` was associated with an asynchronous
+result prior to the call, that result no longer has an associated __unique_future__ instance.]]
+
+[[Throws:] [Nothing.]]
+
+[[Notes:] [If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.]]
+
+]
+
+[endsect]
+
+[section:swap Member function `swap()`]
+
+ void swap(unique_future & other);
+
+[variablelist
+
+[[Effects:] [Swaps ownership of the asynchronous results associated with `other` and `*this`.]]
+
+[[Postconditions:] [[unique_future_get_state_link `this->get_state()`] returns the value of `other->get_state()` prior to the
+call. `other->get_state()` returns the value of `this->get_state()` prior to the call. If `other` was associated with an
+asynchronous result, that result is now associated with `*this`, otherwise `*this` has no associated result. If `*this` was
+associated with an asynchronous result, that result is now associated with `other`, otherwise `other` has no associated result.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+
+[section:get Member function `get()`]
+
+ R&& get();
+ R& unique_future<R&>::get();
+ void unique_future<void>::get();
+
+[variablelist
+
+[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready as-if by a call to
+__unique_future_wait__, and retrieves the result (whether that is a value or an exception).]]
+
+[[Returns:] [If the result type `R` is a reference, returns the stored reference. If `R` is `void`, there is no return
+value. Otherwise, returns an rvalue-reference to the value stored in the asynchronous result.]]
+
+[[Postconditions:] [[unique_future_is_ready_link `this->is_ready()`] returns `true`. [unique_future_get_state_link
+`this->get_state()`] returns __ready__.]]
+
+[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result
+associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception stored in the
+asynchronous result in place of a value.]]
+
+[[Notes:] [`get()` is an ['interruption point].]]
+
+]
+
+[endsect]
+
+[section:wait Member function `wait()`]
+
+ void wait();
+
+[variablelist
+
+[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready. If the result is not ready on
+entry, and the result has a ['wait callback] set, that callback is invoked prior to waiting.]]
+
+[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result
+associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the
+['wait callback] if such a callback is called.]]
+
+[[Postconditions:] [[unique_future_is_ready_link `this->is_ready()`] returns `true`. [unique_future_get_state_link
+`this->get_state()`] returns __ready__.]]
+
+[[Notes:] [`wait()` is an ['interruption point].]]
+
+]
+
+[endsect]
+
+[section:timed_wait_duration Member function `timed_wait()`]
+
+ template<typename Duration>
+ bool timed_wait(Duration const& wait_duration);
+
+[variablelist
+
+[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready, or the time specified by
+`wait_duration` has elapsed. If the result is not ready on entry, and the result has a ['wait callback] set, that callback is
+invoked prior to waiting.]]
+
+[[Returns:] [`true` if `*this` is associated with an asynchronous result, and that result is ready before the specified time has
+elapsed, `false` otherwise.]]
+
+[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result
+associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the
+['wait callback] if such a callback is called.]]
+
+[[Postconditions:] [If this call returned `true`, then [unique_future_is_ready_link `this->is_ready()`] returns `true` and
+[unique_future_get_state_link `this->get_state()`] returns __ready__.]]
+
+[[Notes:] [`timed_wait()` is an ['interruption point]. `Duration` must be a type that meets the Boost.DateTime time duration requirements.]]
+
+]
+
+[endsect]
+
+[section:timed_wait_absolute Member function `timed_wait()`]
+
+ bool timed_wait(boost::system_time const& wait_timeout);
+
+[variablelist
+
+[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready, or the time point specified by
+`wait_timeout` has passed. If the result is not ready on entry, and the result has a ['wait callback] set, that callback is invoked
+prior to waiting.]]
+
+[[Returns:] [`true` if `*this` is associated with an asynchronous result, and that result is ready before the specified time has
+passed, `false` otherwise.]]
+
+[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result
+associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the
+['wait callback] if such a callback is called.]]
+
+[[Postconditions:] [If this call returned `true`, then [unique_future_is_ready_link `this->is_ready()`] returns `true` and
+[unique_future_get_state_link `this->get_state()`] returns __ready__.]]
+
+[[Notes:] [`timed_wait()` is an ['interruption point].]]
+
+]
+
+[endsect]
+
+
+[section:is_ready Member function `is_ready()`]
+
+ bool is_ready();
+
+[variablelist
+
+[[Effects:] [Checks to see if the asynchronous result associated with `*this` is set.]]
+
+[[Returns:] [`true` if `*this` is associated with an asynchronous result, and that result is ready for retrieval, `false`
+otherwise.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[section:has_value Member function `has_value()`]
+
+ bool has_value();
+
+[variablelist
+
+[[Effects:] [Checks to see if the asynchronous result associated with `*this` is set with a value rather than an exception.]]
+
+[[Returns:] [`true` if `*this` is associated with an asynchronous result, that result is ready for retrieval, and the result is a
+stored value, `false` otherwise.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[section:has_exception Member function `has_exception()`]
+
+ bool has_exception();
+
+[variablelist
+
+[[Effects:] [Checks to see if the asynchronous result associated with `*this` is set with an exception rather than a value.]]
+
+[[Returns:] [`true` if `*this` is associated with an asynchronous result, that result is ready for retrieval, and the result is a
+stored exception, `false` otherwise.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[section:get_state Member function `get_state()`]
+
+ future_state::state get_state();
+
+[variablelist
+
+[[Effects:] [Determine the state of the asynchronous result associated with `*this`, if any.]]
+
+[[Returns:] [__uninitialized__ if `*this` is not associated with an asynchronous result. __ready__ if the asynchronous result
+associated with `*this` is ready for retrieval, __waiting__ otherwise.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+
+[endsect]
+
+[section:shared_future `shared_future` class template]
+
+ template <typename R>
+ class shared_future
+ {
+ public:
+ typedef future_state::state state;
+
+ shared_future();
+ ~shared_future();
+
+ // copy support
+ shared_future(shared_future const& other);
+ shared_future& operator=(shared_future const& other);
+
+ // move support
+ shared_future(shared_future && other);
+ shared_future(unique_future<R> && other);
+ shared_future& operator=(shared_future && other);
+ shared_future& operator=(unique_future<R> && other);
+
+ void swap(shared_future& other);
+
+ // retrieving the value
+ R get();
+
+ // functions to check state, and wait for ready
+ state get_state() const;
+ bool is_ready() const;
+ bool has_exception() const;
+ bool has_value() const;
+
+ // waiting for the result to be ready
+ void wait() const;
+ template<typename Duration>
+ bool timed_wait(Duration const& rel_time) const;
+ bool timed_wait_until(boost::system_time const& abs_time) const;
+ };
+
+[section:default_constructor Default Constructor]
+
+ shared_future();
+
+[variablelist
+
+[[Effects:] [Constructs an uninitialized future.]]
+
+[[Postconditions:] [[shared_future_is_ready_link `this->is_ready`] returns `false`. [shared_future_get_state_link
+`this->get_state()`] returns __uninitialized__.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[section:get Member function `get()`]
+
+ const R& get();
+
+[variablelist
+
+[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready as-if by a call to
+__shared_future_wait__, and returns a `const` reference to the result.]]
+
+[[Returns:] [If the result type `R` is a reference, returns the stored reference. If `R` is `void`, there is no return
+value. Otherwise, returns a `const` reference to the value stored in the asynchronous result.]]
+
+[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the
+result associated with `*this` is not ready at the point of the call, and the current thread is interrupted.]]
+
+[[Notes:] [`get()` is an ['interruption point].]]
+
+]
+
+[endsect]
+
+[section:wait Member function `wait()`]
+
+ void wait();
+
+[variablelist
+
+[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready. If the result is not ready on
+entry, and the result has a ['wait callback] set, that callback is invoked prior to waiting.]]
+
+[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result
+associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the
+['wait callback] if such a callback is called.]]
+
+[[Postconditions:] [[shared_future_is_ready_link `this->is_ready()`] returns `true`. [shared_future_get_state_link
+`this->get_state()`] returns __ready__.]]
+
+[[Notes:] [`wait()` is an ['interruption point].]]
+
+]
+
+[endsect]
+
+[section:timed_wait_duration Member function `timed_wait()`]
+
+ template<typename Duration>
+ bool timed_wait(Duration const& wait_duration);
+
+[variablelist
+
+[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready, or the time specified by
+`wait_duration` has elapsed. If the result is not ready on entry, and the result has a ['wait callback] set, that callback is
+invoked prior to waiting.]]
+
+[[Returns:] [`true` if `*this` is associated with an asynchronous result, and that result is ready before the specified time has
+elapsed, `false` otherwise.]]
+
+[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result
+associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the
+['wait callback] if such a callback is called.]]
+
+[[Postconditions:] [If this call returned `true`, then [shared_future_is_ready_link `this->is_ready()`] returns `true` and
+[shared_future_get_state_link `this->get_state()`] returns __ready__.]]
+
+[[Notes:] [`timed_wait()` is an ['interruption point]. `Duration` must be a type that meets the Boost.DateTime time duration requirements.]]
+
+]
+
+[endsect]
+
+[section:timed_wait_absolute Member function `timed_wait()`]
+
+ bool timed_wait(boost::system_time const& wait_timeout);
+
+[variablelist
+
+[[Effects:] [If `*this` is associated with an asynchronous result, waits until the result is ready, or the time point specified by
+`wait_timeout` has passed. If the result is not ready on entry, and the result has a ['wait callback] set, that callback is invoked
+prior to waiting.]]
+
+[[Returns:] [`true` if `*this` is associated with an asynchronous result, and that result is ready before the specified time has
+passed, `false` otherwise.]]
+
+[[Throws:] [__future_uninitialized__ if `*this` is not associated with an asynchronous result. __thread_interrupted__ if the result
+associated with `*this` is not ready at the point of the call, and the current thread is interrupted. Any exception thrown by the
+['wait callback] if such a callback is called.]]
+
+[[Postconditions:] [If this call returned `true`, then [shared_future_is_ready_link `this->is_ready()`] returns `true` and
+[shared_future_get_state_link `this->get_state()`] returns __ready__.]]
+
+[[Notes:] [`timed_wait()` is an ['interruption point].]]
+
+]
+
+[endsect]
+
+[section:is_ready Member function `is_ready()`]
+
+ bool is_ready();
+
+[variablelist
+
+[[Effects:] [Checks to see if the asynchronous result associated with `*this` is set.]]
+
+[[Returns:] [`true` if `*this` is associated with an asynchronous result, and that result is ready for retrieval, `false`
+otherwise.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[section:has_value Member function `has_value()`]
+
+ bool has_value();
+
+[variablelist
+
+[[Effects:] [Checks to see if the asynchronous result associated with `*this` is set with a value rather than an exception.]]
+
+[[Returns:] [`true` if `*this` is associated with an asynchronous result, that result is ready for retrieval, and the result is a
+stored value, `false` otherwise.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[section:has_exception Member function `has_exception()`]
+
+ bool has_exception();
+
+[variablelist
+
+[[Effects:] [Checks to see if the asynchronous result associated with `*this` is set with an exception rather than a value.]]
+
+[[Returns:] [`true` if `*this` is associated with an asynchronous result, that result is ready for retrieval, and the result is a
+stored exception, `false` otherwise.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[section:get_state Member function `get_state()`]
+
+ future_state::state get_state();
+
+[variablelist
+
+[[Effects:] [Determine the state of the asynchronous result associated with `*this`, if any.]]
+
+[[Returns:] [__uninitialized__ if `*this` is not associated with an asynchronous result. __ready__ if the asynchronous result
+associated with `*this` is ready for retrieval, __waiting__ otherwise.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+
+[endsect]
+
+[section:promise `promise` class template]
+
+ template <typename R>
+ class promise
+ {
+ promise(promise & rhs);// = delete;
+ promise & operator=(promise & rhs);// = delete;
+ public:
+ // template <class Allocator> explicit promise(Allocator a);
+
+ promise();
+ ~promise();
+
+ // Move support
+ promise(promise && rhs);
+ promise & operator=(promise&& rhs);
+
+ void swap(promise& other);
+ // Result retrieval
+ unique_future<R> get_future();
+
+ // Set the value
+ void set_value(R& r);
+ void set_value(R&& r);
+ void set_exception(boost::exception_ptr e);
+
+ template<typename F>
+ void set_wait_callback(F f);
+ };
+
+[section:default_constructor Default Constructor]
+
+ promise();
+
+[variablelist
+
+[[Effects:] [Constructs a new __promise__ with no associated result.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[section:move_constructor Move Constructor]
+
+ promise(promise && other);
+
+[variablelist
+
+[[Effects:] [Constructs a new __promise__, and transfers ownership of the result associated with `other` to `*this`, leaving `other`
+with no associated result.]]
+
+[[Throws:] [Nothing.]]
+
+[[Notes:] [If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.]]
+
+]
+
+[endsect]
+
+[section:move_assignment Move Assignment Operator]
+
+ promise& operator=(promise && other);
+
+[variablelist
+
+[[Effects:] [Transfers ownership of the result associated with `other` to `*this`, leaving `other` with no associated result. If there
+was already a result associated with `*this`, and that result was not ['ready], sets any futures associated with that result to
+['ready] with a __broken_promise__ exception as the result. ]]
+
+[[Throws:] [Nothing.]]
+
+[[Notes:] [If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.]]
+
+]
+
+[endsect]
+
+[section:destructor Destructor]
+
+ ~promise();
+
+[variablelist
+
+[[Effects:] [Destroys `*this`. If there was a result associated with `*this`, and that result is not ['ready], sets any futures
+associated with that task to ['ready] with a __broken_promise__ exception as the result.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[section:get_future Member Function `get_future()`]
+
+ unique_future<R> get_future();
+
+[variablelist
+
+[[Effects:] [If `*this` was not associated with a result, allocate storage for a new asynchronous result and associate it with
+`*this`. Returns a __unique_future__ associated with the result associated with `*this`. ]]
+
+[[Throws:] [__future_already_retrieved__ if the future associated with the task has already been retrieved. `std::bad_alloc` if any
+memory necessary could not be allocated.]]
+
+]
+
+[endsect]
+
+[section:set_value Member Function `set_value()`]
+
+ void set_value(R&& r);
+ void set_value(const R& r);
+ void promise<R&>::set_value(R& r);
+ void promise<void>::set_value();
+
+[variablelist
+
+[[Effects:] [If `*this` was not associated with a result, allocate storage for a new asynchronous result and associate it with
+`*this`. Store the value `r` in the asynchronous result associated with `*this`. Any threads blocked waiting for the asynchronous
+result are woken.]]
+
+[[Postconditions:] [All futures waiting on the asynchronous result are ['ready] and __unique_future_has_value__ or
+__shared_future_has_value__ for those futures shall return `true`.]]
+
+[[Throws:] [__promise_already_satisfied__ if the result associated with `*this` is already ['ready]. `std::bad_alloc` if the memory
+required for storage of the result cannot be allocated. Any exception thrown by the copy or move-constructor of `R`.]]
+
+]
+
+[endsect]
+
+[section:set_exception Member Function `set_exception()`]
+
+ void set_exception(boost::exception_ptr e);
+
+[variablelist
+
+[[Effects:] [If `*this` was not associated with a result, allocate storage for a new asynchronous result and associate it with
+`*this`. Store the exception `e` in the asynchronous result associated with `*this`. Any threads blocked waiting for the asynchronous
+result are woken.]]
+
+[[Postconditions:] [All futures waiting on the asynchronous result are ['ready] and __unique_future_has_exception__ or
+__shared_future_has_exception__ for those futures shall return `true`.]]
+
+[[Throws:] [__promise_already_satisfied__ if the result associated with `*this` is already ['ready]. `std::bad_alloc` if the memory
+required for storage of the result cannot be allocated.]]
+
+]
+
+[endsect]
+
+[section:set_wait_callback Member Function `set_wait_callback()`]
+
+ template<typename F>
+ void set_wait_callback(F f);
+
+[variablelist
+
+[[Preconditions:] [The expression `f(t)` where `t` is a lvalue of type __packaged_task__ shall be well-formed. Invoking a copy of
+`f` shall have the same effect as invoking `f`]]
+
+[[Effects:] [Store a copy of `f` with the asynchronous result associated with `*this` as a ['wait callback]. This will replace any
+existing wait callback store alongside that result. If a thread subsequently calls one of the wait functions on a __unique_future__
+or __shared_future__ associated with this result, and the result is not ['ready], `f(*this)` shall be invoked.]]
+
+[[Throws:] [`std::bad_alloc` if memory cannot be allocated for the required storage.]]
+
+]
+
+[endsect]
+
+[endsect]
+
+[section:packaged_task `packaged_task` class template]
+
+ template<typename R>
+ class packaged_task
+ {
+ packaged_task(packaged_task&);// = delete;
+ packaged_task& operator=(packaged_task&);// = delete;
+
+ public:
+ // construction and destruction
+ template <class F>
+ explicit packaged_task(F const& f);
+
+ explicit packaged_task(R(*f)());
+
+ template <class F>
+ explicit packaged_task(F&& f);
+
+ // template <class F, class Allocator>
+ // explicit packaged_task(F const& f, Allocator a);
+ // template <class F, class Allocator>
+ // explicit packaged_task(F&& f, Allocator a);
+
+ ~packaged_task()
+ {}
+
+ // move support
+ packaged_task(packaged_task&& other);
+ packaged_task& operator=(packaged_task&& other);
+
+ void swap(packaged_task& other);
+ // result retrieval
+ unique_future<R> get_future();
+
+ // execution
+ void operator()();
+
+ template<typename F>
+ void set_wait_callback(F f);
+ };
+
+[section:task_constructor Task Constructor]
+
+ template<typename F>
+ packaged_task(F const &f);
+
+ packaged_task(R(*f)());
+
+ template<typename F>
+ packaged_task(F&&f);
+
+[variablelist
+
+[[Preconditions:] [`f()` is a valid expression with a return type convertible to `R`. Invoking a copy of `f` shall behave the same
+as invoking `f`.]]
+
+[[Effects:] [Constructs a new __packaged_task__ with a copy of `f` stored as the associated task.]]
+
+[[Throws:] [Any exceptions thrown by the copy (or move) constructor of `f`. `std::bad_alloc` if memory for the internal data
+structures could not be allocated.]]
+
+]
+
+[endsect]
+
+[section:move_constructor Move Constructor]
+
+ packaged_task(packaged_task && other);
+
+[variablelist
+
+[[Effects:] [Constructs a new __packaged_task__, and transfers ownership of the task associated with `other` to `*this`, leaving `other`
+with no associated task.]]
+
+[[Throws:] [Nothing.]]
+
+[[Notes:] [If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.]]
+
+]
+
+[endsect]
+
+[section:move_assignment Move Assignment Operator]
+
+ packaged_task& operator=(packaged_task && other);
+
+[variablelist
+
+[[Effects:] [Transfers ownership of the task associated with `other` to `*this`, leaving `other` with no associated task. If there
+was already a task associated with `*this`, and that task has not been invoked, sets any futures associated with that task to
+['ready] with a __broken_promise__ exception as the result. ]]
+
+[[Throws:] [Nothing.]]
+
+[[Notes:] [If the compiler does not support rvalue-references, this is implemented using the boost.thread move emulation.]]
+
+]
+
+[endsect]
+
+[section:destructor Destructor]
+
+ ~packaged_task();
+
+[variablelist
+
+[[Effects:] [Destroys `*this`. If there was a task associated with `*this`, and that task has not been invoked, sets any futures
+associated with that task to ['ready] with a __broken_promise__ exception as the result.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+
+[section:get_future Member Function `get_future()`]
+
+ unique_future<R> get_future();
+
+[variablelist
+
+[[Effects:] [Returns a __unique_future__ associated with the result of the task associated with `*this`. ]]
+
+[[Throws:] [__task_moved__ if ownership of the task associated with `*this` has been moved to another instance of
+__packaged_task__. __future_already_retrieved__ if the future associated with the task has already been retrieved.]]
+
+]
+
+[endsect]
+
+[section:call_operator Member Function `operator()()`]
+
+ void operator()();
+
+[variablelist
+
+[[Effects:] [Invoke the task associated with `*this` and store the result in the corresponding future. If the task returns normally,
+the return value is stored as the asynchronous result, otherwise the exception thrown is stored. Any threads blocked waiting for the
+asynchronous result associated with this task are woken.]]
+
+[[Postconditions:] [All futures waiting on the asynchronous result are ['ready]]]
+
+[[Throws:] [__task_moved__ if ownership of the task associated with `*this` has been moved to another instance of
+__packaged_task__. __task_already_started__ if the task has already been invoked.]]
+
+]
+
+[endsect]
+
+[section:set_wait_callback Member Function `set_wait_callback()`]
+
+ template<typename F>
+ void set_wait_callback(F f);
+
+[variablelist
+
+[[Preconditions:] [The expression `f(t)` where `t` is a lvalue of type __packaged_task__ shall be well-formed. Invoking a copy of
+`f` shall have the same effect as invoking `f`]]
+
+[[Effects:] [Store a copy of `f` with the task associated with `*this` as a ['wait callback]. This will replace any existing wait
+callback store alongside that task. If a thread subsequently calls one of the wait functions on a __unique_future__ or
+__shared_future__ associated with this task, and the result of the task is not ['ready], `f(*this)` shall be invoked.]]
+
+[[Throws:] [__task_moved__ if ownership of the task associated with `*this` has been moved to another instance of
+__packaged_task__.]]
+
+]
+
+[endsect]
+
+
+[endsect]
+
+[section:wait_for_any Non-member function `wait_for_any()`]
+
+ template<typename Iterator>
+ Iterator wait_for_any(Iterator begin,Iterator end);
+
+ template<typename F1,typename F2>
+ unsigned wait_for_any(F1& f1,F2& f2);
+
+ template<typename F1,typename F2,typename F3>
+ unsigned wait_for_any(F1& f1,F2& f2,F3& f3);
+
+ template<typename F1,typename F2,typename F3,typename F4>
+ unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4);
+
+ template<typename F1,typename F2,typename F3,typename F4,typename F5>
+ unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5);
+
+[variablelist
+
+[[Preconditions:] [The types `Fn` shall be specializations of
+__unique_future__ or __shared_future__, and `Iterator` shall be a
+forward iterator with a `value_type` which is a specialization of
+__unique_future__ or __shared_future__.]]
+
+[[Effects:] [Waits until at least one of the specified futures is ['ready].]]
+
+[[Returns:] [The range-based overload returns an `Iterator` identifying the first future in the range that was detected as
+['ready]. The remaining overloads return the zero-based index of the first future that was detected as ['ready] (first parameter =>
+0, second parameter => 1, etc.).]]
+
+[[Throws:] [__thread_interrupted__ if the current thread is interrupted. Any exception thrown by the ['wait callback] associated
+with any of the futures being waited for. `std::bad_alloc` if memory could not be allocated for the internal wait structures.]]
+
+[[Notes:] [`wait_for_any()` is an ['interruption point].]]
+
+]
+
+
+[endsect]
+
+[section:wait_for_all Non-member function `wait_for_all()`]
+
+ template<typename Iterator>
+ void wait_for_all(Iterator begin,Iterator end);
+
+ template<typename F1,typename F2>
+ void wait_for_all(F1& f1,F2& f2);
+
+ template<typename F1,typename F2,typename F3>
+ void wait_for_all(F1& f1,F2& f2,F3& f3);
+
+ template<typename F1,typename F2,typename F3,typename F4>
+ void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4);
+
+ template<typename F1,typename F2,typename F3,typename F4,typename F5>
+ void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5);
+
+[variablelist
+
+[[Preconditions:] [The types `Fn` shall be specializations of
+__unique_future__ or __shared_future__, and `Iterator` shall be a
+forward iterator with a `value_type` which is a specialization of
+__unique_future__ or __shared_future__.]]
+
+[[Effects:] [Waits until all of the specified futures are ['ready].]]
+
+[[Throws:] [Any exceptions thrown by a call to `wait()` on the specified futures.]]
+
+[[Notes:] [`wait_for_all()` is an ['interruption point].]]
+
+]
+
+
+[endsect]
+
+
+[endsect]
Added: trunk/libs/thread/doc/futures.qbk
==============================================================================
--- (empty file)
+++ trunk/libs/thread/doc/futures.qbk 2009-10-22 05:33:21 EDT (Thu, 22 Oct 2009)
@@ -0,0 +1,187 @@
+[/
+ (C) Copyright 2008-9 Anthony Williams.
+ 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).
+]
+
+[section:futures Futures]
+
+[template future_state_link[link_text] [link thread.synchronization.futures.reference.future_state [link_text]]]
+[def __uninitialized__ [future_state_link `boost::future_state::uninitialized`]]
+[def __ready__ [future_state_link `boost::future_state::ready`]]
+[def __waiting__ [future_state_link `boost::future_state::waiting`]]
+
+[def __future_uninitialized__ `boost::future_uninitialized`]
+[def __broken_promise__ `boost::broken_promise`]
+[def __future_already_retrieved__ `boost::future_already_retrieved`]
+[def __task_moved__ `boost::task_moved`]
+[def __task_already_started__ `boost::task_already_started`]
+[def __promise_already_satisfied__ `boost::promise_already_satisfied`]
+
+[def __thread_interrupted__ `boost::thread_interrupted`]
+
+
+[template unique_future_link[link_text] [link thread.synchronization.futures.reference.unique_future [link_text]]]
+[def __unique_future__ [unique_future_link `boost::unique_future`]]
+
+[template unique_future_get_link[link_text] [link thread.synchronization.futures.reference.unique_future.get [link_text]]]
+[def __unique_future_get__ [unique_future_get_link `boost::unique_future<R>::get()`]]
+
+[template unique_future_wait_link[link_text] [link thread.synchronization.futures.reference.unique_future.wait [link_text]]]
+[def __unique_future_wait__ [unique_future_wait_link `boost::unique_future<R>::wait()`]]
+
+[template unique_future_is_ready_link[link_text] [link thread.synchronization.futures.reference.unique_future.is_ready [link_text]]]
+[def __unique_future_is_ready__ [unique_future_is_ready_link `boost::unique_future<R>::is_ready()`]]
+
+[template unique_future_has_value_link[link_text] [link thread.synchronization.futures.reference.unique_future.has_value [link_text]]]
+[def __unique_future_has_value__ [unique_future_has_value_link `boost::unique_future<R>::has_value()`]]
+
+[template unique_future_has_exception_link[link_text] [link thread.synchronization.futures.reference.unique_future.has_exception [link_text]]]
+[def __unique_future_has_exception__ [unique_future_has_exception_link `boost::unique_future<R>::has_exception()`]]
+
+[template unique_future_get_state_link[link_text] [link thread.synchronization.futures.reference.unique_future.get_state [link_text]]]
+[def __unique_future_get_state__ [unique_future_get_state_link `boost::unique_future<R>::get_state()`]]
+
+[template shared_future_link[link_text] [link thread.synchronization.futures.reference.shared_future [link_text]]]
+[def __shared_future__ [shared_future_link `boost::shared_future`]]
+
+[template shared_future_get_link[link_text] [link thread.synchronization.futures.reference.shared_future.get [link_text]]]
+[def __shared_future_get__ [shared_future_get_link `boost::shared_future<R>::get()`]]
+
+[template shared_future_wait_link[link_text] [link thread.synchronization.futures.reference.shared_future.wait [link_text]]]
+[def __shared_future_wait__ [shared_future_wait_link `boost::shared_future<R>::wait()`]]
+
+[template shared_future_is_ready_link[link_text] [link thread.synchronization.futures.reference.shared_future.is_ready [link_text]]]
+[def __shared_future_is_ready__ [shared_future_is_ready_link `boost::shared_future<R>::is_ready()`]]
+
+[template shared_future_has_value_link[link_text] [link thread.synchronization.futures.reference.shared_future.has_value [link_text]]]
+[def __shared_future_has_value__ [shared_future_has_value_link `boost::shared_future<R>::has_value()`]]
+
+[template shared_future_has_exception_link[link_text] [link thread.synchronization.futures.reference.shared_future.has_exception [link_text]]]
+[def __shared_future_has_exception__ [shared_future_has_exception_link `boost::shared_future<R>::has_exception()`]]
+
+[template shared_future_get_state_link[link_text] [link thread.synchronization.futures.reference.shared_future.get_state [link_text]]]
+[def __shared_future_get_state__ [shared_future_get_state_link `boost::shared_future<R>::get_state()`]]
+
+[template promise_link[link_text] [link thread.synchronization.futures.reference.promise [link_text]]]
+[def __promise__ [promise_link `boost::promise`]]
+
+[template packaged_task_link[link_text] [link thread.synchronization.futures.reference.packaged_task [link_text]]]
+[def __packaged_task__ [packaged_task_link `boost::packaged_task`]]
+
+[template wait_for_any_link[link_text] [link thread.synchronization.futures.reference.wait_for_any [link_text]]]
+[def __wait_for_any__ [wait_for_any_link `boost::wait_for_any()`]]
+
+[template wait_for_all_link[link_text] [link thread.synchronization.futures.reference.wait_for_all [link_text]]]
+[def __wait_for_all__ [wait_for_all_link `boost::wait_for_all()`]]
+
+
+[section:overview Overview]
+
+The futures library provides a means of handling synchronous future values, whether those values are generated by another thread, or
+on a single thread in response to external stimuli, or on-demand.
+
+This is done through the provision of four class templates: __unique_future__ and __shared_future__ which are used to retrieve the
+asynchronous results, and __promise__ and __packaged_task__ which are used to generate the asynchronous results.
+
+An instance of __unique_future__ holds the one and only reference to a result. Ownership can be transferred between instances using
+the move constructor or move-assignment operator, but at most one instance holds a reference to a given asynchronous result. When
+the result is ready, it is returned from __unique_future_get__ by rvalue-reference to allow the result to be moved or copied as
+appropriate for the type.
+
+On the other hand, many instances of __shared_future__ may reference the same result. Instances can be freely copied and assigned,
+and __shared_future_get__ returns a `const` reference so that multiple calls to __shared_future_get__ are safe. You can move an
+instance of __unique_future__ into an instance of __shared_future__, thus transferring ownership of the associated asynchronous
+result, but not vice-versa.
+
+You can wait for futures either individually or with one of the __wait_for_any__ and __wait_for_all__ functions.
+
+[endsect]
+
+[section:creating Creating asynchronous values]
+
+You can set the value in a future with either a __promise__ or a __packaged_task__. A __packaged_task__ is a callable object that
+wraps a function or callable object. When the packaged task is invoked, it invokes the contained function in turn, and populates a
+future with the return value. This is an answer to the perennial question: "how do I return a value from a thread?": package the
+function you wish to run as a __packaged_task__ and pass the packaged task to the thread constructor. The future retrieved from the
+packaged task can then be used to obtain the return value. If the function throws an exception, that is stored in the future in
+place of the return value.
+
+ int calculate_the_answer_to_life_the_universe_and_everything()
+ {
+ return 42;
+ }
+
+ boost::packaged_task<int> pt(calculate_the_answer_to_life_the_universe_and_everything);
+ boost::unique_future<int> fi=pt.get_future();
+
+ boost::thread task(boost::move(pt)); // launch task on a thread
+
+ fi.wait(); // wait for it to finish
+
+ assert(fi.is_ready());
+ assert(fi.has_value());
+ assert(!fi.has_exception());
+ assert(fi.get_state()==boost::future_state::ready);
+ assert(fi.get()==42);
+
+
+A __promise__ is a bit more low level: it just provides explicit functions to store a value or an exception in the associated
+future. A promise can therefore be used where the value may come from more than one possible source, or where a single operation may
+produce multiple values.
+
+ boost::promise<int> pi;
+ boost::unique_future<int> fi;
+ fi=pi.get_future();
+
+ pi.set_value(42);
+
+ assert(fi.is_ready());
+ assert(fi.has_value());
+ assert(!fi.has_exception());
+ assert(fi.get_state()==boost::future_state::ready);
+ assert(fi.get()==42);
+
+[endsect]
+
+[section:lazy_futures Wait Callbacks and Lazy Futures]
+
+Both __promise__ and __packaged_task__ support ['wait callbacks] that are invoked when a thread blocks in a call to `wait()` or
+`timed_wait()` on a future that is waiting for the result from the __promise__ or __packaged_task__, in the thread that is doing the
+waiting. These can be set using the `set_wait_callback()` member function on the __promise__ or __packaged_task__ in question.
+
+This allows ['lazy futures] where the result is not actually computed until it is needed by some thread. In the example below, the
+call to `f.get()` invokes the callback `invoke_lazy_task`, which runs the task to set the value. If you remove the call to
+`f.get()`, the task is not ever run.
+
+ int calculate_the_answer_to_life_the_universe_and_everything()
+ {
+ return 42;
+ }
+
+ void invoke_lazy_task(boost::packaged_task<int>& task)
+ {
+ try
+ {
+ task();
+ }
+ catch(boost::task_already_started&)
+ {}
+ }
+
+ int main()
+ {
+ boost::packaged_task<int> task(calculate_the_answer_to_life_the_universe_and_everything);
+ task.set_wait_callback(invoke_lazy_task);
+ boost::unique_future<int> f(task.get_future());
+
+ assert(f.get()==42);
+ }
+
+
+[endsect]
+
+[include future_ref.qbk]
+
+[endsect]
\ No newline at end of file
Modified: trunk/libs/thread/doc/thread.qbk
==============================================================================
--- trunk/libs/thread/doc/thread.qbk (original)
+++ trunk/libs/thread/doc/thread.qbk 2009-10-22 05:33:21 EDT (Thu, 22 Oct 2009)
@@ -158,6 +158,7 @@
[include condition_variables.qbk]
[include once.qbk]
[include barrier.qbk]
+[include futures.qbk]
[endsect]
[include tss.qbk]
Modified: trunk/libs/thread/test/Jamfile.v2
==============================================================================
--- trunk/libs/thread/test/Jamfile.v2 (original)
+++ trunk/libs/thread/test/Jamfile.v2 2009-10-22 05:33:21 EDT (Thu, 22 Oct 2009)
@@ -57,6 +57,7 @@
[ thread-run test_shared_mutex_timed_locks.cpp ]
[ thread-run test_lock_concept.cpp ]
[ thread-run test_generic_locks.cpp ]
+ [ thread-run test_futures.cpp ]
[ compile-fail no_implicit_move_from_lvalue_thread.cpp ]
[ compile-fail no_implicit_assign_from_lvalue_thread.cpp ]
;
Added: trunk/libs/thread/test/test_futures.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/thread/test/test_futures.cpp 2009-10-22 05:33:21 EDT (Thu, 22 Oct 2009)
@@ -0,0 +1,1218 @@
+// (C) Copyright 2008-9 Anthony Williams
+//
+// 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 "boost/thread/thread.hpp"
+#include "boost/thread/mutex.hpp"
+#include "boost/thread/condition.hpp"
+#include "boost/thread/future.hpp"
+#include <utility>
+#include <memory>
+#include <string>
+
+#include <boost/test/unit_test.hpp>
+
+#ifdef BOOST_HAS_RVALUE_REFS
+ template<typename T>
+ typename boost::remove_reference<T>::type&& cast_to_rval(T&& t)
+ {
+ return t;
+ }
+#else
+ template<typename T>
+ boost::detail::thread_move_t<T> cast_to_rval(T& t)
+ {
+ return boost::move(t);
+ }
+#endif
+
+struct X
+{
+private:
+
+ X(X& other);
+
+public:
+
+ int i;
+
+ X():
+ i(42)
+ {}
+#ifdef BOOST_HAS_RVALUE_REFS
+ X(X&& other):
+ i(other.i)
+ {
+ other.i=0;
+ }
+#else
+ X(boost::detail::thread_move_t<X> other):
+ i(other->i)
+ {
+ other->i=0;
+ }
+ operator boost::detail::thread_move_t<X>()
+ {
+ return boost::detail::thread_move_t<X>(*this);
+ }
+#endif
+ ~X()
+ {}
+};
+
+int make_int()
+{
+ return 42;
+}
+
+int throw_runtime_error()
+{
+ throw std::runtime_error("42");
+}
+
+void set_promise_thread(boost::promise<int>* p)
+{
+ p->set_value(42);
+}
+
+struct my_exception
+{};
+
+void set_promise_exception_thread(boost::promise<int>* p)
+{
+ p->set_exception(boost::copy_exception(my_exception()));
+}
+
+
+void test_store_value_from_thread()
+{
+ boost::promise<int> pi2;
+ boost::unique_future<int> fi2=pi2.get_future();
+ boost::thread(set_promise_thread,&pi2);
+ int j=fi2.get();
+ BOOST_CHECK(j==42);
+ BOOST_CHECK(fi2.is_ready());
+ BOOST_CHECK(fi2.has_value());
+ BOOST_CHECK(!fi2.has_exception());
+ BOOST_CHECK(fi2.get_state()==boost::future_state::ready);
+}
+
+
+void test_store_exception()
+{
+ boost::promise<int> pi3;
+ boost::unique_future<int> fi3=pi3.get_future();
+ boost::thread(set_promise_exception_thread,&pi3);
+ try
+ {
+ fi3.get();
+ BOOST_CHECK(false);
+ }
+ catch(my_exception)
+ {
+ BOOST_CHECK(true);
+ }
+
+ BOOST_CHECK(fi3.is_ready());
+ BOOST_CHECK(!fi3.has_value());
+ BOOST_CHECK(fi3.has_exception());
+ BOOST_CHECK(fi3.get_state()==boost::future_state::ready);
+}
+
+void test_initial_state()
+{
+ boost::unique_future<int> fi;
+ BOOST_CHECK(!fi.is_ready());
+ BOOST_CHECK(!fi.has_value());
+ BOOST_CHECK(!fi.has_exception());
+ BOOST_CHECK(fi.get_state()==boost::future_state::uninitialized);
+ int i;
+ try
+ {
+ i=fi.get();
+ BOOST_CHECK(false);
+ }
+ catch(boost::future_uninitialized)
+ {
+ BOOST_CHECK(true);
+ }
+}
+
+void test_waiting_future()
+{
+ boost::promise<int> pi;
+ boost::unique_future<int> fi;
+ fi=pi.get_future();
+
+ int i=0;
+ BOOST_CHECK(!fi.is_ready());
+ BOOST_CHECK(!fi.has_value());
+ BOOST_CHECK(!fi.has_exception());
+ BOOST_CHECK(fi.get_state()==boost::future_state::waiting);
+ BOOST_CHECK(i==0);
+}
+
+void test_cannot_get_future_twice()
+{
+ boost::promise<int> pi;
+ pi.get_future();
+
+ try
+ {
+ pi.get_future();
+ BOOST_CHECK(false);
+ }
+ catch(boost::future_already_retrieved&)
+ {
+ BOOST_CHECK(true);
+ }
+}
+
+void test_set_value_updates_future_state()
+{
+ boost::promise<int> pi;
+ boost::unique_future<int> fi;
+ fi=pi.get_future();
+
+ pi.set_value(42);
+
+ BOOST_CHECK(fi.is_ready());
+ BOOST_CHECK(fi.has_value());
+ BOOST_CHECK(!fi.has_exception());
+ BOOST_CHECK(fi.get_state()==boost::future_state::ready);
+}
+
+void test_set_value_can_be_retrieved()
+{
+ boost::promise<int> pi;
+ boost::unique_future<int> fi;
+ fi=pi.get_future();
+
+ pi.set_value(42);
+
+ int i=0;
+ BOOST_CHECK(i=fi.get());
+ BOOST_CHECK(i==42);
+ BOOST_CHECK(fi.is_ready());
+ BOOST_CHECK(fi.has_value());
+ BOOST_CHECK(!fi.has_exception());
+ BOOST_CHECK(fi.get_state()==boost::future_state::ready);
+}
+
+void test_set_value_can_be_moved()
+{
+// boost::promise<int> pi;
+// boost::unique_future<int> fi;
+// fi=pi.get_future();
+
+// pi.set_value(42);
+
+// int i=0;
+// BOOST_CHECK(i=fi.get());
+// BOOST_CHECK(i==42);
+// BOOST_CHECK(fi.is_ready());
+// BOOST_CHECK(fi.has_value());
+// BOOST_CHECK(!fi.has_exception());
+// BOOST_CHECK(fi.get_state()==boost::future_state::ready);
+}
+
+void test_future_from_packaged_task_is_waiting()
+{
+ boost::packaged_task<int> pt(make_int);
+ boost::unique_future<int> fi=pt.get_future();
+ int i=0;
+ BOOST_CHECK(!fi.is_ready());
+ BOOST_CHECK(!fi.has_value());
+ BOOST_CHECK(!fi.has_exception());
+ BOOST_CHECK(fi.get_state()==boost::future_state::waiting);
+ BOOST_CHECK(i==0);
+}
+
+void test_invoking_a_packaged_task_populates_future()
+{
+ boost::packaged_task<int> pt(make_int);
+ boost::unique_future<int> fi=pt.get_future();
+
+ pt();
+
+ int i=0;
+ BOOST_CHECK(fi.is_ready());
+ BOOST_CHECK(fi.has_value());
+ BOOST_CHECK(!fi.has_exception());
+ BOOST_CHECK(fi.get_state()==boost::future_state::ready);
+ BOOST_CHECK(i=fi.get());
+ BOOST_CHECK(i==42);
+}
+
+void test_invoking_a_packaged_task_twice_throws()
+{
+ boost::packaged_task<int> pt(make_int);
+
+ pt();
+ try
+ {
+ pt();
+ BOOST_CHECK(false);
+ }
+ catch(boost::task_already_started)
+ {
+ BOOST_CHECK(true);
+ }
+}
+
+
+void test_cannot_get_future_twice_from_task()
+{
+ boost::packaged_task<int> pt(make_int);
+ pt.get_future();
+ try
+ {
+ pt.get_future();
+ BOOST_CHECK(false);
+ }
+ catch(boost::future_already_retrieved)
+ {
+ BOOST_CHECK(true);
+ }
+}
+
+void test_task_stores_exception_if_function_throws()
+{
+ boost::packaged_task<int> pt(throw_runtime_error);
+ boost::unique_future<int> fi=pt.get_future();
+
+ pt();
+
+ BOOST_CHECK(fi.is_ready());
+ BOOST_CHECK(!fi.has_value());
+ BOOST_CHECK(fi.has_exception());
+ BOOST_CHECK(fi.get_state()==boost::future_state::ready);
+ try
+ {
+ fi.get();
+ BOOST_CHECK(false);
+ }
+ catch(std::exception&)
+ {
+ BOOST_CHECK(true);
+ }
+ catch(...)
+ {
+ BOOST_CHECK(!"Unknown exception thrown");
+ }
+
+}
+
+void test_void_promise()
+{
+ boost::promise<void> p;
+ boost::unique_future<void> f=p.get_future();
+ p.set_value();
+ BOOST_CHECK(f.is_ready());
+ BOOST_CHECK(f.has_value());
+ BOOST_CHECK(!f.has_exception());
+ BOOST_CHECK(f.get_state()==boost::future_state::ready);
+ f.get();
+}
+
+void test_reference_promise()
+{
+ boost::promise<int&> p;
+ boost::unique_future<int&> f=p.get_future();
+ int i=42;
+ p.set_value(i);
+ BOOST_CHECK(f.is_ready());
+ BOOST_CHECK(f.has_value());
+ BOOST_CHECK(!f.has_exception());
+ BOOST_CHECK(f.get_state()==boost::future_state::ready);
+ BOOST_CHECK(&f.get()==&i);
+}
+
+void do_nothing()
+{}
+
+void test_task_returning_void()
+{
+ boost::packaged_task<void> pt(do_nothing);
+ boost::unique_future<void> fi=pt.get_future();
+
+ pt();
+
+ BOOST_CHECK(fi.is_ready());
+ BOOST_CHECK(fi.has_value());
+ BOOST_CHECK(!fi.has_exception());
+ BOOST_CHECK(fi.get_state()==boost::future_state::ready);
+}
+
+int global_ref_target=0;
+
+int& return_ref()
+{
+ return global_ref_target;
+}
+
+void test_task_returning_reference()
+{
+ boost::packaged_task<int&> pt(return_ref);
+ boost::unique_future<int&> fi=pt.get_future();
+
+ pt();
+
+ BOOST_CHECK(fi.is_ready());
+ BOOST_CHECK(fi.has_value());
+ BOOST_CHECK(!fi.has_exception());
+ BOOST_CHECK(fi.get_state()==boost::future_state::ready);
+ int& i=fi.get();
+ BOOST_CHECK(&i==&global_ref_target);
+}
+
+void test_shared_future()
+{
+ boost::packaged_task<int> pt(make_int);
+ boost::unique_future<int> fi=pt.get_future();
+
+ boost::shared_future<int> sf(::cast_to_rval(fi));
+ BOOST_CHECK(fi.get_state()==boost::future_state::uninitialized);
+
+ pt();
+
+ int i=0;
+ BOOST_CHECK(sf.is_ready());
+ BOOST_CHECK(sf.has_value());
+ BOOST_CHECK(!sf.has_exception());
+ BOOST_CHECK(sf.get_state()==boost::future_state::ready);
+ BOOST_CHECK(i=sf.get());
+ BOOST_CHECK(i==42);
+}
+
+void test_copies_of_shared_future_become_ready_together()
+{
+ boost::packaged_task<int> pt(make_int);
+ boost::unique_future<int> fi=pt.get_future();
+
+ boost::shared_future<int> sf(::cast_to_rval(fi));
+ boost::shared_future<int> sf2(sf);
+ boost::shared_future<int> sf3;
+ sf3=sf;
+ BOOST_CHECK(sf.get_state()==boost::future_state::waiting);
+ BOOST_CHECK(sf2.get_state()==boost::future_state::waiting);
+ BOOST_CHECK(sf3.get_state()==boost::future_state::waiting);
+
+ pt();
+
+ int i=0;
+ BOOST_CHECK(sf.is_ready());
+ BOOST_CHECK(sf.has_value());
+ BOOST_CHECK(!sf.has_exception());
+ BOOST_CHECK(sf.get_state()==boost::future_state::ready);
+ BOOST_CHECK(i=sf.get());
+ BOOST_CHECK(i==42);
+ i=0;
+ BOOST_CHECK(sf2.is_ready());
+ BOOST_CHECK(sf2.has_value());
+ BOOST_CHECK(!sf2.has_exception());
+ BOOST_CHECK(sf2.get_state()==boost::future_state::ready);
+ BOOST_CHECK(i=sf2.get());
+ BOOST_CHECK(i==42);
+ i=0;
+ BOOST_CHECK(sf3.is_ready());
+ BOOST_CHECK(sf3.has_value());
+ BOOST_CHECK(!sf3.has_exception());
+ BOOST_CHECK(sf3.get_state()==boost::future_state::ready);
+ BOOST_CHECK(i=sf3.get());
+ BOOST_CHECK(i==42);
+}
+
+void test_shared_future_can_be_move_assigned_from_unique_future()
+{
+ boost::packaged_task<int> pt(make_int);
+ boost::unique_future<int> fi=pt.get_future();
+
+ boost::shared_future<int> sf;
+ sf=::cast_to_rval(fi);
+ BOOST_CHECK(fi.get_state()==boost::future_state::uninitialized);
+
+ BOOST_CHECK(!sf.is_ready());
+ BOOST_CHECK(!sf.has_value());
+ BOOST_CHECK(!sf.has_exception());
+ BOOST_CHECK(sf.get_state()==boost::future_state::waiting);
+}
+
+void test_shared_future_void()
+{
+ boost::packaged_task<void> pt(do_nothing);
+ boost::unique_future<void> fi=pt.get_future();
+
+ boost::shared_future<void> sf(::cast_to_rval(fi));
+ BOOST_CHECK(fi.get_state()==boost::future_state::uninitialized);
+
+ pt();
+
+ BOOST_CHECK(sf.is_ready());
+ BOOST_CHECK(sf.has_value());
+ BOOST_CHECK(!sf.has_exception());
+ BOOST_CHECK(sf.get_state()==boost::future_state::ready);
+ sf.get();
+}
+
+void test_shared_future_ref()
+{
+ boost::promise<int&> p;
+ boost::shared_future<int&> f(p.get_future());
+ int i=42;
+ p.set_value(i);
+ BOOST_CHECK(f.is_ready());
+ BOOST_CHECK(f.has_value());
+ BOOST_CHECK(!f.has_exception());
+ BOOST_CHECK(f.get_state()==boost::future_state::ready);
+ BOOST_CHECK(&f.get()==&i);
+}
+
+void test_can_get_a_second_future_from_a_moved_promise()
+{
+ boost::promise<int> pi;
+ boost::unique_future<int> fi=pi.get_future();
+
+ boost::promise<int> pi2(::cast_to_rval(pi));
+ boost::unique_future<int> fi2=pi.get_future();
+
+ pi2.set_value(3);
+ BOOST_CHECK(fi.is_ready());
+ BOOST_CHECK(!fi2.is_ready());
+ BOOST_CHECK(fi.get()==3);
+ pi.set_value(42);
+ BOOST_CHECK(fi2.is_ready());
+ BOOST_CHECK(fi2.get()==42);
+}
+
+void test_can_get_a_second_future_from_a_moved_void_promise()
+{
+ boost::promise<void> pi;
+ boost::unique_future<void> fi=pi.get_future();
+
+ boost::promise<void> pi2(::cast_to_rval(pi));
+ boost::unique_future<void> fi2=pi.get_future();
+
+ pi2.set_value();
+ BOOST_CHECK(fi.is_ready());
+ BOOST_CHECK(!fi2.is_ready());
+ pi.set_value();
+ BOOST_CHECK(fi2.is_ready());
+}
+
+void test_unique_future_for_move_only_udt()
+{
+ boost::promise<X> pt;
+ boost::unique_future<X> fi=pt.get_future();
+
+ pt.set_value(X());
+ X res(fi.get());
+ BOOST_CHECK(res.i==42);
+}
+
+void test_unique_future_for_string()
+{
+ boost::promise<std::string> pt;
+ boost::unique_future<std::string> fi=pt.get_future();
+
+ pt.set_value(std::string("hello"));
+ std::string res(fi.get());
+ BOOST_CHECK(res=="hello");
+
+ boost::promise<std::string> pt2;
+ fi=pt2.get_future();
+
+ std::string const s="goodbye";
+
+ pt2.set_value(s);
+ res=fi.get();
+ BOOST_CHECK(res=="goodbye");
+
+ boost::promise<std::string> pt3;
+ fi=pt3.get_future();
+
+ std::string s2="foo";
+
+ pt3.set_value(s2);
+ res=fi.get();
+ BOOST_CHECK(res=="foo");
+}
+
+boost::mutex callback_mutex;
+unsigned callback_called=0;
+
+void wait_callback(boost::promise<int>& pi)
+{
+ boost::lock_guard<boost::mutex> lk(callback_mutex);
+ ++callback_called;
+ try
+ {
+ pi.set_value(42);
+ }
+ catch(...)
+ {
+ }
+}
+
+void do_nothing_callback(boost::promise<int>& pi)
+{
+ boost::lock_guard<boost::mutex> lk(callback_mutex);
+ ++callback_called;
+}
+
+void test_wait_callback()
+{
+ callback_called=0;
+ boost::promise<int> pi;
+ boost::unique_future<int> fi=pi.get_future();
+ pi.set_wait_callback(wait_callback);
+ fi.wait();
+ BOOST_CHECK(callback_called);
+ BOOST_CHECK(fi.get()==42);
+ fi.wait();
+ fi.wait();
+ BOOST_CHECK(callback_called==1);
+}
+
+void test_wait_callback_with_timed_wait()
+{
+ callback_called=0;
+ boost::promise<int> pi;
+ boost::unique_future<int> fi=pi.get_future();
+ pi.set_wait_callback(do_nothing_callback);
+ bool success=fi.timed_wait(boost::posix_time::milliseconds(10));
+ BOOST_CHECK(callback_called);
+ BOOST_CHECK(!success);
+ success=fi.timed_wait(boost::posix_time::milliseconds(10));
+ BOOST_CHECK(!success);
+ success=fi.timed_wait(boost::posix_time::milliseconds(10));
+ BOOST_CHECK(!success);
+ BOOST_CHECK(callback_called==3);
+ pi.set_value(42);
+ success=fi.timed_wait(boost::posix_time::milliseconds(10));
+ BOOST_CHECK(success);
+ BOOST_CHECK(callback_called==3);
+ BOOST_CHECK(fi.get()==42);
+ BOOST_CHECK(callback_called==3);
+}
+
+
+void wait_callback_for_task(boost::packaged_task<int>& pt)
+{
+ boost::lock_guard<boost::mutex> lk(callback_mutex);
+ ++callback_called;
+ try
+ {
+ pt();
+ }
+ catch(...)
+ {
+ }
+}
+
+
+void test_wait_callback_for_packaged_task()
+{
+ callback_called=0;
+ boost::packaged_task<int> pt(make_int);
+ boost::unique_future<int> fi=pt.get_future();
+ pt.set_wait_callback(wait_callback_for_task);
+ fi.wait();
+ BOOST_CHECK(callback_called);
+ BOOST_CHECK(fi.get()==42);
+ fi.wait();
+ fi.wait();
+ BOOST_CHECK(callback_called==1);
+}
+
+void test_packaged_task_can_be_moved()
+{
+ boost::packaged_task<int> pt(make_int);
+
+ boost::unique_future<int> fi=pt.get_future();
+
+ BOOST_CHECK(!fi.is_ready());
+
+ boost::packaged_task<int> pt2(::cast_to_rval(pt));
+
+ BOOST_CHECK(!fi.is_ready());
+ try
+ {
+ pt();
+ BOOST_CHECK(!"Can invoke moved task!");
+ }
+ catch(boost::task_moved&)
+ {
+ }
+
+ BOOST_CHECK(!fi.is_ready());
+
+ pt2();
+
+ BOOST_CHECK(fi.is_ready());
+}
+
+void test_destroying_a_promise_stores_broken_promise()
+{
+ boost::unique_future<int> f;
+
+ {
+ boost::promise<int> p;
+ f=p.get_future();
+ }
+ BOOST_CHECK(f.is_ready());
+ BOOST_CHECK(f.has_exception());
+ try
+ {
+ f.get();
+ }
+ catch(boost::broken_promise&)
+ {
+ }
+}
+
+void test_destroying_a_packaged_task_stores_broken_promise()
+{
+ boost::unique_future<int> f;
+
+ {
+ boost::packaged_task<int> p(make_int);
+ f=p.get_future();
+ }
+ BOOST_CHECK(f.is_ready());
+ BOOST_CHECK(f.has_exception());
+ try
+ {
+ f.get();
+ }
+ catch(boost::broken_promise&)
+ {
+ }
+}
+
+int make_int_slowly()
+{
+ boost::this_thread::sleep(boost::posix_time::seconds(1));
+ return 42;
+}
+
+void test_wait_for_either_of_two_futures_1()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+
+ boost::thread(::cast_to_rval(pt));
+
+ unsigned const future=boost::wait_for_any(f1,f2);
+
+ BOOST_CHECK(future==0);
+ BOOST_CHECK(f1.is_ready());
+ BOOST_CHECK(!f2.is_ready());
+ BOOST_CHECK(f1.get()==42);
+}
+
+void test_wait_for_either_of_two_futures_2()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+
+ boost::thread(::cast_to_rval(pt2));
+
+ unsigned const future=boost::wait_for_any(f1,f2);
+
+ BOOST_CHECK(future==1);
+ BOOST_CHECK(!f1.is_ready());
+ BOOST_CHECK(f2.is_ready());
+ BOOST_CHECK(f2.get()==42);
+}
+
+void test_wait_for_either_of_three_futures_1()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+ boost::packaged_task<int> pt3(make_int_slowly);
+ boost::unique_future<int> f3(pt3.get_future());
+
+ boost::thread(::cast_to_rval(pt));
+
+ unsigned const future=boost::wait_for_any(f1,f2,f3);
+
+ BOOST_CHECK(future==0);
+ BOOST_CHECK(f1.is_ready());
+ BOOST_CHECK(!f2.is_ready());
+ BOOST_CHECK(!f3.is_ready());
+ BOOST_CHECK(f1.get()==42);
+}
+
+void test_wait_for_either_of_three_futures_2()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+ boost::packaged_task<int> pt3(make_int_slowly);
+ boost::unique_future<int> f3(pt3.get_future());
+
+ boost::thread(::cast_to_rval(pt2));
+
+ unsigned const future=boost::wait_for_any(f1,f2,f3);
+
+ BOOST_CHECK(future==1);
+ BOOST_CHECK(!f1.is_ready());
+ BOOST_CHECK(f2.is_ready());
+ BOOST_CHECK(!f3.is_ready());
+ BOOST_CHECK(f2.get()==42);
+}
+
+void test_wait_for_either_of_three_futures_3()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+ boost::packaged_task<int> pt3(make_int_slowly);
+ boost::unique_future<int> f3(pt3.get_future());
+
+ boost::thread(::cast_to_rval(pt3));
+
+ unsigned const future=boost::wait_for_any(f1,f2,f3);
+
+ BOOST_CHECK(future==2);
+ BOOST_CHECK(!f1.is_ready());
+ BOOST_CHECK(!f2.is_ready());
+ BOOST_CHECK(f3.is_ready());
+ BOOST_CHECK(f3.get()==42);
+}
+
+void test_wait_for_either_of_four_futures_1()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+ boost::packaged_task<int> pt3(make_int_slowly);
+ boost::unique_future<int> f3(pt3.get_future());
+ boost::packaged_task<int> pt4(make_int_slowly);
+ boost::unique_future<int> f4(pt4.get_future());
+
+ boost::thread(::cast_to_rval(pt));
+
+ unsigned const future=boost::wait_for_any(f1,f2,f3,f4);
+
+ BOOST_CHECK(future==0);
+ BOOST_CHECK(f1.is_ready());
+ BOOST_CHECK(!f2.is_ready());
+ BOOST_CHECK(!f3.is_ready());
+ BOOST_CHECK(!f4.is_ready());
+ BOOST_CHECK(f1.get()==42);
+}
+
+void test_wait_for_either_of_four_futures_2()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+ boost::packaged_task<int> pt3(make_int_slowly);
+ boost::unique_future<int> f3(pt3.get_future());
+ boost::packaged_task<int> pt4(make_int_slowly);
+ boost::unique_future<int> f4(pt4.get_future());
+
+ boost::thread(::cast_to_rval(pt2));
+
+ unsigned const future=boost::wait_for_any(f1,f2,f3,f4);
+
+ BOOST_CHECK(future==1);
+ BOOST_CHECK(!f1.is_ready());
+ BOOST_CHECK(f2.is_ready());
+ BOOST_CHECK(!f3.is_ready());
+ BOOST_CHECK(!f4.is_ready());
+ BOOST_CHECK(f2.get()==42);
+}
+
+void test_wait_for_either_of_four_futures_3()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+ boost::packaged_task<int> pt3(make_int_slowly);
+ boost::unique_future<int> f3(pt3.get_future());
+ boost::packaged_task<int> pt4(make_int_slowly);
+ boost::unique_future<int> f4(pt4.get_future());
+
+ boost::thread(::cast_to_rval(pt3));
+
+ unsigned const future=boost::wait_for_any(f1,f2,f3,f4);
+
+ BOOST_CHECK(future==2);
+ BOOST_CHECK(!f1.is_ready());
+ BOOST_CHECK(!f2.is_ready());
+ BOOST_CHECK(f3.is_ready());
+ BOOST_CHECK(!f4.is_ready());
+ BOOST_CHECK(f3.get()==42);
+}
+
+void test_wait_for_either_of_four_futures_4()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+ boost::packaged_task<int> pt3(make_int_slowly);
+ boost::unique_future<int> f3(pt3.get_future());
+ boost::packaged_task<int> pt4(make_int_slowly);
+ boost::unique_future<int> f4(pt4.get_future());
+
+ boost::thread(::cast_to_rval(pt4));
+
+ unsigned const future=boost::wait_for_any(f1,f2,f3,f4);
+
+ BOOST_CHECK(future==3);
+ BOOST_CHECK(!f1.is_ready());
+ BOOST_CHECK(!f2.is_ready());
+ BOOST_CHECK(!f3.is_ready());
+ BOOST_CHECK(f4.is_ready());
+ BOOST_CHECK(f4.get()==42);
+}
+
+void test_wait_for_either_of_five_futures_1()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+ boost::packaged_task<int> pt3(make_int_slowly);
+ boost::unique_future<int> f3(pt3.get_future());
+ boost::packaged_task<int> pt4(make_int_slowly);
+ boost::unique_future<int> f4(pt4.get_future());
+ boost::packaged_task<int> pt5(make_int_slowly);
+ boost::unique_future<int> f5(pt5.get_future());
+
+ boost::thread(::cast_to_rval(pt));
+
+ unsigned const future=boost::wait_for_any(f1,f2,f3,f4,f5);
+
+ BOOST_CHECK(future==0);
+ BOOST_CHECK(f1.is_ready());
+ BOOST_CHECK(!f2.is_ready());
+ BOOST_CHECK(!f3.is_ready());
+ BOOST_CHECK(!f4.is_ready());
+ BOOST_CHECK(!f5.is_ready());
+ BOOST_CHECK(f1.get()==42);
+}
+
+void test_wait_for_either_of_five_futures_2()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+ boost::packaged_task<int> pt3(make_int_slowly);
+ boost::unique_future<int> f3(pt3.get_future());
+ boost::packaged_task<int> pt4(make_int_slowly);
+ boost::unique_future<int> f4(pt4.get_future());
+ boost::packaged_task<int> pt5(make_int_slowly);
+ boost::unique_future<int> f5(pt5.get_future());
+
+ boost::thread(::cast_to_rval(pt2));
+
+ unsigned const future=boost::wait_for_any(f1,f2,f3,f4,f5);
+
+ BOOST_CHECK(future==1);
+ BOOST_CHECK(!f1.is_ready());
+ BOOST_CHECK(f2.is_ready());
+ BOOST_CHECK(!f3.is_ready());
+ BOOST_CHECK(!f4.is_ready());
+ BOOST_CHECK(!f5.is_ready());
+ BOOST_CHECK(f2.get()==42);
+}
+void test_wait_for_either_of_five_futures_3()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+ boost::packaged_task<int> pt3(make_int_slowly);
+ boost::unique_future<int> f3(pt3.get_future());
+ boost::packaged_task<int> pt4(make_int_slowly);
+ boost::unique_future<int> f4(pt4.get_future());
+ boost::packaged_task<int> pt5(make_int_slowly);
+ boost::unique_future<int> f5(pt5.get_future());
+
+ boost::thread(::cast_to_rval(pt3));
+
+ unsigned const future=boost::wait_for_any(f1,f2,f3,f4,f5);
+
+ BOOST_CHECK(future==2);
+ BOOST_CHECK(!f1.is_ready());
+ BOOST_CHECK(!f2.is_ready());
+ BOOST_CHECK(f3.is_ready());
+ BOOST_CHECK(!f4.is_ready());
+ BOOST_CHECK(!f5.is_ready());
+ BOOST_CHECK(f3.get()==42);
+}
+void test_wait_for_either_of_five_futures_4()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+ boost::packaged_task<int> pt3(make_int_slowly);
+ boost::unique_future<int> f3(pt3.get_future());
+ boost::packaged_task<int> pt4(make_int_slowly);
+ boost::unique_future<int> f4(pt4.get_future());
+ boost::packaged_task<int> pt5(make_int_slowly);
+ boost::unique_future<int> f5(pt5.get_future());
+
+ boost::thread(::cast_to_rval(pt4));
+
+ unsigned const future=boost::wait_for_any(f1,f2,f3,f4,f5);
+
+ BOOST_CHECK(future==3);
+ BOOST_CHECK(!f1.is_ready());
+ BOOST_CHECK(!f2.is_ready());
+ BOOST_CHECK(!f3.is_ready());
+ BOOST_CHECK(f4.is_ready());
+ BOOST_CHECK(!f5.is_ready());
+ BOOST_CHECK(f4.get()==42);
+}
+void test_wait_for_either_of_five_futures_5()
+{
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> f1(pt.get_future());
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> f2(pt2.get_future());
+ boost::packaged_task<int> pt3(make_int_slowly);
+ boost::unique_future<int> f3(pt3.get_future());
+ boost::packaged_task<int> pt4(make_int_slowly);
+ boost::unique_future<int> f4(pt4.get_future());
+ boost::packaged_task<int> pt5(make_int_slowly);
+ boost::unique_future<int> f5(pt5.get_future());
+
+ boost::thread(::cast_to_rval(pt5));
+
+ unsigned const future=boost::wait_for_any(f1,f2,f3,f4,f5);
+
+ BOOST_CHECK(future==4);
+ BOOST_CHECK(!f1.is_ready());
+ BOOST_CHECK(!f2.is_ready());
+ BOOST_CHECK(!f3.is_ready());
+ BOOST_CHECK(!f4.is_ready());
+ BOOST_CHECK(f5.is_ready());
+ BOOST_CHECK(f5.get()==42);
+}
+
+void test_wait_for_either_invokes_callbacks()
+{
+ callback_called=0;
+ boost::packaged_task<int> pt(make_int_slowly);
+ boost::unique_future<int> fi=pt.get_future();
+ boost::packaged_task<int> pt2(make_int_slowly);
+ boost::unique_future<int> fi2=pt2.get_future();
+ pt.set_wait_callback(wait_callback_for_task);
+
+ boost::thread(::cast_to_rval(pt));
+
+ boost::wait_for_any(fi,fi2);
+ BOOST_CHECK(callback_called==1);
+ BOOST_CHECK(fi.get()==42);
+}
+
+void test_wait_for_any_from_range()
+{
+ unsigned const count=10;
+ for(unsigned i=0;i<count;++i)
+ {
+ boost::packaged_task<int> tasks[count];
+ boost::unique_future<int> futures[count];
+ for(unsigned j=0;j<count;++j)
+ {
+ tasks[j]=boost::packaged_task<int>(make_int_slowly);
+ futures[j]=tasks[j].get_future();
+ }
+ boost::thread(::cast_to_rval(tasks[i]));
+
+ boost::unique_future<int>* const future=boost::wait_for_any(futures,futures+count);
+
+ BOOST_CHECK(future==(futures+i));
+ for(unsigned j=0;j<count;++j)
+ {
+ if(j!=i)
+ {
+ BOOST_CHECK(!futures[j].is_ready());
+ }
+ else
+ {
+ BOOST_CHECK(futures[j].is_ready());
+ }
+ }
+ BOOST_CHECK(futures[i].get()==42);
+ }
+}
+
+void test_wait_for_all_from_range()
+{
+ unsigned const count=10;
+ boost::unique_future<int> futures[count];
+ for(unsigned j=0;j<count;++j)
+ {
+ boost::packaged_task<int> task(make_int_slowly);
+ futures[j]=task.get_future();
+ boost::thread(::cast_to_rval(task));
+ }
+
+ boost::wait_for_all(futures,futures+count);
+
+ for(unsigned j=0;j<count;++j)
+ {
+ BOOST_CHECK(futures[j].is_ready());
+ }
+}
+
+void test_wait_for_all_two_futures()
+{
+ unsigned const count=2;
+ boost::unique_future<int> futures[count];
+ for(unsigned j=0;j<count;++j)
+ {
+ boost::packaged_task<int> task(make_int_slowly);
+ futures[j]=task.get_future();
+ boost::thread(::cast_to_rval(task));
+ }
+
+ boost::wait_for_all(futures[0],futures[1]);
+
+ for(unsigned j=0;j<count;++j)
+ {
+ BOOST_CHECK(futures[j].is_ready());
+ }
+}
+
+void test_wait_for_all_three_futures()
+{
+ unsigned const count=3;
+ boost::unique_future<int> futures[count];
+ for(unsigned j=0;j<count;++j)
+ {
+ boost::packaged_task<int> task(make_int_slowly);
+ futures[j]=task.get_future();
+ boost::thread(::cast_to_rval(task));
+ }
+
+ boost::wait_for_all(futures[0],futures[1],futures[2]);
+
+ for(unsigned j=0;j<count;++j)
+ {
+ BOOST_CHECK(futures[j].is_ready());
+ }
+}
+
+void test_wait_for_all_four_futures()
+{
+ unsigned const count=4;
+ boost::unique_future<int> futures[count];
+ for(unsigned j=0;j<count;++j)
+ {
+ boost::packaged_task<int> task(make_int_slowly);
+ futures[j]=task.get_future();
+ boost::thread(::cast_to_rval(task));
+ }
+
+ boost::wait_for_all(futures[0],futures[1],futures[2],futures[3]);
+
+ for(unsigned j=0;j<count;++j)
+ {
+ BOOST_CHECK(futures[j].is_ready());
+ }
+}
+
+void test_wait_for_all_five_futures()
+{
+ unsigned const count=5;
+ boost::unique_future<int> futures[count];
+ for(unsigned j=0;j<count;++j)
+ {
+ boost::packaged_task<int> task(make_int_slowly);
+ futures[j]=task.get_future();
+ boost::thread(::cast_to_rval(task));
+ }
+
+ boost::wait_for_all(futures[0],futures[1],futures[2],futures[3],futures[4]);
+
+ for(unsigned j=0;j<count;++j)
+ {
+ BOOST_CHECK(futures[j].is_ready());
+ }
+}
+
+
+boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[])
+{
+ boost::unit_test_framework::test_suite* test =
+ BOOST_TEST_SUITE("Boost.Threads: futures test suite");
+
+ test->add(BOOST_TEST_CASE(test_initial_state));
+ test->add(BOOST_TEST_CASE(test_waiting_future));
+ test->add(BOOST_TEST_CASE(test_cannot_get_future_twice));
+ test->add(BOOST_TEST_CASE(test_set_value_updates_future_state));
+ test->add(BOOST_TEST_CASE(test_set_value_can_be_retrieved));
+ test->add(BOOST_TEST_CASE(test_set_value_can_be_moved));
+ test->add(BOOST_TEST_CASE(test_store_value_from_thread));
+ test->add(BOOST_TEST_CASE(test_store_exception));
+ test->add(BOOST_TEST_CASE(test_future_from_packaged_task_is_waiting));
+ test->add(BOOST_TEST_CASE(test_invoking_a_packaged_task_populates_future));
+ test->add(BOOST_TEST_CASE(test_invoking_a_packaged_task_twice_throws));
+ test->add(BOOST_TEST_CASE(test_cannot_get_future_twice_from_task));
+ test->add(BOOST_TEST_CASE(test_task_stores_exception_if_function_throws));
+ test->add(BOOST_TEST_CASE(test_void_promise));
+ test->add(BOOST_TEST_CASE(test_reference_promise));
+ test->add(BOOST_TEST_CASE(test_task_returning_void));
+ test->add(BOOST_TEST_CASE(test_task_returning_reference));
+ test->add(BOOST_TEST_CASE(test_shared_future));
+ test->add(BOOST_TEST_CASE(test_copies_of_shared_future_become_ready_together));
+ test->add(BOOST_TEST_CASE(test_shared_future_can_be_move_assigned_from_unique_future));
+ test->add(BOOST_TEST_CASE(test_shared_future_void));
+ test->add(BOOST_TEST_CASE(test_shared_future_ref));
+ test->add(BOOST_TEST_CASE(test_can_get_a_second_future_from_a_moved_promise));
+ test->add(BOOST_TEST_CASE(test_can_get_a_second_future_from_a_moved_void_promise));
+ test->add(BOOST_TEST_CASE(test_unique_future_for_move_only_udt));
+ test->add(BOOST_TEST_CASE(test_unique_future_for_string));
+ test->add(BOOST_TEST_CASE(test_wait_callback));
+ test->add(BOOST_TEST_CASE(test_wait_callback_with_timed_wait));
+ test->add(BOOST_TEST_CASE(test_wait_callback_for_packaged_task));
+ test->add(BOOST_TEST_CASE(test_packaged_task_can_be_moved));
+ test->add(BOOST_TEST_CASE(test_destroying_a_promise_stores_broken_promise));
+ test->add(BOOST_TEST_CASE(test_destroying_a_packaged_task_stores_broken_promise));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_two_futures_1));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_two_futures_2));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_three_futures_1));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_three_futures_2));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_three_futures_3));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_four_futures_1));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_four_futures_2));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_four_futures_3));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_four_futures_4));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_five_futures_1));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_five_futures_2));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_five_futures_3));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_five_futures_4));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_of_five_futures_5));
+ test->add(BOOST_TEST_CASE(test_wait_for_either_invokes_callbacks));
+ test->add(BOOST_TEST_CASE(test_wait_for_any_from_range));
+ test->add(BOOST_TEST_CASE(test_wait_for_all_from_range));
+ test->add(BOOST_TEST_CASE(test_wait_for_all_two_futures));
+ test->add(BOOST_TEST_CASE(test_wait_for_all_three_futures));
+ test->add(BOOST_TEST_CASE(test_wait_for_all_four_futures));
+ test->add(BOOST_TEST_CASE(test_wait_for_all_five_futures));
+
+ return test;
+}
Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk