source: mainline/uspace/lib/cpp/include/__bits/thread/shared_state.hpp@ a95e75e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a95e75e was a95e75e, checked in by Jaroslav Jindrak <dzejrou@…>, 6 years ago

cpp: split future.hpp into subheaders for each type

  • Property mode set to 100644
File size: 10.1 KB
Line 
1/*
2 * Copyright (c) 2019 Jaroslav Jindrak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef LIBCPP_BITS_THREAD_SHARED_STATE
30#define LIBCPP_BITS_THREAD_SHARED_STATE
31
32/**
33 * 30.6.4, shared state:
34 */
35
36#include <__bits/functional/function.hpp>
37#include <__bits/functional/invoke.hpp>
38#include <__bits/refcount_obj.hpp>
39#include <__bits/thread/threading.hpp>
40
41namespace std::aux
42{
43 template<class R>
44 class shared_state: public aux::refcount_obj
45 {
46 public:
47 shared_state()
48 : mutex_{}, condvar_{}, value_{}, value_set_{false},
49 exception_{}, has_exception_{false}
50 {
51 threading::mutex::init(mutex_);
52 threading::condvar::init(condvar_);
53 }
54
55 void destroy() override
56 {
57 /**
58 * Note: No need to act in this case, async shared
59 * state is the object that needs to sometimes
60 * invoke its payload.
61 */
62 }
63
64 void set_value(const R& val, bool set)
65 {
66 /**
67 * Note: This is the 'mark ready' move described
68 * in 30.6.4 (6).
69 */
70
71 aux::threading::mutex::lock(mutex_);
72 value_ = val;
73 value_set_ = set;
74 aux::threading::mutex::unlock(mutex_);
75
76 aux::threading::condvar::broadcast(condvar_);
77 }
78
79 void set_value(R&& val, bool set = true)
80 {
81 aux::threading::mutex::lock(mutex_);
82 value_ = std::move(val);
83 value_set_ = set;
84 aux::threading::mutex::unlock(mutex_);
85
86 aux::threading::condvar::broadcast(condvar_);
87 }
88
89 void mark_set(bool set = true) noexcept
90 {
91 value_set_ = set;
92 }
93
94 bool is_set() const noexcept
95 {
96 return value_set_;
97 }
98
99 R& get()
100 {
101 return value_;
102 }
103
104 void set_exception(exception_ptr ptr)
105 {
106 exception_ = ptr;
107 has_exception_ = true;
108 }
109
110 bool has_exception() const noexcept
111 {
112 return has_exception_;
113 }
114
115 void throw_stored_exception() const
116 {
117 // TODO: implement
118 }
119
120 /**
121 * TODO: This member function is supposed to be marked
122 * as 'const'. In such a case, however, we cannot
123 * use the underlying fibril API because these
124 * references get converted to pointers and the API
125 * does not accept e.g. 'const fibril_condvar_t*'.
126 *
127 * The same applies to the wait_for and wait_until
128 * functions.
129 */
130 virtual void wait()
131 {
132 aux::threading::mutex::lock(mutex_);
133 while (!value_set_)
134 aux::threading::condvar::wait(condvar_, mutex_);
135 aux::threading::mutex::unlock(mutex_);
136 }
137
138 template<class Rep, class Period>
139 virtual future_status
140 wait_for(const chrono::duration<Rep, Period>& rel_time)
141 {
142 aux::threading::mutex::lock(mutex_);
143 auto res = aux::threading::condvar::wait_for(
144 condvar_, mutex_,
145 aux::threading::time::convert(rel_time)
146 );
147 aux::threading::mutex::unlock(mutex_);
148
149 return res == ETIMEOUT ? future_status::timeout
150 : future_status::ready;
151 }
152
153 template<class Clock, class Duration>
154 virtual future_status
155 wait_until(const chrono::time_point<Clock, Duration>& abs_time)
156 {
157 aux::threading::mutex::lock(mutex_);
158 auto res = aux::threading::condvar::wait_for(
159 condvar_, mutex_,
160 aux::threading::time::convert(abs_time - Clock::now())
161 );
162 aux::threading::mutex::unlock(mutex_);
163
164 return res == ETIMEOUT ? future_status::timeout
165 : future_status::ready;
166 }
167
168 ~shared_state() override = default;
169
170 private:
171 aux::mutex_t mutex_;
172 aux::condvar_t condvar_;
173
174 R value_;
175 bool value_set_;
176
177 exception_ptr exception_;
178 bool has_exception_;
179 };
180
181 /**
182 * We could make one state for both async and
183 * deferred policies, but then we would be wasting
184 * memory and the only benefit would be the ability
185 * for additional implementation defined policies done
186 * directly in that state (as opposed to making new
187 * states for them).
188 *
189 * But since we have no plan (nor need) to make those,
190 * this approach seems to be the best one.
191 */
192
193 template<class R, class F, class... Args>
194 class async_shared_state: public shared_state<R>
195 {
196 public:
197 async_shared_state(F&& f, Args&&... args)
198 : shared_state<R>{}, thread_{}
199 {
200 thread_ = thread{
201 [=](){
202 try
203 {
204 this->set_value(invoke(f, args...));
205 }
206 catch(...) // TODO: Any exception.
207 {
208 // TODO: Store it.
209 }
210 }
211 };
212 }
213
214 void destroy() override
215 {
216 if (!this->is_set())
217 thread_.join();
218 }
219
220 void wait() override
221 {
222 if (!this->is_set())
223 thread_.join();
224 }
225
226 template<class Rep, class Period>
227 future_status
228 wait_for(const chrono::duration<Rep, Period>&) override
229 {
230 // TODO: have to sleep and check
231 return future_status::ready;
232 }
233
234 template<class Clock, class Duration>
235 future_status
236 wait_until(const chrono::time_point<Clock, Duration>&) override
237 {
238 // TODO: have to sleep and check
239 return future_status::ready;
240 }
241
242 ~async_shared_state() override
243 {
244 destroy();
245 }
246
247 private:
248 thread thread_;
249 };
250
251 template<class R, class F, class... Args>
252 class deferred_shared_state: public shared_state<R>
253 {
254 public:
255 template<class G>
256 deferred_shared_state(G&& f, Args&&... args)
257 : shared_state<R>{}, func_{forward<F>(f)},
258 args_{forward<Args>(args)...}
259 { /* DUMMY BODY */ }
260
261 void destroy() override
262 {
263 aux::threading::mutex::lock(mutex_);
264 if (!this->is_set())
265 invoke_(make_index_sequence<sizeof...(Args)>{});
266 aux::threading::mutex::unlock(mutex_);
267 }
268
269 void wait() override
270 {
271 aux::threading::mutex::lock(mutex_);
272 if (!this->is_set())
273 invoke_(make_index_sequence<sizeof...(Args)>{});
274 aux::threading::mutex::unlock(mutex_);
275 }
276
277 template<class Rep, class Period>
278 future_status
279 wait_for(const chrono::duration<Rep, Period>&) override
280 {
281 /**
282 * Note: Neither of the wait_ functions has any effect
283 * for deferred functions spawned by async (which
284 * are the only users of this state type).
285 */
286 return future_status::deferred;
287 }
288
289 template<class Clock, class Duration>
290 future_status
291 wait_until(const chrono::time_point<Clock, Duration>&) override
292 {
293 return future_status::deferred;
294 }
295
296 ~deferred_shared_state() override
297 {
298 destroy();
299 }
300
301 private:
302 function<R(decay_t<Args>...)> func_;
303 tuple<decay_t<Args>...> args_;
304
305 template<size_t... Is>
306 void invoke_(index_sequence<Is...>)
307 {
308 try
309 {
310 this->set_value(invoke(move(func_), get<Is>(move(args_))...));
311 }
312 catch(...)
313 {
314 // TODO: Store it.
315 }
316 }
317 };
318}
319
320#endif
Note: See TracBrowser for help on using the repository browser.