1 // (C) Copyright 2008-10 Anthony Williams
3 // Distributed under the Boost Software License, Version 1.0. (See
4 // accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
7 #ifndef BOOST_THREAD_FUTURE_HPP
8 #define BOOST_THREAD_FUTURE_HPP
10 #include <boost/thread/detail/move.hpp>
11 #include <boost/thread/thread_time.hpp>
12 #include <boost/thread/mutex.hpp>
13 #include <boost/thread/condition_variable.hpp>
14 #include <boost/exception_ptr.hpp>
15 #include <boost/shared_ptr.hpp>
16 #include <boost/scoped_ptr.hpp>
17 #include <boost/type_traits/is_fundamental.hpp>
18 #include <boost/type_traits/is_convertible.hpp>
19 #include <boost/mpl/if.hpp>
20 #include <boost/config.hpp>
21 #include <boost/throw_exception.hpp>
23 #include <boost/function.hpp>
24 #include <boost/bind.hpp>
25 #include <boost/ref.hpp>
26 #include <boost/scoped_array.hpp>
27 #include <boost/utility/enable_if.hpp>
29 #include <boost/next_prior.hpp>
34 class future_uninitialized:
35 public std::logic_error
38 future_uninitialized():
39 std::logic_error("Future Uninitialized")
43 public std::logic_error
47 std::logic_error("Broken promise")
50 class future_already_retrieved:
51 public std::logic_error
54 future_already_retrieved():
55 std::logic_error("Future already retrieved")
58 class promise_already_satisfied:
59 public std::logic_error
62 promise_already_satisfied():
63 std::logic_error("Promise already satisfied")
67 class task_already_started:
68 public std::logic_error
71 task_already_started():
72 std::logic_error("Task already started")
77 public std::logic_error
81 std::logic_error("Task moved")
85 namespace future_state
87 enum state { uninitialized, waiting, ready, moved };
92 struct future_object_base
94 boost::exception_ptr exception;
97 boost::condition_variable waiters;
98 typedef std::list<boost::condition_variable_any*> waiter_list;
99 waiter_list external_waiters;
100 boost::function<void()> callback;
102 future_object_base():
105 virtual ~future_object_base()
108 waiter_list::iterator register_external_waiter(boost::condition_variable_any& cv)
110 boost::unique_lock<boost::mutex> lock(mutex);
112 return external_waiters.insert(external_waiters.end(),&cv);
115 void remove_external_waiter(waiter_list::iterator it)
117 boost::lock_guard<boost::mutex> lock(mutex);
118 external_waiters.erase(it);
121 void mark_finished_internal()
124 waiters.notify_all();
125 for(waiter_list::const_iterator it=external_waiters.begin(),
126 end=external_waiters.end();it!=end;++it)
134 boost::unique_lock<boost::mutex>& lock;
136 relocker(boost::unique_lock<boost::mutex>& lock_):
146 relocker& operator=(relocker const&);
149 void do_callback(boost::unique_lock<boost::mutex>& lock)
151 if(callback && !done)
153 boost::function<void()> local_callback=callback;
154 relocker relock(lock);
160 void wait(bool rethrow=true)
162 boost::unique_lock<boost::mutex> lock(mutex);
168 if(rethrow && exception)
170 boost::rethrow_exception(exception);
174 bool timed_wait_until(boost::system_time const& target_time)
176 boost::unique_lock<boost::mutex> lock(mutex);
180 bool const success=waiters.timed_wait(lock,target_time);
181 if(!success && !done)
189 void mark_exceptional_finish_internal(boost::exception_ptr const& e)
192 mark_finished_internal();
194 void mark_exceptional_finish()
196 boost::lock_guard<boost::mutex> lock(mutex);
197 mark_exceptional_finish_internal(boost::current_exception());
202 boost::lock_guard<boost::mutex> lock(mutex);
203 return done && !exception;
207 boost::lock_guard<boost::mutex> lock(mutex);
208 return done && exception;
211 template<typename F,typename U>
212 void set_wait_callback(F f,U* u)
214 callback=boost::bind(f,boost::ref(*u));
218 future_object_base(future_object_base const&);
219 future_object_base& operator=(future_object_base const&);
225 typedef boost::scoped_ptr<T> storage_type;
226 #ifndef BOOST_NO_RVALUE_REFERENCES
227 typedef T const& source_reference_type;
229 typedef typename boost::mpl::if_<boost::is_fundamental<T>,dummy&,T&&>::type rvalue_source_type;
230 typedef typename boost::mpl::if_<boost::is_fundamental<T>,T,T&&>::type move_dest_type;
232 typedef T& source_reference_type;
233 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;
234 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;
237 static void init(storage_type& storage,source_reference_type t)
239 storage.reset(new T(t));
242 static void init(storage_type& storage,rvalue_source_type t)
244 storage.reset(new T(static_cast<rvalue_source_type>(t)));
247 static void cleanup(storage_type& storage)
254 struct future_traits<T&>
256 typedef T* storage_type;
257 typedef T& source_reference_type;
258 struct rvalue_source_type
260 typedef T& move_dest_type;
262 static void init(storage_type& storage,T& t)
267 static void cleanup(storage_type& storage)
274 struct future_traits<void>
276 typedef bool storage_type;
277 typedef void move_dest_type;
279 static void init(storage_type& storage)
284 static void cleanup(storage_type& storage)
292 struct future_object:
293 detail::future_object_base
295 typedef typename future_traits<T>::storage_type storage_type;
296 typedef typename future_traits<T>::source_reference_type source_reference_type;
297 typedef typename future_traits<T>::rvalue_source_type rvalue_source_type;
298 typedef typename future_traits<T>::move_dest_type move_dest_type;
306 void mark_finished_with_result_internal(source_reference_type result_)
308 future_traits<T>::init(result,result_);
309 mark_finished_internal();
311 void mark_finished_with_result_internal(rvalue_source_type result_)
313 future_traits<T>::init(result,static_cast<rvalue_source_type>(result_));
314 mark_finished_internal();
317 void mark_finished_with_result(source_reference_type result_)
319 boost::lock_guard<boost::mutex> lock(mutex);
320 mark_finished_with_result_internal(result_);
322 void mark_finished_with_result(rvalue_source_type result_)
324 boost::lock_guard<boost::mutex> lock(mutex);
325 mark_finished_with_result_internal(result_);
331 return static_cast<move_dest_type>(*result);
334 future_state::state get_state()
336 boost::lock_guard<boost::mutex> guard(mutex);
339 return future_state::waiting;
343 return future_state::ready;
348 future_object(future_object const&);
349 future_object& operator=(future_object const&);
353 struct future_object<void>:
354 detail::future_object_base
356 typedef void move_dest_type;
361 void mark_finished_with_result_internal()
363 mark_finished_internal();
366 void mark_finished_with_result()
368 boost::lock_guard<boost::mutex> lock(mutex);
369 mark_finished_with_result_internal();
377 future_state::state get_state()
379 boost::lock_guard<boost::mutex> guard(mutex);
382 return future_state::waiting;
386 return future_state::ready;
391 future_object(future_object const&);
392 future_object& operator=(future_object const&);
397 struct registered_waiter;
398 typedef std::vector<registered_waiter>::size_type count_type;
400 struct registered_waiter
402 boost::shared_ptr<detail::future_object_base> future;
403 detail::future_object_base::waiter_list::iterator wait_iterator;
406 registered_waiter(boost::shared_ptr<detail::future_object_base> const& future_,
407 detail::future_object_base::waiter_list::iterator wait_iterator_,
409 future(future_),wait_iterator(wait_iterator_),index(index_)
414 struct all_futures_lock
417 typedef std::ptrdiff_t count_type_portable;
419 typedef count_type count_type_portable;
421 count_type_portable count;
423 boost::scoped_array<boost::unique_lock<boost::mutex> > locks;
425 all_futures_lock(std::vector<registered_waiter>& futures):
426 count(futures.size()),locks(new boost::unique_lock<boost::mutex>[count])
428 for(count_type_portable i=0;i<count;++i)
430 #if defined __DECCXX || defined __SUNPRO_CC
431 locks[i]=boost::unique_lock<boost::mutex>(futures[i].future->mutex).move();
433 locks[i]=boost::unique_lock<boost::mutex>(futures[i].future->mutex);
440 boost::lock(locks.get(),locks.get()+count);
445 for(count_type_portable i=0;i<count;++i)
452 boost::condition_variable_any cv;
453 std::vector<registered_waiter> futures;
454 count_type future_count;
466 futures.push_back(registered_waiter(f.future,f.future->register_external_waiter(cv),future_count));
473 all_futures_lock lk(futures);
476 for(count_type i=0;i<futures.size();++i)
478 if(futures[i].future->done)
480 return futures[i].index;
489 for(count_type i=0;i<futures.size();++i)
491 futures[i].future->remove_external_waiter(futures[i].wait_iterator);
499 template <typename R>
502 template <typename R>
506 struct is_future_type
508 BOOST_STATIC_CONSTANT(bool, value=false);
512 struct is_future_type<unique_future<T> >
514 BOOST_STATIC_CONSTANT(bool, value=true);
518 struct is_future_type<shared_future<T> >
520 BOOST_STATIC_CONSTANT(bool, value=true);
523 template<typename Iterator>
524 typename boost::disable_if<is_future_type<Iterator>,void>::type wait_for_all(Iterator begin,Iterator end)
526 for(Iterator current=begin;current!=end;++current)
532 template<typename F1,typename F2>
533 typename boost::enable_if<is_future_type<F1>,void>::type wait_for_all(F1& f1,F2& f2)
539 template<typename F1,typename F2,typename F3>
540 void wait_for_all(F1& f1,F2& f2,F3& f3)
547 template<typename F1,typename F2,typename F3,typename F4>
548 void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4)
556 template<typename F1,typename F2,typename F3,typename F4,typename F5>
557 void wait_for_all(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5)
566 template<typename Iterator>
567 typename boost::disable_if<is_future_type<Iterator>,Iterator>::type wait_for_any(Iterator begin,Iterator end)
572 detail::future_waiter waiter;
573 for(Iterator current=begin;current!=end;++current)
575 waiter.add(*current);
577 return boost::next(begin,waiter.wait());
580 template<typename F1,typename F2>
581 typename boost::enable_if<is_future_type<F1>,unsigned>::type wait_for_any(F1& f1,F2& f2)
583 detail::future_waiter waiter;
586 return waiter.wait();
589 template<typename F1,typename F2,typename F3>
590 unsigned wait_for_any(F1& f1,F2& f2,F3& f3)
592 detail::future_waiter waiter;
596 return waiter.wait();
599 template<typename F1,typename F2,typename F3,typename F4>
600 unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4)
602 detail::future_waiter waiter;
607 return waiter.wait();
610 template<typename F1,typename F2,typename F3,typename F4,typename F5>
611 unsigned wait_for_any(F1& f1,F2& f2,F3& f3,F4& f4,F5& f5)
613 detail::future_waiter waiter;
619 return waiter.wait();
622 template <typename R>
625 template <typename R>
628 template <typename R>
631 unique_future(unique_future & rhs);// = delete;
632 unique_future& operator=(unique_future& rhs);// = delete;
634 typedef boost::shared_ptr<detail::future_object<R> > future_ptr;
638 friend class shared_future<R>;
639 friend class promise<R>;
640 friend class packaged_task<R>;
641 friend class detail::future_waiter;
643 typedef typename detail::future_traits<R>::move_dest_type move_dest_type;
645 unique_future(future_ptr future_):
650 typedef future_state::state state;
658 #ifndef BOOST_NO_RVALUE_REFERENCES
659 unique_future(unique_future && other)
661 future.swap(other.future);
663 unique_future& operator=(unique_future && other)
666 other.future.reset();
670 unique_future(boost::detail::thread_move_t<unique_future> other):
671 future(other->future)
673 other->future.reset();
676 unique_future& operator=(boost::detail::thread_move_t<unique_future> other)
678 future=other->future;
679 other->future.reset();
683 operator boost::detail::thread_move_t<unique_future>()
685 return boost::detail::thread_move_t<unique_future>(*this);
689 void swap(unique_future& other)
691 future.swap(other.future);
694 // retrieving the value
699 boost::throw_exception(future_uninitialized());
702 return future->get();
705 // functions to check state, and wait for ready
706 state get_state() const
710 return future_state::uninitialized;
712 return future->get_state();
716 bool is_ready() const
718 return get_state()==future_state::ready;
721 bool has_exception() const
723 return future && future->has_exception();
726 bool has_value() const
728 return future && future->has_value();
735 boost::throw_exception(future_uninitialized());
740 template<typename Duration>
741 bool timed_wait(Duration const& rel_time) const
743 return timed_wait_until(boost::get_system_time()+rel_time);
746 bool timed_wait_until(boost::system_time const& abs_time) const
750 boost::throw_exception(future_uninitialized());
752 return future->timed_wait_until(abs_time);
757 #ifdef BOOST_NO_RVALUE_REFERENCES
758 template <typename T>
759 struct has_move_emulation_enabled_aux<unique_future<T> >
760 : BOOST_MOVE_BOOST_NS::integral_constant<bool, true>
764 template <typename R>
767 typedef boost::shared_ptr<detail::future_object<R> > future_ptr;
771 // shared_future(const unique_future<R>& other);
772 // shared_future& operator=(const unique_future<R>& other);
774 friend class detail::future_waiter;
775 friend class promise<R>;
776 friend class packaged_task<R>;
778 shared_future(future_ptr future_):
783 shared_future(shared_future const& other):
787 typedef future_state::state state;
795 shared_future& operator=(shared_future const& other)
800 #ifndef BOOST_NO_RVALUE_REFERENCES
801 shared_future(shared_future && other)
803 future.swap(other.future);
805 shared_future(unique_future<R> && other)
807 future.swap(other.future);
809 shared_future& operator=(shared_future && other)
811 future.swap(other.future);
812 other.future.reset();
815 shared_future& operator=(unique_future<R> && other)
817 future.swap(other.future);
818 other.future.reset();
822 shared_future(boost::detail::thread_move_t<shared_future> other):
823 future(other->future)
825 other->future.reset();
827 // shared_future(const unique_future<R> &) = delete;
828 shared_future(boost::detail::thread_move_t<unique_future<R> > other):
829 future(other->future)
831 other->future.reset();
833 shared_future& operator=(boost::detail::thread_move_t<shared_future> other)
835 future.swap(other->future);
836 other->future.reset();
839 shared_future& operator=(boost::detail::thread_move_t<unique_future<R> > other)
841 future.swap(other->future);
842 other->future.reset();
846 operator boost::detail::thread_move_t<shared_future>()
848 return boost::detail::thread_move_t<shared_future>(*this);
853 void swap(shared_future& other)
855 future.swap(other.future);
858 // retrieving the value
859 //typename detail::future_object<R>::move_dest_type get()
864 boost::throw_exception(future_uninitialized());
867 return future->get();
870 // functions to check state, and wait for ready
871 state get_state() const
875 return future_state::uninitialized;
877 return future->get_state();
881 bool is_ready() const
883 return get_state()==future_state::ready;
886 bool has_exception() const
888 return future && future->has_exception();
891 bool has_value() const
893 return future && future->has_value();
900 boost::throw_exception(future_uninitialized());
905 template<typename Duration>
906 bool timed_wait(Duration const& rel_time) const
908 return timed_wait_until(boost::get_system_time()+rel_time);
911 bool timed_wait_until(boost::system_time const& abs_time) const
915 boost::throw_exception(future_uninitialized());
917 return future->timed_wait_until(abs_time);
922 #ifdef BOOST_NO_RVALUE_REFERENCES
923 template <typename T>
924 struct has_move_emulation_enabled_aux<shared_future<T> >
925 : BOOST_MOVE_BOOST_NS::integral_constant<bool, true>
929 template <typename R>
932 typedef boost::shared_ptr<detail::future_object<R> > future_ptr;
935 bool future_obtained;
937 promise(promise & rhs);// = delete;
938 promise & operator=(promise & rhs);// = delete;
942 if(!atomic_load(&future))
945 atomic_compare_exchange(&future,&blank,future_ptr(new detail::future_object<R>));
950 // template <class Allocator> explicit promise(Allocator a);
953 future(),future_obtained(false)
960 boost::lock_guard<boost::mutex> lock(future->mutex);
964 future->mark_exceptional_finish_internal(boost::copy_exception(broken_promise()));
970 #ifndef BOOST_NO_RVALUE_REFERENCES
971 promise(promise && rhs):
972 future_obtained(rhs.future_obtained)
974 future.swap(rhs.future);
975 rhs.future_obtained=false;
977 promise & operator=(promise&& rhs)
979 future.swap(rhs.future);
980 future_obtained=rhs.future_obtained;
982 rhs.future_obtained=false;
986 promise(boost::detail::thread_move_t<promise> rhs):
987 future(rhs->future),future_obtained(rhs->future_obtained)
990 rhs->future_obtained=false;
992 promise & operator=(boost::detail::thread_move_t<promise> rhs)
995 future_obtained=rhs->future_obtained;
997 rhs->future_obtained=false;
1001 operator boost::detail::thread_move_t<promise>()
1003 return boost::detail::thread_move_t<promise>(*this);
1007 void swap(promise& other)
1009 future.swap(other.future);
1010 std::swap(future_obtained,other.future_obtained);
1014 unique_future<R> get_future()
1019 boost::throw_exception(future_already_retrieved());
1021 future_obtained=true;
1022 return unique_future<R>(future);
1025 void set_value(typename detail::future_traits<R>::source_reference_type r)
1028 boost::lock_guard<boost::mutex> lock(future->mutex);
1031 boost::throw_exception(promise_already_satisfied());
1033 future->mark_finished_with_result_internal(r);
1036 // void set_value(R && r);
1037 void set_value(typename detail::future_traits<R>::rvalue_source_type r)
1040 boost::lock_guard<boost::mutex> lock(future->mutex);
1043 boost::throw_exception(promise_already_satisfied());
1045 future->mark_finished_with_result_internal(static_cast<typename detail::future_traits<R>::rvalue_source_type>(r));
1048 void set_exception(boost::exception_ptr p)
1051 boost::lock_guard<boost::mutex> lock(future->mutex);
1054 boost::throw_exception(promise_already_satisfied());
1056 future->mark_exceptional_finish_internal(p);
1059 template<typename F>
1060 void set_wait_callback(F f)
1063 future->set_wait_callback(f,this);
1071 typedef boost::shared_ptr<detail::future_object<void> > future_ptr;
1074 bool future_obtained;
1076 promise(promise & rhs);// = delete;
1077 promise & operator=(promise & rhs);// = delete;
1081 if(!atomic_load(&future))
1084 atomic_compare_exchange(&future,&blank,future_ptr(new detail::future_object<void>));
1088 // template <class Allocator> explicit promise(Allocator a);
1091 future(),future_obtained(false)
1098 boost::lock_guard<boost::mutex> lock(future->mutex);
1102 future->mark_exceptional_finish_internal(boost::copy_exception(broken_promise()));
1108 #ifndef BOOST_NO_RVALUE_REFERENCES
1109 promise(promise && rhs):
1110 future_obtained(rhs.future_obtained)
1112 future.swap(rhs.future);
1113 rhs.future_obtained=false;
1115 promise & operator=(promise&& rhs)
1117 future.swap(rhs.future);
1118 future_obtained=rhs.future_obtained;
1120 rhs.future_obtained=false;
1124 promise(boost::detail::thread_move_t<promise> rhs):
1125 future(rhs->future),future_obtained(rhs->future_obtained)
1127 rhs->future.reset();
1128 rhs->future_obtained=false;
1130 promise & operator=(boost::detail::thread_move_t<promise> rhs)
1133 future_obtained=rhs->future_obtained;
1134 rhs->future.reset();
1135 rhs->future_obtained=false;
1139 operator boost::detail::thread_move_t<promise>()
1141 return boost::detail::thread_move_t<promise>(*this);
1145 void swap(promise& other)
1147 future.swap(other.future);
1148 std::swap(future_obtained,other.future_obtained);
1152 unique_future<void> get_future()
1158 boost::throw_exception(future_already_retrieved());
1160 future_obtained=true;
1161 return unique_future<void>(future);
1167 boost::lock_guard<boost::mutex> lock(future->mutex);
1170 boost::throw_exception(promise_already_satisfied());
1172 future->mark_finished_with_result_internal();
1175 void set_exception(boost::exception_ptr p)
1178 boost::lock_guard<boost::mutex> lock(future->mutex);
1181 boost::throw_exception(promise_already_satisfied());
1183 future->mark_exceptional_finish_internal(p);
1186 template<typename F>
1187 void set_wait_callback(F f)
1190 future->set_wait_callback(f,this);
1195 #ifdef BOOST_NO_RVALUE_REFERENCES
1196 template <typename T>
1197 struct has_move_emulation_enabled_aux<promise<T> >
1198 : BOOST_MOVE_BOOST_NS::integral_constant<bool, true>
1204 template<typename R>
1206 detail::future_object<R>
1217 boost::lock_guard<boost::mutex> lk(this->mutex);
1220 boost::throw_exception(task_already_started());
1227 void owner_destroyed()
1229 boost::lock_guard<boost::mutex> lk(this->mutex);
1233 this->mark_exceptional_finish_internal(boost::copy_exception(boost::broken_promise()));
1238 virtual void do_run()=0;
1242 template<typename R,typename F>
1247 task_object(F const& f_):
1250 #ifndef BOOST_NO_RVALUE_REFERENCES
1251 task_object(F&& f_):
1255 task_object(boost::detail::thread_move_t<F> f_):
1264 this->mark_finished_with_result(f());
1268 this->mark_exceptional_finish();
1273 template<typename F>
1274 struct task_object<void,F>:
1278 task_object(F const& f_):
1281 #ifndef BOOST_NO_RVALUE_REFERENCES
1282 task_object(F&& f_):
1286 task_object(boost::detail::thread_move_t<F> f_):
1296 this->mark_finished_with_result();
1300 this->mark_exceptional_finish();
1308 template<typename R>
1311 boost::shared_ptr<detail::task_base<R> > task;
1312 bool future_obtained;
1314 packaged_task(packaged_task&);// = delete;
1315 packaged_task& operator=(packaged_task&);// = delete;
1319 future_obtained(false)
1322 // construction and destruction
1324 explicit packaged_task(F const& f):
1325 task(new detail::task_object<R,F>(f)),future_obtained(false)
1327 explicit packaged_task(R(*f)()):
1328 task(new detail::task_object<R,R(*)()>(f)),future_obtained(false)
1331 #ifndef BOOST_NO_RVALUE_REFERENCES
1333 explicit packaged_task(F&& f):
1334 task(new detail::task_object<R,F>(f)),future_obtained(false)
1338 explicit packaged_task(boost::detail::thread_move_t<F> f):
1339 task(new detail::task_object<R,F>(f)),future_obtained(false)
1343 // template <class F, class Allocator>
1344 // explicit packaged_task(F const& f, Allocator a);
1345 // template <class F, class Allocator>
1346 // explicit packaged_task(F&& f, Allocator a);
1353 task->owner_destroyed();
1358 #ifndef BOOST_NO_RVALUE_REFERENCES
1359 packaged_task(packaged_task&& other):
1360 future_obtained(other.future_obtained)
1362 task.swap(other.task);
1363 other.future_obtained=false;
1365 packaged_task& operator=(packaged_task&& other)
1367 packaged_task temp(static_cast<packaged_task&&>(other));
1372 packaged_task(boost::detail::thread_move_t<packaged_task> other):
1373 future_obtained(other->future_obtained)
1375 task.swap(other->task);
1376 other->future_obtained=false;
1378 packaged_task& operator=(boost::detail::thread_move_t<packaged_task> other)
1380 packaged_task temp(other);
1384 operator boost::detail::thread_move_t<packaged_task>()
1386 return boost::detail::thread_move_t<packaged_task>(*this);
1390 void swap(packaged_task& other)
1392 task.swap(other.task);
1393 std::swap(future_obtained,other.future_obtained);
1397 unique_future<R> get_future()
1401 boost::throw_exception(task_moved());
1403 else if(!future_obtained)
1405 future_obtained=true;
1406 return unique_future<R>(task);
1410 boost::throw_exception(future_already_retrieved());
1420 boost::throw_exception(task_moved());
1425 template<typename F>
1426 void set_wait_callback(F f)
1428 task->set_wait_callback(f,this);
1433 #ifdef BOOST_NO_RVALUE_REFERENCES
1434 template <typename T>
1435 struct has_move_emulation_enabled_aux<packaged_task<T> >
1436 : BOOST_MOVE_BOOST_NS::integral_constant<bool, true>