MOTION  0.01
Framework for mixed-protocol multi-party computation
reusable_future.h
Go to the documentation of this file.
1 // MIT License
2 //
3 // Copyright (c) 2019 Lennart Braun
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in all
13 // copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 
23 // This file contains promise/future-like data structures with the difference
24 // that they can be reused: After the value stored in the future was
25 // retrieved, a new value can be set using the same promise.
26 //
27 // We cannot throw away our future(s) all the time, we need to recycle them.
28 
29 #pragma once
30 
31 #include <boost/fiber/future.hpp>
32 #include <condition_variable>
33 #include <future>
34 #include <mutex>
35 #include <type_traits>
36 
37 namespace encrypto::motion {
38 
39 namespace detail {
40 
41 // shared state to be used by ReusableFuture and ReusablePromise
42 template <typename R, typename MutexType, typename ConditionVariableType>
44  public:
45  ReusableSharedState() : contains_value_(false) {}
47  if (contains_value_) {
48  // delete the object
49  delete_helper();
50  }
51  }
52 
53  // set value
54  template <typename Argument>
55  void set(Argument&& argument) {
56  {
57  std::scoped_lock lock(mutex_);
58  if (contains_value_) {
59  throw std::future_error(std::future_errc::promise_already_satisfied);
60  }
61  // construct R from argument in the pre-allocated value_storage
62  new (&value_storage_) R(std::forward<Argument>(argument));
63  contains_value_ = true;
64  }
65  condition_variable_.notify_all();
66  }
67 
68  // remove value if present
69  void reset() noexcept {
70  std::unique_lock lock(mutex_);
71  if (contains_value_) {
72  // destroy object
73  delete_helper();
74  contains_value_ = false;
75  }
76  }
77 
78  // wait until there is a value
79  void wait() const noexcept {
80  std::unique_lock lock(mutex_);
81  wait_helper(lock);
82  }
83 
84  // move value out of the shared state
85  R move() noexcept {
86  std::unique_lock lock(mutex_);
87  wait_helper(lock);
88  contains_value_ = false;
89  return std::move(*reinterpret_cast<R*>(&value_storage_));
90  }
91 
92  // check if there is some value stored
93  bool contains_value() const noexcept {
94  std::scoped_lock lock(mutex_);
95  return contains_value_;
96  }
97 
98  private:
99  // storage for the value
100  std::aligned_storage_t<sizeof(R), std::alignment_of_v<R>> value_storage_;
101 
102  // status: true -> there is a value in the shared state
103  bool contains_value_;
104 
105  // synchronization stuff
106  mutable MutexType mutex_;
107  mutable ConditionVariableType condition_variable_;
108 
109  // helper functions
110  void wait_helper(std::unique_lock<decltype(mutex_)>& lock) const noexcept {
111  if (!contains_value_) {
112  condition_variable_.wait(lock, [this] { return contains_value_; });
113  }
114  }
115 
116  // delete the stored object
117  void delete_helper() noexcept { reinterpret_cast<R*>(&value_storage_)->~R(); }
118 };
119 
120 } // namespace detail
121 
122 template <typename R, typename MutexType, typename ConditionVariableType>
124 
125 // std::future-like future whose value can be set and read repeatedly,
126 // basically the consumer end of a channel with a capacity of one
127 template <typename R, typename MutexType = std::mutex,
128  typename ConditionVariableType = std::condition_variable>
130  public:
131  // create future without associated state
132  ReusableFuture() noexcept : shared_state_(nullptr) {}
133 
134  // cannot copy construct future
135  ReusableFuture(const ReusableFuture&) = delete;
136 
137  // move constructor
138  ReusableFuture(ReusableFuture&& other) noexcept : shared_state_(std::move(other.shared_state_)) {
139  other.shared_state_ = nullptr;
140  }
141 
142  ~ReusableFuture() = default;
143 
144  // cannot copy-assign
145  ReusableFuture& operator=(const ReusableFuture&) = delete;
146 
147  // move-assign
149  shared_state_ = std::move(other.shared_state_);
150  other.shared_state_ = nullptr;
151  return *this;
152  }
153 
154  // retrieve the stored value
155  R get() {
156  if (!shared_state_) {
157  throw std::future_error(std::future_errc::no_state);
158  }
159  return shared_state_->move();
160  }
161 
162  // swap two futures
163  void swap(ReusableFuture& other) noexcept { std::swap(shared_state_, other.shared_state_); };
164 
165  // check if the future has an associated state
166  bool valid() const noexcept { return shared_state_ != nullptr; }
167 
168  // wait until the future gets ready
169  void wait() const { shared_state_->wait(); }
170 
171  // TODO: wait_for, wait_until
172 
173  private:
174  // allow ReusablePromise to use the following constructor
176 
177  // create future with associated state
179  shared_state) noexcept
180  : shared_state_(std::move(shared_state)) {}
181 
182  // pointer to the shared state
183  std::shared_ptr<detail::ReusableSharedState<R, MutexType, ConditionVariableType>> shared_state_;
184 };
185 
186 template <typename R>
187 using ReusableFiberFuture =
189 
190 // std::promise-like promise which can be used repeatedly to set thevalue in the corresponding
191 // shared state, basically the produceer end of a channel with a capacity of one
192 template <typename R, typename MutexType = std::mutex,
193  typename ConditionVariableType = std::condition_variable>
194 class ReusablePromise {
195  public:
196  ReusablePromise() noexcept
197  : shared_state_(
198  std::make_shared<detail::ReusableSharedState<R, MutexType, ConditionVariableType>>()),
199  future_retrieved_(false) {}
200 
201  // no copy constructor
202  ReusablePromise(const ReusablePromise&) = delete;
203 
204  // move constructor
206  : shared_state_(other.shared_state_), future_retrieved_(other.future_retrieved_) {
207  other.shared_state_ = nullptr;
208  other.future_retrieved_ = false;
209  }
210 
211  ~ReusablePromise() = default;
212 
213  // no copy assign
214  ReusablePromise& operator=(const ReusablePromise&) = delete;
215 
216  // move assign
218  shared_state_ = other.shared_state_;
219  future_retrieved_ = other.future_retrieved_;
220  other.shared_state_ = nullptr;
221  other.future_retrieved_ = false;
222  return *this;
223  }
224 
225  // set value of the shared state
226  void set_value(const R& value) {
227  if (!shared_state_) {
228  throw std::future_error(std::future_errc::no_state);
229  }
230  shared_state_->set(value);
231  }
232 
233  // set value of the shared state
234  void set_value(R&& value) {
235  if (!shared_state_) {
236  throw std::future_error(std::future_errc::no_state);
237  }
238  shared_state_->set(std::move(value));
239  }
240 
241  // returns future associated with the shared state of the promise
243  if (!shared_state_) {
244  throw std::future_error(std::future_errc::no_state);
245  }
246  if (future_retrieved_) {
247  throw std::future_error(std::future_errc::future_already_retrieved);
248  }
249  future_retrieved_ = true;
250  return ReusableFuture(shared_state_);
251  }
252 
253  // swaps this future with another
254  void swap(ReusablePromise& other) noexcept {
255  std::swap(shared_state_, other.shared_state_);
256  std::swap(future_retrieved_, other.future_retrieved_);
257  }
258 
259  private:
260  std::shared_ptr<detail::ReusableSharedState<R, MutexType, ConditionVariableType>> shared_state_;
261  bool future_retrieved_ = false;
262 };
263 
264 template <typename R>
265 using ReusableFiberPromise =
267 
268 // make ReusablePromise swappable
269 template <typename R, typename MutexType, typename ConditionVariableType>
272  lhs.swap(rhs);
273 }
274 
275 } // namespace encrypto::motion
encrypto::motion::ReusableFuture::wait
void wait() const
Definition: reusable_future.h:169
encrypto::motion::detail::ReusableSharedState
Definition: reusable_future.h:43
encrypto::motion::ReusableFuture::swap
void swap(ReusableFuture &other) noexcept
Definition: reusable_future.h:163
encrypto::motion::ReusableFuture
Definition: reusable_future.h:129
encrypto::motion::detail::ReusableSharedState::ReusableSharedState
ReusableSharedState()
Definition: reusable_future.h:45
encrypto::motion::ReusablePromise::get_future
ReusableFuture< R, MutexType, ConditionVariableType > get_future()
Definition: reusable_future.h:242
encrypto::motion::ReusableFuture::ReusableFuture
ReusableFuture(ReusableFuture &&other) noexcept
Definition: reusable_future.h:138
encrypto::motion::ReusablePromise::set_value
void set_value(R &&value)
Definition: reusable_future.h:234
encrypto::motion::detail::ReusableSharedState::wait
void wait() const noexcept
Definition: reusable_future.h:79
encrypto::motion::ReusablePromise::operator=
ReusablePromise & operator=(ReusablePromise &&other) noexcept
Definition: reusable_future.h:217
encrypto::motion::detail::ReusableSharedState::reset
void reset() noexcept
Definition: reusable_future.h:69
encrypto::motion
Definition: algorithm_description.cpp:35
encrypto::motion::detail::ReusableSharedState::move
R move() noexcept
Definition: reusable_future.h:85
encrypto::motion::ReusableFuture::valid
bool valid() const noexcept
Definition: reusable_future.h:166
encrypto::motion::ReusableFuture::operator=
ReusableFuture & operator=(ReusableFuture &&other) noexcept
Definition: reusable_future.h:148
encrypto::motion::detail::ReusableSharedState::contains_value
bool contains_value() const noexcept
Definition: reusable_future.h:93
encrypto::motion::ReusablePromise::ReusablePromise
ReusablePromise(ReusablePromise &&other) noexcept
Definition: reusable_future.h:205
encrypto::motion::ReusableFuture::ReusableFuture
ReusableFuture() noexcept
Definition: reusable_future.h:132
encrypto::motion::ReusablePromise
Definition: reusable_future.h:123
encrypto::motion::ReusableFiberPromise
ReusablePromise< R, boost::fibers::mutex, boost::fibers::condition_variable > ReusableFiberPromise
Definition: reusable_future.h:266
encrypto::motion::ReusableFuture::~ReusableFuture
~ReusableFuture()=default
encrypto::motion::ReusableFuture::operator=
ReusableFuture & operator=(const ReusableFuture &)=delete
encrypto::motion::swap
void swap(ReusablePromise< R, MutexType, ConditionVariableType > &lhs, ReusablePromise< R, MutexType, ConditionVariableType > &rhs) noexcept
Definition: reusable_future.h:270
encrypto::motion::ReusablePromise::operator=
ReusablePromise & operator=(const ReusablePromise &)=delete
encrypto::motion::ReusablePromise::swap
void swap(ReusablePromise &other) noexcept
Definition: reusable_future.h:254
encrypto::motion::ReusablePromise::~ReusablePromise
~ReusablePromise()=default
encrypto::motion::detail::ReusableSharedState::~ReusableSharedState
~ReusableSharedState()
Definition: reusable_future.h:46
encrypto::motion::ReusablePromise::set_value
void set_value(const R &value)
Definition: reusable_future.h:226
encrypto::motion::ReusableFiberFuture
ReusableFuture< R, boost::fibers::mutex, boost::fibers::condition_variable > ReusableFiberFuture
Definition: reusable_future.h:188
encrypto::motion::ReusablePromise::ReusablePromise
ReusablePromise() noexcept
Definition: reusable_future.h:196
encrypto::motion::detail::ReusableSharedState::set
void set(Argument &&argument)
Definition: reusable_future.h:55
encrypto::motion::ReusableFuture::get
R get()
Definition: reusable_future.h:155