peerplays-fc/include/fc/future.hpp
2012-11-01 00:23:06 -04:00

253 lines
7.3 KiB
C++

#ifndef _FC_FUTURE_HPP_
#define _FC_FUTURE_HPP_
#include <fc/utility.hpp>
#include <fc/time.hpp>
#include <fc/shared_ptr.hpp>
#include <fc/exception.hpp>
#include <fc/spin_yield_lock.hpp>
#include <fc/optional.hpp>
namespace fc {
class abstract_thread;
struct 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<typename Functor, typename T>
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<const T*>(v), e);
}
private:
Functor _func;
};
template<typename Functor>
class completion_handler_impl<Functor,void> : 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<promise_base> 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<typename T = void>
class promise : virtual public promise_base {
public:
typedef shared_ptr< promise<T> > 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<typename CompletionHandler>
void on_complete( CompletionHandler&& c ) {
_on_complete( new detail::completion_handler_impl<CompletionHandler,T>(fc::forward<CompletionHandler>(c)) );
}
protected:
optional<T> result;
~promise(){}
};
template<>
class promise<void> : virtual public promise_base {
public:
typedef shared_ptr< promise<void> > 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<typename CompletionHandler>
void on_complete( CompletionHandler&& c ) {
_on_complete( new detail::completion_handler_impl<CompletionHandler,void>(fc::forward<CompletionHandler>(c)) );
}
protected:
~promise(){}
};
/**
* @brief a placeholder for the result of an asynchronous operation.
*
* By calling future<T>::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<typename T>
class future {
public:
future( const shared_ptr<promise<T>>& p ):m_prom(p){}
future( shared_ptr<promise<T>>&& 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<typename CompletionHandler>
void on_complete( CompletionHandler&& c ) {
m_prom->on_complete( fc::forward<CompletionHandler>(c) );
}
private:
friend class thread;
shared_ptr<promise<T>> m_prom;
};
template<>
class future<void> {
public:
future( const shared_ptr<promise<void>>& p ):m_prom(p){}
future( shared_ptr<promise<void>>&& 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<typename CompletionHandler>
void on_complete( CompletionHandler&& c ) {
m_prom->on_complete( fc::forward<CompletionHandler>(c) );
}
private:
friend class thread;
shared_ptr<promise<void>> m_prom;
};
}
#endif // _FC_FUTURE_HPP_