512 lines
15 KiB
C++
512 lines
15 KiB
C++
/*
|
|
* 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 <OnixS/FIXEngine/ABI.h>
|
|
#include <OnixS/FIXEngine/Compiler.h>
|
|
#include <OnixS/FIXEngine/Threading/Definitions.h>
|
|
|
|
#ifdef ONIXS_FIXENGINE_CXX11
|
|
#include <exception>
|
|
#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<typename ValueType>
|
|
class Value : public ValueBase
|
|
{
|
|
typedef Value<ValueType> Type;
|
|
|
|
public:
|
|
|
|
static const ValueType & value(const ValueBase * value)
|
|
{
|
|
return static_cast<const Type*>(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<T>.
|
|
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<typename T>
|
|
struct FutureGetReturn
|
|
{
|
|
typedef const T & Type;
|
|
};
|
|
|
|
template<>
|
|
struct FutureGetReturn<void>
|
|
{
|
|
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 <em> shared state </em>
|
|
/// (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 <typename T>
|
|
class SharedFuture : public Implementation::FutureBase
|
|
{
|
|
public:
|
|
SharedFuture() ONIXS_FIXENGINE_NOTHROW
|
|
: FutureBase() {}
|
|
|
|
/// The copy constructor.
|
|
SharedFuture(const SharedFuture<T> & other) ONIXS_FIXENGINE_NOTHROW
|
|
: FutureBase(other) {}
|
|
|
|
/// The copy assignment.
|
|
SharedFuture<T> & operator=(const SharedFuture<T> & other) ONIXS_FIXENGINE_NOTHROW
|
|
{
|
|
FutureBase::operator=(other);
|
|
return *this;
|
|
}
|
|
|
|
#ifdef ONIXS_FIXENGINE_CXX11
|
|
|
|
SharedFuture(SharedFuture<T> && other) ONIXS_FIXENGINE_NOTHROW
|
|
: FutureBase(std::move(other)) {}
|
|
|
|
FutureBase & operator=(SharedFuture<T> && 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<T>::Type get() const
|
|
{
|
|
return Implementation::Value<T>::value(getValue().value());
|
|
}
|
|
|
|
/// swaps two SharedFuture objects
|
|
void swap(SharedFuture<T> & 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<void>::get() const
|
|
{
|
|
getVoid();
|
|
}
|
|
|
|
namespace Implementation {
|
|
|
|
/// The base implementation of the PromiseFuture<T>.
|
|
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<FutureValue> getFutureImpl() const;
|
|
|
|
template<typename T>
|
|
SharedFuture<T> getTypedFuture() const
|
|
{
|
|
return SharedFuture<T>(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 <typename T>
|
|
class Promise : public Implementation::PromiseBase
|
|
{
|
|
public:
|
|
|
|
Promise() ONIXS_FIXENGINE_NOTHROW
|
|
: PromiseBase() {}
|
|
|
|
/// The copy constructor.
|
|
Promise(const Promise<T> & other) ONIXS_FIXENGINE_NOTHROW
|
|
: PromiseBase(other) {}
|
|
|
|
/// The copy assignment.
|
|
Promise<T> & operator=(const Promise<T> & other) ONIXS_FIXENGINE_NOTHROW
|
|
{
|
|
PromiseBase::operator=(other);
|
|
return *this;
|
|
}
|
|
|
|
#ifdef ONIXS_FIXENGINE_CXX11
|
|
|
|
Promise(Promise<T> && other) ONIXS_FIXENGINE_NOTHROW
|
|
: PromiseBase(std::move(other)) {}
|
|
|
|
Promise & operator=(Promise<T> && other) ONIXS_FIXENGINE_NOTHROW
|
|
{
|
|
PromiseBase::operator=(std::move(other));
|
|
return *this;
|
|
}
|
|
|
|
#endif
|
|
|
|
/// Swaps two Promise objects
|
|
void swap(Promise<T> & other) ONIXS_FIXENGINE_NOTHROW
|
|
{
|
|
PromiseBase::swap(other);
|
|
}
|
|
|
|
SharedFuture<T> getFuture() const
|
|
{
|
|
return getTypedFuture<T>();
|
|
}
|
|
|
|
/// Sets the successful result value to the given shared state.
|
|
void set(const T & value)
|
|
{
|
|
setValue(Implementation::Value<T>::create(value));
|
|
}
|
|
|
|
/// Sets the result to indicate an exception.
|
|
///
|
|
/// @note Does nothing if the shared state already stores a value or exception.
|
|
template<typename ExceptionType>
|
|
void setException(const ExceptionType & exception)
|
|
{
|
|
try
|
|
{
|
|
throw exception;
|
|
}
|
|
catch(...)
|
|
{
|
|
setExceptionImpl();
|
|
}
|
|
}
|
|
};
|
|
|
|
template<>
|
|
class Promise<void> : public Implementation::PromiseBase
|
|
{
|
|
public:
|
|
|
|
Promise() ONIXS_FIXENGINE_NOTHROW
|
|
: PromiseBase() {}
|
|
|
|
/// The copy constructor.
|
|
Promise(const Promise<void> & other) ONIXS_FIXENGINE_NOTHROW
|
|
: PromiseBase(other) {}
|
|
|
|
/// The copy assignment.
|
|
Promise<void> & operator=(const Promise<void> & other) ONIXS_FIXENGINE_NOTHROW
|
|
{
|
|
PromiseBase::operator=(other);
|
|
return *this;
|
|
}
|
|
|
|
#ifdef ONIXS_FIXENGINE_CXX11
|
|
|
|
Promise(Promise<void> && other) ONIXS_FIXENGINE_NOTHROW
|
|
: PromiseBase(std::move(other)) {}
|
|
|
|
Promise & operator=(Promise<void> && other) ONIXS_FIXENGINE_NOTHROW
|
|
{
|
|
PromiseBase::operator=(std::move(other));
|
|
return *this;
|
|
}
|
|
|
|
#endif
|
|
|
|
/// Swaps two Promise objects
|
|
void swap(Promise<void> & other) ONIXS_FIXENGINE_NOTHROW
|
|
{
|
|
PromiseBase::swap(other);
|
|
}
|
|
|
|
SharedFuture<void> getFuture() const
|
|
{
|
|
return getTypedFuture<void>();
|
|
}
|
|
|
|
/// 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<typename ExceptionType>
|
|
void setException(const ExceptionType & exception)
|
|
{
|
|
try
|
|
{
|
|
throw exception;
|
|
}
|
|
catch(...)
|
|
{
|
|
setExceptionImpl();
|
|
}
|
|
}
|
|
};
|
|
|
|
}
|
|
}
|