#ifndef _FC_FUTURE_HPP_ #define _FC_FUTURE_HPP_ #include #include #include #include #include #include namespace fc { class abstract_thread; class void_t; class priority; class exception_ptr; class thread; namespace detail { class completion_handler { public: virtual ~completion_handler(){}; virtual void on_complete( const void* v, const fc::exception_ptr& e ) = 0; }; template class completion_handler_impl : public completion_handler { public: completion_handler_impl( Functor&& f ):_func(fc::move(f)){} completion_handler_impl( const Functor& f ):_func(f){} virtual void on_complete( const void* v, const fc::exception_ptr& e ) { _func( *static_cast(v), e); } private: Functor _func; }; template class completion_handler_impl : public completion_handler { public: completion_handler_impl( Functor&& f ):_func(fc::move(f)){} completion_handler_impl( const Functor& f ):_func(f){} virtual void on_complete( const void* v, const fc::exception_ptr& e ) { _func(e); } private: Functor _func; }; } class promise_base : virtual public retainable { public: typedef shared_ptr ptr; promise_base(const char* desc="?"); const char* get_desc()const; void cancel(); bool ready()const; bool error()const; void set_exception( const fc::exception_ptr& e ); protected: void _wait( const microseconds& timeout_us ); void _wait_until( const time_point& timeout_us ); void _enqueue_thread(); void _notify(); void _set_timeout(); void _set_value(const void* v); void _on_complete( detail::completion_handler* c ); ~promise_base(); private: friend class thread; friend struct context; friend class thread_d; bool _ready; mutable spin_yield_lock _spin_yield; thread* _blocked_thread; time_point _timeout; fc::exception_ptr _except; bool _canceled; const char* _desc; detail::completion_handler* _compl; }; template class promise : virtual public promise_base { public: typedef shared_ptr< promise > ptr; promise( const char* desc = "?" ):promise_base(desc){} promise( const T& val ){ set_value(val); } promise( T&& val ){ set_value(fc::move(val) ); } const T& wait(const microseconds& timeout = microseconds::max() ){ this->_wait( timeout ); return *result; } const T& wait_until(const time_point& tp ) { this->_wait_until( tp ); return *result; } void set_value( const T& v ) { result = v; _set_value(&*result); } void set_value( T&& v ) { result = fc::move(v); _set_value(&*result); } template void on_complete( CompletionHandler&& c ) { _on_complete( new detail::completion_handler_impl(fc::forward(c)) ); } protected: optional result; ~promise(){} }; template<> class promise : virtual public promise_base { public: typedef shared_ptr< promise > ptr; promise( const char* desc = "?" ):promise_base(desc){} promise( const void_t& v ){ set_value(); } void wait(const microseconds& timeout = microseconds::max() ){ this->_wait( timeout ); } void wait_until(const time_point& tp ) { this->_wait_until( tp ); } void set_value(){ this->_set_value(nullptr); } void set_value( const void_t& v ) { this->_set_value(nullptr); } template void on_complete( CompletionHandler&& c ) { _on_complete( new detail::completion_handler_impl(fc::forward(c)) ); } protected: ~promise(){} }; /** * @brief a placeholder for the result of an asynchronous operation. * * By calling future::wait() you will block the current fiber until * the asynchronous operation completes. * * If you would like to use an asynchronous interface instead of the synchronous * 'wait' method you could specify a CompletionHandler which is a method that takes * two parameters, a const reference to the value and an exception_ptr. If the * exception_ptr is set, the value reference is invalid and accessing it is * 'undefined'. * * Promises have pointer semantics, futures have reference semantics that * contain a shared pointer to a promise. */ template class future { public: future( const shared_ptr>& p ):m_prom(p){} future( shared_ptr>&& p ):m_prom(fc::move(p)){} future(){} /// @pre valid() /// @post ready() /// @throws timeout const T& wait( const microseconds& timeout = microseconds::max() ){ return m_prom->wait(timeout); } /// @pre valid() /// @post ready() /// @throws timeout const T& wait_until( const time_point& tp ) { return m_prom->wait_until(tp); } bool valid()const { return !!m_prom; } /// @pre valid() bool ready()const { return m_prom->ready(); } /// @pre valid() bool error()const { return m_prom->error(); } void cancel()const { m_prom->cancel(); } /** * @pre valid() * * The given completion handler will be called from some * arbitrary thread and should not 'block'. Generally * it should post an event or start a new async operation. */ template void on_complete( CompletionHandler&& c ) { m_prom->on_complete( fc::forward(c) ); } private: shared_ptr> m_prom; }; template<> class future { public: future( const shared_ptr>& p ):m_prom(p){} future( shared_ptr>&& p ):m_prom(fc::move(p)){} future(){} /// @pre valid() /// @post ready() /// @throws timeout void wait( const microseconds& timeout = microseconds::max() ){ m_prom->wait(timeout); } /// @pre valid() /// @post ready() /// @throws timeout void wait_until( const time_point& tp ) { m_prom->wait_until(tp); } bool valid()const { return !!m_prom; } /// @pre valid() bool ready()const { return m_prom->ready(); } /// @pre valid() bool error()const { return m_prom->error(); } void cancel()const { m_prom->cancel(); } template void on_complete( CompletionHandler&& c ) { m_prom->on_complete( fc::forward(c) ); } private: shared_ptr> m_prom; }; } #endif // _FC_FUTURE_HPP_