Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/core/StarRpcThreadPromise.hpp
blob: 896d21d5dc423c689ed41a89f9f8a55a6430bfb2 (plain)
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
#pragma once

#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 <typename Result, typename Error = String>
class RpcThreadPromiseKeeper {
public:
  void fulfill(Result result);
  void fail(Error error);

private:
  template <typename ResultT, typename ErrorT>
  friend class RpcThreadPromise;

  function<void(Result)> m_fulfill;
  function<void(Error)> m_fail;
};

template <typename Result, typename Error = String>
class RpcThreadPromise {
public:
  static pair<RpcThreadPromise, RpcThreadPromiseKeeper<Result, Error>> 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> result() const;

  // Returns the error of a failed rpc call.  Returns nothing if the call is
  // successful or not yet finished.
  Maybe<Error> error() const;

private:
  template <typename ResultT, typename ErrorT>
  friend class RpcThreadPromise;

  struct Value {
    Mutex mutex;

    Maybe<Result> result;
    Maybe<Error> error;
  };

  RpcThreadPromise() = default;

  function<Value*()> m_getValue;
};

template <typename Result, typename Error>
void RpcThreadPromiseKeeper<Result, Error>::fulfill(Result result) {
  m_fulfill(std::move(result));
}

template <typename Result, typename Error>
void RpcThreadPromiseKeeper<Result, Error>::fail(Error error) {
  m_fail(std::move(error));
}

template <typename Result, typename Error>
pair<RpcThreadPromise<Result, Error>, RpcThreadPromiseKeeper<Result, Error>> RpcThreadPromise<Result, Error>::createPair() {
  auto valuePtr = make_shared<Value>();

  RpcThreadPromise promise;
  promise.m_getValue = [valuePtr]() {
    return valuePtr.get();
  };

  RpcThreadPromiseKeeper<Result, Error> 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 <typename Result, typename Error>
RpcThreadPromise<Result, Error> RpcThreadPromise<Result, Error>::createFulfilled(Result result) {
  auto valuePtr = make_shared<Value>();
  valuePtr->result = std::move(result);

  RpcThreadPromise<Result, Error> promise;
  promise.m_getValue = [valuePtr]() {
    return valuePtr.get();
  };
  return promise;
}

template <typename Result, typename Error>
RpcThreadPromise<Result, Error> RpcThreadPromise<Result, Error>::createFailed(Error error) {
  auto valuePtr = make_shared<Value>();
  valuePtr->error = std::move(error);

  RpcThreadPromise<Result, Error> promise;
  promise.m_getValue = [valuePtr]() {
    return valuePtr.get();
  };
  return promise;
}

template <typename Result, typename Error>
bool RpcThreadPromise<Result, Error>::finished() const {
  auto val = m_getValue();
  MutexLocker lock(val->mutex);
  return val->result || val->error;
}

template <typename Result, typename Error>
bool RpcThreadPromise<Result, Error>::succeeded() const {
  auto val = m_getValue();
  MutexLocker lock(val->mutex);
  return val->result.isValid();
}

template <typename Result, typename Error>
bool RpcThreadPromise<Result, Error>::failed() const {
  auto val = m_getValue();
  MutexLocker lock(val->mutex);
  return val->error.isValid();
}

template <typename Result, typename Error>
Maybe<Result> RpcThreadPromise<Result, Error>::result() const {
  auto val = m_getValue();
  MutexLocker lock(val->mutex);
  return val->result;
}

template <typename Result, typename Error>
Maybe<Error> RpcThreadPromise<Result, Error>::error() const {
  auto val = m_getValue();
  MutexLocker lock(val->mutex);
  return val->error;
}

}