Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[simix] Avoid useless mutex increment
[simgrid.git] / include / xbt / future.hpp
1 /* Copyright (c) 2015-2016. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #ifndef XBT_FUTURE_HPP
8 #define XBT_FUTURE_HPP
9
10 #include <cstddef>
11
12 #include <utility>
13 #include <exception>
14 #include <stdexcept>
15
16 #include <type_traits>
17
18
19
20 namespace simgrid {
21 namespace xbt {
22
23 /** A value or an exception
24  *
25  *  The API is similar to the one of future and promise.
26  **/
27 template<class T>
28 class Result {
29   enum class ResultStatus {
30     invalid,
31     value,
32     exception,
33   };
34 public:
35   Result() {}
36   ~Result() { this->reset(); }
37
38   // Copy (if T is copyable) and move:
39   Result(Result const& that)
40   {
41     (*this) = that;
42   }
43   Result& operator=(Result const& that)
44   {
45     this->reset();
46     switch (that.status_) {
47       case ResultStatus::invalid:
48         break;
49       case ResultStatus::valid:
50         new (&value_) T(that.value);
51         break;
52       case ResultStatus::exception:
53         new (&exception_) T(that.exception);
54         break;
55     }
56     return *this;
57   }
58   Result(Result&& that)
59   {
60     *this = std::move(that);
61   }
62   Result& operator=(Result&& that)
63   {
64     this->reset();
65     switch (that.status_) {
66       case ResultStatus::invalid:
67         break;
68       case ResultStatus::valid:
69         new (&value_) T(std::move(that.value));
70         that.value.~T();
71         break;
72       case ResultStatus::exception:
73         new (&exception_) T(std::move(that.exception));
74         that.exception.~exception_ptr();
75         break;
76     }
77     that.status_ = ResultStatus::invalid;
78     return *this;
79   }
80
81   bool is_valid() const
82   {
83     return status_ != ResultStatus::invalid;
84   }
85   void reset()
86   {
87     switch (status_) {
88       case ResultStatus::invalid:
89         break;
90       case ResultStatus::value:
91         value_.~T();
92         break;
93       case ResultStatus::exception:
94         exception_.~exception_ptr();
95         break;
96     }
97     status_ = ResultStatus::invalid;
98   }
99   void set_exception(std::exception_ptr e)
100   {
101     this->reset();
102     new (&exception_) std::exception_ptr(std::move(e));
103     status_ = ResultStatus::exception;
104   }
105   void set_value(T&& value)
106   {
107     this->reset();
108     new (&value_) T(std::move(value));
109     status_ = ResultStatus::value;
110   }
111   void set_value(T const& value)
112   {
113     this->reset();
114     new (&value_) T(value);
115     status_ = ResultStatus::value;
116   }
117
118   /** Extract the value from the future
119    *
120    *  After this the value is invalid.
121    **/
122   T get()
123   {
124     switch (status_) {
125       case ResultStatus::invalid:
126       default:
127         throw std::logic_error("Invalid result");
128       case ResultStatus::value: {
129         T value = std::move(value_);
130         value_.~T();
131         status_ = ResultStatus::invalid;
132         return std::move(value);
133       }
134       case ResultStatus::exception: {
135         std::exception_ptr exception = std::move(exception_);
136         exception_.~exception_ptr();
137         status_ = ResultStatus::invalid;
138         std::rethrow_exception(std::move(exception));
139         break;
140       }
141     }
142   }
143 private:
144   ResultStatus status_ = ResultStatus::invalid;
145   union {
146     T value_;
147     std::exception_ptr exception_;
148   };
149 };
150
151 template<>
152 class Result<void> : public Result<std::nullptr_t>
153 {
154 public:
155   void set_value()
156   {
157     Result<std::nullptr_t>::set_value(nullptr);
158   }
159   void get()
160   {
161     Result<std::nullptr_t>::get();
162   }
163 };
164
165 template<class T>
166 class Result<T&> : public Result<std::reference_wrapper<T>>
167 {
168 public:
169   void set_value(T& value)
170   {
171     Result<std::reference_wrapper<T>>::set_value(std::ref(value));
172   }
173   T& get()
174   {
175     return Result<std::reference_wrapper<T>>::get();
176   }
177 };
178
179 /** Execute some code and set a promise or result accordingly
180  *
181  *  Roughly this does:
182  *
183  *  <pre>
184  *  promise.set_value(code());
185  *  </pre>
186  *
187  *  but it takes care of exceptions and works with void.
188  *
189  *  We might need this when working with generic code because
190  *  the trivial implementation does not work with `void` (before C++1z).
191  *
192  *  @param    code  What we want to do
193  *  @param  promise Where to want to store the result
194  */
195 template<class R, class F>
196 auto fulfillPromise(R& promise, F&& code)
197 -> decltype(promise.set_value(code()))
198 {
199   try {
200     promise.set_value(std::forward<F>(code)());
201   }
202   catch(...) {
203     promise.set_exception(std::current_exception());
204   }
205 }
206
207 template<class P, class F>
208 auto fulfillPromise(P& promise, F&& code)
209 -> decltype(promise.set_value())
210 {
211   try {
212     std::forward<F>(code)();
213     promise.set_value();
214   }
215   catch(...) {
216     promise.set_exception(std::current_exception());
217   }
218 }
219
220 /** Set a promise/result from a future/resul
221  *
222  *  Roughly this does:
223  *
224  *  <pre>promise.set_value(future);</pre>
225  *
226  *  but it takes care of exceptions and works with `void`.
227  *
228  *  We might need this when working with generic code because
229  *  the trivial implementation does not work with `void` (before C++1z).
230  *
231  *  @param promise output (a valid future or a result)
232  *  @param future  input (a ready/waitable future or a valid result)
233  */
234 template<class P, class F> inline
235 void setPromise(P& promise, F&& future)
236 {
237   fulfillPromise(promise, [&]{ return std::forward<F>(future).get(); });
238 }
239
240 }
241 }
242
243 #endif