// (C) Copyright 2008-10 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/thread/mutex.hpp> | |
#include <boost/thread/condition_variable.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 <boost/throw_exception.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(); | |
} | |
private: | |
relocker& operator=(relocker const&); | |
}; | |
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; | |
#ifndef BOOST_NO_RVALUE_REFERENCES | |
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 static_cast<move_dest_type>(*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; | |
typedef std::vector<registered_waiter>::size_type count_type; | |
struct registered_waiter | |
{ | |
boost::shared_ptr<detail::future_object_base> future; | |
detail::future_object_base::waiter_list::iterator wait_iterator; | |
count_type index; | |
registered_waiter(boost::shared_ptr<detail::future_object_base> const& future_, | |
detail::future_object_base::waiter_list::iterator wait_iterator_, | |
count_type index_): | |
future(future_),wait_iterator(wait_iterator_),index(index_) | |
{} | |
}; | |
struct all_futures_lock | |
{ | |
count_type 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(count_type 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(count_type i=0;i<count;++i) | |
{ | |
locks[i].unlock(); | |
} | |
} | |
}; | |
boost::condition_variable_any cv; | |
std::vector<registered_waiter> futures; | |
count_type 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; | |
} | |
count_type wait() | |
{ | |
all_futures_lock lk(futures); | |
for(;;) | |
{ | |
for(count_type i=0;i<futures.size();++i) | |
{ | |
if(futures[i].future->done) | |
{ | |
return futures[i].index; | |
} | |
} | |
cv.wait(lk); | |
} | |
} | |
~future_waiter() | |
{ | |
for(count_type 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) | |
{ | |
if(begin==end) | |
return 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() | |
{} | |
#ifndef BOOST_NO_RVALUE_REFERENCES | |
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) | |
{ | |
boost::throw_exception(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) | |
{ | |
boost::throw_exception(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) | |
{ | |
boost::throw_exception(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; | |
} | |
#ifndef BOOST_NO_RVALUE_REFERENCES | |
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) | |
{ | |
boost::throw_exception(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) | |
{ | |
boost::throw_exception(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) | |
{ | |
boost::throw_exception(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(!atomic_load(&future)) | |
{ | |
future_ptr blank; | |
atomic_compare_exchange(&future,&blank,future_ptr(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 | |
#ifndef BOOST_NO_RVALUE_REFERENCES | |
promise(promise && rhs): | |
future_obtained(rhs.future_obtained) | |
{ | |
future.swap(rhs.future); | |
rhs.future_obtained=false; | |
} | |
promise & operator=(promise&& rhs) | |
{ | |
future.swap(rhs.future); | |
future_obtained=rhs.future_obtained; | |
rhs.future.reset(); | |
rhs.future_obtained=false; | |
return *this; | |
} | |
#else | |
promise(boost::detail::thread_move_t<promise> rhs): | |
future(rhs->future),future_obtained(rhs->future_obtained) | |
{ | |
rhs->future.reset(); | |
rhs->future_obtained=false; | |
} | |
promise & operator=(boost::detail::thread_move_t<promise> rhs) | |
{ | |
future=rhs->future; | |
future_obtained=rhs->future_obtained; | |
rhs->future.reset(); | |
rhs->future_obtained=false; | |
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) | |
{ | |
boost::throw_exception(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) | |
{ | |
boost::throw_exception(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) | |
{ | |
boost::throw_exception(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) | |
{ | |
boost::throw_exception(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(!atomic_load(&future)) | |
{ | |
future_ptr blank; | |
atomic_compare_exchange(&future,&blank,future_ptr(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 | |
#ifndef BOOST_NO_RVALUE_REFERENCES | |
promise(promise && rhs): | |
future_obtained(rhs.future_obtained) | |
{ | |
future.swap(rhs.future); | |
rhs.future_obtained=false; | |
} | |
promise & operator=(promise&& rhs) | |
{ | |
future.swap(rhs.future); | |
future_obtained=rhs.future_obtained; | |
rhs.future.reset(); | |
rhs.future_obtained=false; | |
return *this; | |
} | |
#else | |
promise(boost::detail::thread_move_t<promise> rhs): | |
future(rhs->future),future_obtained(rhs->future_obtained) | |
{ | |
rhs->future.reset(); | |
rhs->future_obtained=false; | |
} | |
promise & operator=(boost::detail::thread_move_t<promise> rhs) | |
{ | |
future=rhs->future; | |
future_obtained=rhs->future_obtained; | |
rhs->future.reset(); | |
rhs->future_obtained=false; | |
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) | |
{ | |
boost::throw_exception(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) | |
{ | |
boost::throw_exception(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) | |
{ | |
boost::throw_exception(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) | |
{ | |
boost::throw_exception(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 | |
#ifndef BOOST_NO_RVALUE_REFERENCES | |
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) | |
{ | |
boost::throw_exception(task_moved()); | |
} | |
else if(!future_obtained) | |
{ | |
future_obtained=true; | |
return unique_future<R>(task); | |
} | |
else | |
{ | |
boost::throw_exception(future_already_retrieved()); | |
} | |
} | |
// execution | |
void operator()() | |
{ | |
if(!task) | |
{ | |
boost::throw_exception(task_moved()); | |
} | |
task->run(); | |
} | |
template<typename F> | |
void set_wait_callback(F f) | |
{ | |
task->set_wait_callback(f,this); | |
} | |
}; | |
} | |
#endif |