173 lines
5.1 KiB
C++
173 lines
5.1 KiB
C++
#pragma once
|
|
|
|
#include "StarEither.hpp"
|
|
#include "StarString.hpp"
|
|
|
|
namespace Star {
|
|
|
|
STAR_EXCEPTION(RpcPromiseException, StarException);
|
|
|
|
// The other side of an RpcPromise, can be used to either fulfill or fail a
|
|
// paired promise. Call either fulfill or fail function exactly once, any
|
|
// further invocations will result in an exception.
|
|
template <typename Result, typename Error = String>
|
|
class RpcPromiseKeeper {
|
|
public:
|
|
void fulfill(Result result);
|
|
void fail(Error error);
|
|
|
|
private:
|
|
template <typename ResultT, typename ErrorT>
|
|
friend class RpcPromise;
|
|
|
|
function<void(Result)> m_fulfill;
|
|
function<void(Error)> m_fail;
|
|
};
|
|
|
|
// A generic promise for the result of a remote procedure call. It has
|
|
// reference semantics and is implicitly shared, but is not thread safe.
|
|
template <typename Result, typename Error = String>
|
|
class RpcPromise {
|
|
public:
|
|
static pair<RpcPromise, RpcPromiseKeeper<Result, Error>> createPair();
|
|
static RpcPromise createFulfilled(Result result);
|
|
static RpcPromise createFailed(Error error);
|
|
|
|
// Has the respoonse either failed or succeeded?
|
|
bool finished() const;
|
|
// Has the response finished with success?
|
|
bool succeeded() const;
|
|
// Has the response finished with failure?
|
|
bool failed() const;
|
|
|
|
// Returns the result of the rpc call on success, nothing on failure or when
|
|
// not yet finished.
|
|
Maybe<Result> const& result() const;
|
|
|
|
// Returns the error of a failed rpc call. Returns nothing if the call is
|
|
// successful or not yet finished.
|
|
Maybe<Error> const& error() const;
|
|
|
|
// Wrap this RpcPromise into another promise which returns instead the result
|
|
// of this function when fulfilled
|
|
template <typename Function>
|
|
decltype(auto) wrap(Function function);
|
|
|
|
private:
|
|
template <typename ResultT, typename ErrorT>
|
|
friend class RpcPromise;
|
|
|
|
struct Value {
|
|
Maybe<Result> result;
|
|
Maybe<Error> error;
|
|
};
|
|
|
|
RpcPromise() = default;
|
|
|
|
function<Value const*()> m_getValue;
|
|
};
|
|
|
|
template <typename Result, typename Error>
|
|
void RpcPromiseKeeper<Result, Error>::fulfill(Result result) {
|
|
m_fulfill(std::move(result));
|
|
}
|
|
|
|
template <typename Result, typename Error>
|
|
void RpcPromiseKeeper<Result, Error>::fail(Error error) {
|
|
m_fail(std::move(error));
|
|
}
|
|
|
|
template <typename Result, typename Error>
|
|
pair<RpcPromise<Result, Error>, RpcPromiseKeeper<Result, Error>> RpcPromise<Result, Error>::createPair() {
|
|
auto valuePtr = std::make_shared<Value>();
|
|
|
|
RpcPromise promise;
|
|
promise.m_getValue = [valuePtr]() {
|
|
return valuePtr.get();
|
|
};
|
|
|
|
RpcPromiseKeeper<Result, Error> keeper;
|
|
keeper.m_fulfill = [valuePtr](Result result) {
|
|
if (valuePtr->result || valuePtr->error)
|
|
throw RpcPromiseException("fulfill called on already finished RpcPromise");
|
|
valuePtr->result = std::move(result);
|
|
};
|
|
keeper.m_fail = [valuePtr](Error error) {
|
|
if (valuePtr->result || valuePtr->error)
|
|
throw RpcPromiseException("fail called on already finished RpcPromise");
|
|
valuePtr->error = std::move(error);
|
|
};
|
|
|
|
return {std::move(promise), std::move(keeper)};
|
|
}
|
|
|
|
template <typename Result, typename Error>
|
|
RpcPromise<Result, Error> RpcPromise<Result, Error>::createFulfilled(Result result) {
|
|
auto valuePtr = std::make_shared<Value>();
|
|
valuePtr->result = std::move(result);
|
|
|
|
RpcPromise<Result, Error> promise;
|
|
promise.m_getValue = [valuePtr]() {
|
|
return valuePtr.get();
|
|
};
|
|
return promise;
|
|
}
|
|
|
|
template <typename Result, typename Error>
|
|
RpcPromise<Result, Error> RpcPromise<Result, Error>::createFailed(Error error) {
|
|
auto valuePtr = std::make_shared<Value>();
|
|
valuePtr->error = std::move(error);
|
|
|
|
RpcPromise<Result, Error> promise;
|
|
promise.m_getValue = [valuePtr]() {
|
|
return valuePtr.get();
|
|
};
|
|
return promise;
|
|
}
|
|
|
|
template <typename Result, typename Error>
|
|
bool RpcPromise<Result, Error>::finished() const {
|
|
auto val = m_getValue();
|
|
return val->result || val->error;
|
|
}
|
|
|
|
template <typename Result, typename Error>
|
|
bool RpcPromise<Result, Error>::succeeded() const {
|
|
return m_getValue()->result.isValid();
|
|
}
|
|
|
|
template <typename Result, typename Error>
|
|
bool RpcPromise<Result, Error>::failed() const {
|
|
return m_getValue()->error.isValid();
|
|
}
|
|
|
|
template <typename Result, typename Error>
|
|
Maybe<Result> const& RpcPromise<Result, Error>::result() const {
|
|
return m_getValue()->result;
|
|
}
|
|
|
|
template <typename Result, typename Error>
|
|
Maybe<Error> const& RpcPromise<Result, Error>::error() const {
|
|
return m_getValue()->error;
|
|
}
|
|
|
|
template <typename Result, typename Error>
|
|
template <typename Function>
|
|
decltype(auto) RpcPromise<Result, Error>::wrap(Function function) {
|
|
typedef RpcPromise<typename std::decay<decltype(function(std::declval<Result>()))>::type, Error> WrappedPromise;
|
|
WrappedPromise wrappedPromise;
|
|
wrappedPromise.m_getValue = [wrapper = std::move(function), valuePtr = std::make_shared<typename WrappedPromise::Value>(), otherGetValue = m_getValue]() {
|
|
if (!valuePtr->result && !valuePtr->error) {
|
|
auto otherValue = otherGetValue();
|
|
if (otherValue->result)
|
|
valuePtr->result.set(wrapper(*otherValue->result));
|
|
else if (otherValue->error)
|
|
valuePtr->error.set(*otherValue->error);
|
|
}
|
|
return valuePtr.get();
|
|
};
|
|
return wrappedPromise;
|
|
}
|
|
|
|
}
|