1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
#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;
}
}
|