Logo AND Algorithmique Numérique Distribuée

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