osb/source/core/StarRpcPromise.hpp
2024-02-25 15:46:47 +01:00

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;
}
}