/* * Copyright Onix Solutions Limited [OnixS]. All rights reserved. * * This software owned by Onix Solutions Limited [OnixS] and is protected by copyright law * and international copyright treaties. * * Access to and use of the software is governed by the terms of the applicable OnixS Software * Services Agreement (the Agreement) and Customer end user license agreements granting * a non-assignable, non-transferable and non-exclusive license to use the software * for it's own data processing purposes under the terms defined in the Agreement. * * Except as otherwise granted within the terms of the Agreement, copying or reproduction of any part * of this source code or associated reference material to any other location for further reproduction * or redistribution, and any amendments to this copyright notice, are expressly prohibited. * * Any reproduction or redistribution for sale or hiring of the Software not in accordance with * the terms of the Agreement is a violation of copyright law. */ #pragma once #include #include #include #ifdef ONIXS_FIXENGINE_CXX11 #include #endif namespace OnixS { namespace System { class FutureSharedState; } namespace Threading { /// The state of a \c SharedFuture object (similar to std::future_status, @see http://en.cppreference.com/w/cpp/thread/future_status ). struct FutureStatus { enum Enum { /// The shared state is ready. ready, /// The shared state did not become ready before the specified timeout duration has passed. timeout, /// The shared state contains a deferred function, so the result will be computed only when explicitly requested. deferred }; }; namespace Implementation { class FutureHelper; class PromiseBase; // Implementation details. class ValueBase { public: virtual ~ValueBase() ONIXS_FIXENGINE_DEFAULT; virtual ValueBase * clone() const = 0; }; /// Implementation details. class FutureValue { public: FutureValue(ValueBase * value); FutureValue(); FutureValue(const FutureValue & other); ~FutureValue(); ValueBase * value() const { return value_; } private: ValueBase * value_; }; /// Implementation details. template class Value : public ValueBase { typedef Value Type; public: static const ValueType & value(const ValueBase * value) { return static_cast(value)->value(); } static ValueBase * create(const ValueType & value) { return new Type(value); } ValueBase * clone() const ONIXS_FIXENGINE_OVERRIDE { return create(value_); } private: explicit Value(const ValueType & value) : value_(value) {} const ValueType & value() const { return value_; } const ValueType value_; }; /// The base implementation of the SharedFuture. class FutureBase { public: /// Check if a future instance is associated with an asynchronous result. /// /// Returns \c true if the *this has an associated asynchronous result, false otherwise. bool valid() const ONIXS_FIXENGINE_NOTHROW { return state_ != ONIXS_FIXENGINE_NULLPTR; } /// Returns \c true if the asynchronous result associated with this Future is ready /// (has a value or an exception stored in the shared state), \c false otherwise. /// /// There are often situations where a \c get() call on a Future may not be a blocking call, /// or is only a blocking call under certain circumstances. /// This method gives the ability to test for early completion and allows us to avoid /// associating a continuation, which needs to be scheduled with some non-trivial overhead /// and near-certain loss of the cache efficiency. /// /// @throw The std::logic_error if this instance does not refer to a shared state. ONIXS_FIXENGINE_API bool isReady() const; /// Returns \c true if the asynchronous result associated with this Future has a stored value, /// \c false otherwise. /// /// @throw The std::logic_error if this instance does not refer to a shared state. ONIXS_FIXENGINE_API bool hasValue() const; /// Returns \c true if the asynchronous result associated with this Future has a stored exception, /// \c false otherwise. /// /// @throw The std::logic_error if this instance does not refer to a shared state. ONIXS_FIXENGINE_API bool hasException() const; #ifdef ONIXS_FIXENGINE_CXX11 /// Returns the stored exception. /// /// @throw The std::logic_error if this instance does not refer to a shared state. /// @throw The std::logic_error if the operation has *not* finished with an error. ONIXS_FIXENGINE_API std::exception_ptr getExceptionPtr() const; #endif enum { InfiniteTimeout = -1 }; /// Waits for the result to become available during the timeout. /// /// \note Calling \c wait on the same Future from multiple threads is \e not safe; /// the intended use is for each thread that waits on the same shared state to have a \e copy of a Future. /// /// \throw The std::logic_error if this instance does not refer to a shared state. ONIXS_FIXENGINE_API FutureStatus::Enum wait(int timeoutInMs = InfiniteTimeout) const; protected: FutureBase() : state_() {} ONIXS_FIXENGINE_API FutureBase(const FutureBase & other) ONIXS_FIXENGINE_NOTHROW; ONIXS_FIXENGINE_API FutureBase & operator=(const FutureBase & other) ONIXS_FIXENGINE_NOTHROW; #ifdef ONIXS_FIXENGINE_CXX11 ONIXS_FIXENGINE_API FutureBase(FutureBase && other) ONIXS_FIXENGINE_NOTHROW; ONIXS_FIXENGINE_API FutureBase & operator=(FutureBase && other) ONIXS_FIXENGINE_NOTHROW; #endif struct moving_init_t {}; /// Initializes the instance with shared state. ONIXS_FIXENGINE_API FutureBase(const System::FutureSharedState * state) ONIXS_FIXENGINE_NOTHROW; /// Initializes the instance with shared state. ONIXS_FIXENGINE_API FutureBase(const System::FutureSharedState * state, moving_init_t) ONIXS_FIXENGINE_NOTHROW; /// Destroys a future object. /// /// If this is the last reference to the asynchronous result associated with *this (if any), then destroy that asynchronous result. ONIXS_FIXENGINE_API ~FutureBase() ONIXS_FIXENGINE_NOTHROW; ONIXS_FIXENGINE_API void swap(FutureBase & other) ONIXS_FIXENGINE_NOTHROW; ONIXS_FIXENGINE_API const FutureValue & getValue() const; ONIXS_FIXENGINE_API void getVoid() const; private: const System::FutureSharedState * state_; friend class FutureHelper; friend class PromiseBase; }; template struct FutureGetReturn { typedef const T & Type; }; template<> struct FutureGetReturn { typedef void Type; }; } /// Represents a future result of an asynchronous operation - a result that will eventually appear in the Future after /// the processing is complete. /// /// The class template Future provides a mechanism to access the result of \e asynchronous operations : /// \li An asynchronous operation (e.g. created via Promise) can provide a Future object to the creator of that asynchronous /// operation (e.g. via the Promise::getFuture). /// \li The creator of the asynchronous operation can then use a variety of methods to query, wait for, or extract a value from Future. /// These methods may block if the asynchronous operation has not yet provided a value. /// \li When the asynchronous operation is ready to send a result to the creator, it can do so by modifying the shared state /// (e.g. via the Promise::setValue) that is linked to the creator's Future. /// /// The Future is the synchronization object constructed around the \e receiving end of the Promise channel. It allows for the separation /// of the initiation of an operation and the act of waiting for its result. /// /// The Future is \e copiable and multiple SharedFuture objects may refer to the same shared state. /// /// Unlike the std::future, this implementation has an extended API, allowing you to query the state of SharedFuture. /// The internal implementation is lock-free; therefore, setting and polling states do not lead to blocking of threads and/or switching /// of an execution context. /// /// \note An access to the same shared state from multiple threads is safe if each thread does it through its own copy of /// a SharedFuture object. /// /// \see http://en.cppreference.com/w/cpp/thread/shared_future . template class SharedFuture : public Implementation::FutureBase { public: SharedFuture() ONIXS_FIXENGINE_NOTHROW : FutureBase() {} /// The copy constructor. SharedFuture(const SharedFuture & other) ONIXS_FIXENGINE_NOTHROW : FutureBase(other) {} /// The copy assignment. SharedFuture & operator=(const SharedFuture & other) ONIXS_FIXENGINE_NOTHROW { FutureBase::operator=(other); return *this; } #ifdef ONIXS_FIXENGINE_CXX11 SharedFuture(SharedFuture && other) ONIXS_FIXENGINE_NOTHROW : FutureBase(std::move(other)) {} FutureBase & operator=(SharedFuture && other) ONIXS_FIXENGINE_NOTHROW { FutureBase::operator=(std::move(other)); return *this; } #endif /// Returns the result. If the result is not ready, the method will block. When completes, it either returns a value or throws an exception. /// /// This method waits until the Future has a valid result and retrieves it. It effectively calls the wait() in order to wait for the result. /// /// \exception If an exception was stored in the shared state referenced by this Future (e.g. via a call to the Promise::setException()) then that exception will be thrown. typename Implementation::FutureGetReturn::Type get() const { return Implementation::Value::value(getValue().value()); } /// swaps two SharedFuture objects void swap(SharedFuture & other) ONIXS_FIXENGINE_NOTHROW { FutureBase::swap(other); } private: /// Initializes the instance with shared state. SharedFuture(const System::FutureSharedState * state) : FutureBase(state) {} /// Initializes the instance with shared state. SharedFuture(const System::FutureSharedState * state, moving_init_t t) ONIXS_FIXENGINE_NOTHROW : FutureBase(state, t) {} friend class Implementation::FutureHelper; friend class Implementation::PromiseBase; }; template<> inline void SharedFuture::get() const { getVoid(); } namespace Implementation { /// The base implementation of the PromiseFuture. class PromiseBase { public: /// Check if a future instance is associated with an asynchronous result. /// /// Returns \c true if the *this has an associated asynchronous result, false otherwise. bool valid(); ONIXS_FIXENGINE_API void setExceptionImpl(); protected: ONIXS_FIXENGINE_API PromiseBase() ONIXS_FIXENGINE_NOTHROW; ONIXS_FIXENGINE_API PromiseBase(const PromiseBase & other) ONIXS_FIXENGINE_NOTHROW; ONIXS_FIXENGINE_API PromiseBase & operator=(const PromiseBase & other) ONIXS_FIXENGINE_NOTHROW; #ifdef ONIXS_FIXENGINE_CXX11 ONIXS_FIXENGINE_API PromiseBase(PromiseBase && other) ONIXS_FIXENGINE_NOTHROW; ONIXS_FIXENGINE_API PromiseBase & operator=(PromiseBase && other) ONIXS_FIXENGINE_NOTHROW; #endif /// Destroys a future object. /// /// If this is the last reference to the asynchronous result associated with *this (if any), then destroy that asynchronous result. ONIXS_FIXENGINE_API ~PromiseBase() ONIXS_FIXENGINE_NOTHROW; ONIXS_FIXENGINE_API void swap(PromiseBase & other) ONIXS_FIXENGINE_NOTHROW; ONIXS_FIXENGINE_API void setValue(ValueBase * value); ONIXS_FIXENGINE_API void setVoid(); ONIXS_FIXENGINE_API SharedFuture getFutureImpl() const; template SharedFuture getTypedFuture() const { return SharedFuture(getFutureImpl().state_); } private: /// Implementation details. struct Impl; Impl * impl_; }; } /// Provides a facility to store a value or an exception that can be acquired asynchronously via a `SharedFuture` object created by the `Promise` object. /// Each `Promise` object is associated with a shared state, which contains a some state information and a result which may be not yet evaluated, evaluated to a value or evaluated to an exception. /// The promise stores the result or the exception in the shared state. Marks the state ready and unblocks any thread waiting on a future associated with the shared state. template class Promise : public Implementation::PromiseBase { public: Promise() ONIXS_FIXENGINE_NOTHROW : PromiseBase() {} /// The copy constructor. Promise(const Promise & other) ONIXS_FIXENGINE_NOTHROW : PromiseBase(other) {} /// The copy assignment. Promise & operator=(const Promise & other) ONIXS_FIXENGINE_NOTHROW { PromiseBase::operator=(other); return *this; } #ifdef ONIXS_FIXENGINE_CXX11 Promise(Promise && other) ONIXS_FIXENGINE_NOTHROW : PromiseBase(std::move(other)) {} Promise & operator=(Promise && other) ONIXS_FIXENGINE_NOTHROW { PromiseBase::operator=(std::move(other)); return *this; } #endif /// Swaps two Promise objects void swap(Promise & other) ONIXS_FIXENGINE_NOTHROW { PromiseBase::swap(other); } SharedFuture getFuture() const { return getTypedFuture(); } /// Sets the successful result value to the given shared state. void set(const T & value) { setValue(Implementation::Value::create(value)); } /// Sets the result to indicate an exception. /// /// @note Does nothing if the shared state already stores a value or exception. template void setException(const ExceptionType & exception) { try { throw exception; } catch(...) { setExceptionImpl(); } } }; template<> class Promise : public Implementation::PromiseBase { public: Promise() ONIXS_FIXENGINE_NOTHROW : PromiseBase() {} /// The copy constructor. Promise(const Promise & other) ONIXS_FIXENGINE_NOTHROW : PromiseBase(other) {} /// The copy assignment. Promise & operator=(const Promise & other) ONIXS_FIXENGINE_NOTHROW { PromiseBase::operator=(other); return *this; } #ifdef ONIXS_FIXENGINE_CXX11 Promise(Promise && other) ONIXS_FIXENGINE_NOTHROW : PromiseBase(std::move(other)) {} Promise & operator=(Promise && other) ONIXS_FIXENGINE_NOTHROW { PromiseBase::operator=(std::move(other)); return *this; } #endif /// Swaps two Promise objects void swap(Promise & other) ONIXS_FIXENGINE_NOTHROW { PromiseBase::swap(other); } SharedFuture getFuture() const { return getTypedFuture(); } /// Sets the successful result to the given shared state. void set() { setVoid(); } /// Sets the result to indicate an exception. /// /// @note Does nothing if the shared state already stores a value or exception. template void setException(const ExceptionType & exception) { try { throw exception; } catch(...) { setExceptionImpl(); } } }; } }