#ifndef STAR_RPC_THREAD_PROMISE_HPP #define STAR_RPC_THREAD_PROMISE_HPP #include "StarEither.hpp" #include "StarString.hpp" #include "StarThread.hpp" // A thread-safe version of RpcPromise. // This is just a copy-paste with a Mutex tacked on. I don't like that, but it's 11 PM. namespace Star { STAR_EXCEPTION(RpcThreadPromiseException, StarException); template class RpcThreadPromiseKeeper { public: void fulfill(Result result); void fail(Error error); private: template friend class RpcThreadPromise; function m_fulfill; function m_fail; }; template class RpcThreadPromise { public: static pair> createPair(); static RpcThreadPromise createFulfilled(Result result); static RpcThreadPromise 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; // Returns the error of a failed rpc call. Returns nothing if the call is // successful or not yet finished. Maybe error() const; private: template friend class RpcThreadPromise; struct Value { Mutex mutex; Maybe result; Maybe error; }; RpcThreadPromise() = default; function m_getValue; }; template void RpcThreadPromiseKeeper::fulfill(Result result) { m_fulfill(std::move(result)); } template void RpcThreadPromiseKeeper::fail(Error error) { m_fail(std::move(error)); } template pair, RpcThreadPromiseKeeper> RpcThreadPromise::createPair() { auto valuePtr = make_shared(); RpcThreadPromise promise; promise.m_getValue = [valuePtr]() { return valuePtr.get(); }; RpcThreadPromiseKeeper keeper; keeper.m_fulfill = [valuePtr](Result result) { MutexLocker lock(valuePtr->mutex); if (valuePtr->result || valuePtr->error) throw RpcThreadPromiseException("fulfill called on already finished RpcThreadPromise"); valuePtr->result = std::move(result); }; keeper.m_fail = [valuePtr](Error error) { MutexLocker lock(valuePtr->mutex); if (valuePtr->result || valuePtr->error) throw RpcThreadPromiseException("fail called on already finished RpcThreadPromise"); valuePtr->error = std::move(error); }; return {std::move(promise), std::move(keeper)}; } template RpcThreadPromise RpcThreadPromise::createFulfilled(Result result) { auto valuePtr = make_shared(); valuePtr->result = std::move(result); RpcThreadPromise promise; promise.m_getValue = [valuePtr]() { return valuePtr.get(); }; return promise; } template RpcThreadPromise RpcThreadPromise::createFailed(Error error) { auto valuePtr = make_shared(); valuePtr->error = std::move(error); RpcThreadPromise promise; promise.m_getValue = [valuePtr]() { return valuePtr.get(); }; return promise; } template bool RpcThreadPromise::finished() const { auto val = m_getValue(); MutexLocker lock(val->mutex); return val->result || val->error; } template bool RpcThreadPromise::succeeded() const { auto val = m_getValue(); MutexLocker lock(val->mutex); return val->result.isValid(); } template bool RpcThreadPromise::failed() const { auto val = m_getValue(); MutexLocker lock(val->mutex); return val->error.isValid(); } template Maybe RpcThreadPromise::result() const { auto val = m_getValue(); MutexLocker lock(val->mutex); return val->result; } template Maybe RpcThreadPromise::error() const { auto val = m_getValue(); MutexLocker lock(val->mutex); return val->error; } } #endif