Main Page | Class List | File List | Class Members

Futures.hpp

00001 /*
00002  * Copyright (C) 2004
00003  * Slawomir Lisznianski <slisznianski@asyncnet.com>
00004  *
00005  * Permission to use, copy, modify, distribute and sell this software
00006  * and its documentation for any purpose is hereby granted without fee,
00007  * provided that the above copyright notice appear in all copies and
00008  * that both that copyright notice and this permission notice appear
00009  * in supporting documentation. Slawomir Lisznianski makes no
00010  * representations about the suitability of this software for any
00011  * purpose. It is provided "as is" without express or implied warranty.
00012  *
00013  */
00014 
00015 #ifndef RHA_FUTURES_HPP
00016 #define RHA_FUTURES_HPP
00017 
00018 #include <stdexcept>
00019 
00020 #include <boost/thread/thread.hpp>
00021 #include <boost/thread/condition.hpp>
00022 #include <boost/variant/variant.hpp>
00023 #include <boost/variant/static_visitor.hpp>
00024 #include <boost/variant/apply_visitor.hpp>
00025 #include <boost/variant/get.hpp>
00026 #include <boost/mpl/front.hpp>
00027 #include <boost/mpl/pop_front.hpp>
00028 #include <boost/mpl/empty.hpp>
00029 #include <boost/mpl/vector.hpp>
00030 #include <boost/mpl/push_back.hpp>
00031 #include <boost/optional.hpp>
00032 #include <boost/call_traits.hpp>
00033 #include <boost/function.hpp>
00034 #include <boost/shared_ptr.hpp>
00035 
00036 namespace Rha
00037 {
00043   class UnboundFuture :
00044     public std::runtime_error
00045   {
00046   public:
00047     UnboundFuture() :
00048       std::runtime_error("call to unbound future")
00049     { }
00050   };
00051 
00052   namespace Details
00053   {
00054     /*
00055     * Unusable is a helper to handle objects returning void.
00056     */
00057     struct Unusable
00058     {
00059       Unusable() {}
00060       template<typename T> Unusable(const T&) {}
00061     };
00062 
00063     /*
00064     * ReturnTypeHelper used to deal with void return
00065     * type.
00066     */
00067     template<typename ResultType>
00068     struct ReturnTypeHelper
00069     {
00070       typedef ResultType Type;
00071     };
00072 
00073     template<>
00074     struct ReturnTypeHelper<void>
00075     {
00076       typedef Unusable Type;
00077     };
00078   }
00079 
00086   template<typename E0 = boost::detail::variant::void_,
00087            typename E1 = boost::detail::variant::void_,
00088            typename E2 = boost::detail::variant::void_,
00089            typename E3 = boost::detail::variant::void_,
00090            typename E4 = boost::detail::variant::void_,
00091            typename E5 = boost::detail::variant::void_,
00092            typename E6 = boost::detail::variant::void_,
00093            typename E7 = boost::detail::variant::void_,
00094            typename E8 = boost::detail::variant::void_>
00095   struct FutureRegistry
00096   {
00097     typedef E0 Exception0;
00098     typedef E1 Exception1;
00099     typedef E2 Exception2;
00100     typedef E3 Exception3;
00101     typedef E4 Exception4;
00102     typedef E5 Exception5;
00103     typedef E6 Exception6;
00104     typedef E7 Exception7;
00105     typedef E8 Exception8;
00106     template<typename R> class Future;
00107   private:
00112     template<typename ReturnType>
00113     struct ReturnTypeList
00114     {
00115       typedef boost::variant<ReturnType,
00116                              Exception0,
00117                              Exception1,
00118                              Exception2,
00119                              Exception3,
00120                              Exception4,
00121                              Exception5,
00122                              Exception6,
00123                              Exception7,
00124                              Exception8> Type;
00125     };
00126 
00131     template<typename R>
00132     struct TaskContext
00133     {
00134       typedef R ReturnType;
00135 
00136       TaskContext(const boost::function0<ReturnType>& functor) :
00137         MReleased(false),
00138         M_functor(functor)
00139       { }
00140 
00141       bool                                          MReleased;
00142       boost::function0<ReturnType>                  M_functor;
00143       typename ReturnTypeList<typename Details::ReturnTypeHelper<ReturnType>::Type>::Type MResult;
00144       boost::condition                              M_condition;
00145       boost::mutex                                  M_mutex;
00146     };
00147 
00152     template <bool isEmpty, class ExceptionTypeList, bool isVoid>
00153     struct CallerImpl
00154     {
00155       template<class _Fun, class ResList>
00156       static void call(_Fun f, ResList* res)
00157       {
00158         f();
00159         *res = Details::Unusable();
00160       }
00161     };
00162 
00167     template <class ExceptionTypeList>
00168     struct CallerImpl<true, ExceptionTypeList, false>
00169     {
00170       template<class _Fun, class ResList>
00171       static void call(_Fun f, ResList* res)
00172       {
00173         *res = f();
00174       }
00175     };
00176 
00181     template <class ExceptionTypeList, bool isVoid>
00182     struct CallerImpl<false, ExceptionTypeList, isVoid>
00183     {
00184       template<class _Fun, class ResList>
00185       static void call(_Fun f, ResList* res)
00186       {
00187         try
00188         {
00189           typedef typename boost::mpl::pop_front<ExceptionTypeList>::type popped;
00190           CallerImpl<boost::mpl::empty<popped>::value, popped, isVoid>::call(f, res);
00191         }
00192         catch (typename boost::mpl::front<ExceptionTypeList>::type e)
00193         {
00194           *res = e;
00195         }
00196       }
00197     };
00198 
00202     template<typename R>
00203     class JoinableTask
00204     {
00205     public:
00206       typedef R ReturnType;
00207       class Proxy;
00208       friend class Future<ReturnType>;
00209 
00210       JoinableTask(const boost::function0<ReturnType>& functor) :
00211         M_taskContext(new TaskContext<ReturnType> (functor))
00212       { }
00213 
00214       Proxy proxy()
00215       {
00216         return Proxy(M_taskContext);
00217       }
00218 
00219     private:
00225       class Proxy
00226       {
00227       public:
00228         Proxy(typename boost::call_traits<boost::shared_ptr<TaskContext<ReturnType> > >::param_type taskContext) :
00229           M_sentry(new _Sentry_(taskContext))
00230         { }
00231 
00232         ~Proxy()
00233         {  }
00234 
00235         void operator()()
00236         {
00237           try
00238           {
00239             // Remove the return value type. The rest in the list are exception types.
00240             //
00241             typedef typename boost::mpl::pop_front<typename ReturnTypeList<typename Details::ReturnTypeHelper<ReturnType>::Type>::Type::types>::type popped;
00242             CallerImpl<boost::mpl::empty<popped>::value, popped, ::boost::is_void<ReturnType>::value>::call(M_sentry->M_taskContext->M_functor, &M_sentry->M_taskContext->MResult);
00243           }
00244           catch (...)
00245           {
00246             // A task has thrown an exception that is not listed in the
00247             // exception-specification of this FutureRegistry.
00248             //
00249             std::unexpected();
00250           }
00251         }
00252 
00253       private:
00254         struct _Sentry_
00255         {
00256           _Sentry_(typename boost::call_traits<boost::shared_ptr<TaskContext<ReturnType> > >::param_type taskContext) :
00257             M_taskContext(taskContext)
00258           { }
00259 
00260           ~_Sentry_()
00261           {
00262             boost::mutex::scoped_lock lock__(M_taskContext->M_mutex);
00263             M_taskContext->MReleased = true;
00264             M_taskContext->M_condition.notify_all();
00265           }
00266           boost::shared_ptr<TaskContext<ReturnType> > M_taskContext;
00267         };
00268         boost::shared_ptr<_Sentry_> M_sentry;
00269       };
00270       boost::shared_ptr<TaskContext<ReturnType> > M_taskContext;
00271     };
00272   public:
00279     template<typename R>
00280     class Future
00281     {
00282     public:
00283       typedef R ReturnType;
00284       typedef R result_type; // Boost compatibility
00285 
00286       Future(Future const& future) :
00287         M_taskContext(future.M_taskContext)
00288       {  }
00289 
00290       template<class _Scheduler, class _Function>
00291         Future(_Scheduler& s, _Function f)
00292       {
00293         JoinableTask<ReturnType> jt(f);
00294         s.push(jt.proxy());
00295         M_taskContext = jt.M_taskContext;
00296       }
00297 
00298       Future()
00299       { }
00300 
00301       Future& operator=(Future const& r)
00302       {
00303         M_taskContext = r.M_taskContext;
00304         return (*this);
00305       }
00306 
00307       typename Details::ReturnTypeHelper<ReturnType>::Type join()
00308       {
00309         if (!this->bound())
00310           throw UnboundFuture();
00311         // Check if the proxy has released us.
00312         //
00313         if (!M_taskContext->MReleased)
00314         {
00315           boost::mutex::scoped_lock lock__(M_taskContext->M_mutex);
00316           while(!M_taskContext->MReleased)
00317             M_taskContext->M_condition.wait(lock__);
00318         } // Lock scope ends.
00319         if (typename Details::ReturnTypeHelper<ReturnType>::Type const* p =
00320               boost::get<typename Details::ReturnTypeHelper<ReturnType>::Type>(&M_taskContext->MResult))
00321         {
00322           return *p;
00323         }
00324         boost::apply_visitor(Rethrower(), M_taskContext->MResult);
00325         // We should never reach past this line.
00326         //
00327         std::unexpected();
00328       }
00329 
00330       typename Details::ReturnTypeHelper<ReturnType>::Type operator*()
00331       {
00332         return join();
00333       }
00334 
00335       bool bound()
00336       {
00337         return (M_taskContext);
00338       }
00339     private:
00340       struct Rethrower : boost::static_visitor<>
00341       {
00342         template<class Exception>
00343           void operator()(Exception& e) const
00344         {
00345           throw e;
00346         }
00347       };
00348 
00349       boost::shared_ptr<TaskContext<ReturnType> > M_taskContext;
00350     };
00351   };
00352 }
00353 
00354 #endif // RHA_FUTURES_HPP
00355