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

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

cpp: remove completed todo

  • Property mode set to 100644
File size: 11.7 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/exception.hpp>
37#include <__bits/functional/function.hpp>
38#include <__bits/functional/invoke.hpp>
39#include <__bits/refcount_obj.hpp>
40#include <__bits/thread/future_common.hpp>
41#include <__bits/thread/threading.hpp>
42#include <cerrno>
43#include <thread>
44#include <tuple>
45
46namespace std::aux
47{
48 class shared_state_base: public aux::refcount_obj
49 {
50 public:
51 shared_state_base()
52 : mutex_{}, condvar_{}, value_set_{false},
53 exception_{}, has_exception_{false}
54 {
55 threading::mutex::init(mutex_);
56 threading::condvar::init(condvar_);
57 }
58
59 void destroy() override
60 {
61 /**
62 * Note: No need to act in this case, async shared
63 * state is the object that needs to sometimes
64 * invoke its payload.
65 */
66 }
67
68 void mark_set(bool set = true) noexcept
69 {
70 value_set_ = set;
71 }
72
73 bool is_set() const noexcept
74 {
75 return value_set_;
76 }
77
78 void set_exception(exception_ptr ptr)
79 {
80 exception_ = ptr;
81 has_exception_ = true;
82 }
83
84 bool has_exception() const noexcept
85 {
86 return has_exception_;
87 }
88
89 void throw_stored_exception() const
90 {
91 // TODO: implement
92 }
93
94 /**
95 * TODO: This member function is supposed to be marked
96 * as 'const'. In such a case, however, we cannot
97 * use the underlying fibril API because these
98 * references get converted to pointers and the API
99 * does not accept e.g. 'const fibril_condvar_t*'.
100 *
101 * The same applies to the wait_for and wait_until
102 * functions.
103 */
104 virtual void wait()
105 {
106 aux::threading::mutex::lock(mutex_);
107 while (!value_set_)
108 aux::threading::condvar::wait(condvar_, mutex_);
109 aux::threading::mutex::unlock(mutex_);
110 }
111
112 template<class Rep, class Period>
113 future_status
114 wait_for(const chrono::duration<Rep, Period>& rel_time)
115 {
116 aux::threading::mutex::lock(mutex_);
117 auto res = timed_wait_(
118 aux::threading::time::convert(rel_time)
119 );
120 aux::threading::mutex::unlock(mutex_);
121
122 return res;
123 }
124
125 template<class Clock, class Duration>
126 future_status
127 wait_until(const chrono::time_point<Clock, Duration>& abs_time)
128 {
129 aux::threading::mutex::lock(mutex_);
130 auto res = timed_wait_(
131 aux::threading::time::convert(abs_time - Clock::now())
132 );
133 aux::threading::mutex::unlock(mutex_);
134
135 return res;
136 }
137
138 ~shared_state_base() override = default;
139
140 protected:
141 aux::mutex_t mutex_;
142 aux::condvar_t condvar_;
143
144 bool value_set_;
145
146 exception_ptr exception_;
147 bool has_exception_;
148
149 /**
150 * Note: wait_for and wait_until are templates and as such
151 * cannot be virtual and overriden by the deferred_ and
152 * async_ children. However, we are using aux::time_unit_t
153 * in the end anyway, so we can work around that
154 * by using the 'template method' design pattern
155 * (i.e. by providing a virtual function called by these
156 * templates and then overriding that function in the
157 * children).
158 */
159 virtual future_status timed_wait_(aux::time_unit_t time)
160 {
161 auto res = aux::threading::condvar::wait_for(
162 condvar_, mutex_, time
163 );
164
165 return res == ETIMEOUT ? future_status::timeout
166 : future_status::ready;
167 }
168 };
169
170 template<class R>
171 class shared_state: public shared_state_base
172 {
173 public:
174 shared_state()
175 : shared_state_base{}
176 { /* DUMMY BODY */ }
177
178 void set_value(const R& val, bool set)
179 {
180 /**
181 * Note: This is the 'mark ready' move described
182 * in 30.6.4 (6).
183 */
184
185 aux::threading::mutex::lock(mutex_);
186 value_ = val;
187 value_set_ = set;
188 aux::threading::mutex::unlock(mutex_);
189
190 aux::threading::condvar::broadcast(condvar_);
191 }
192
193 void set_value(R&& val, bool set = true)
194 {
195 aux::threading::mutex::lock(mutex_);
196 value_ = std::move(val);
197 value_set_ = set;
198 aux::threading::mutex::unlock(mutex_);
199
200 aux::threading::condvar::broadcast(condvar_);
201 }
202
203 R& get()
204 {
205 return value_;
206 }
207
208 protected:
209 R value_;
210 };
211
212 template<>
213 class shared_state<void>: public shared_state_base
214 {
215 public:
216 shared_state()
217 : shared_state_base{}
218 { /* DUMMY BODY */ }
219
220 void set_value()
221 {
222 value_set_ = true;
223 aux::threading::condvar::broadcast(condvar_);
224 }
225
226 void get()
227 { /* DUMMY BODY */ }
228 };
229
230 /**
231 * We could make one state for both async and
232 * deferred policies, but then we would be wasting
233 * memory and the only benefit would be the ability
234 * for additional implementation defined policies done
235 * directly in that state (as opposed to making new
236 * states for them).
237 *
238 * But since we have no plan (nor need) to make those,
239 * this approach seems to be the best one.
240 *
241 * Also note that unlike the parent class shared_state,
242 * we do not need to specialize these for void. This is because
243 * the difference in class contents are handled by the parent
244 * specialization and setting the value can be handled easily
245 * by if constexpr and checking for the equivalence of the
246 * R template parameter and void.
247 */
248
249 template<class R, class F, class... Args>
250 class async_shared_state: public shared_state<R>
251 {
252 public:
253 async_shared_state(F&& f, Args&&... args)
254 : shared_state<R>{}, thread_{}
255 {
256 thread_ = thread{
257 [=](){
258 try
259 {
260 if constexpr (!is_same_v<R, void>)
261 this->set_value(invoke(f, args...));
262 else
263 {
264 invoke(f, args...);
265 this->mark_set(true);
266 }
267 }
268 catch(...) // TODO: Any exception.
269 {
270 // TODO: Store it.
271 }
272 }
273 };
274 }
275
276 void destroy() override
277 {
278 if (!this->is_set())
279 thread_.join();
280 }
281
282 void wait() override
283 {
284 if (!this->is_set())
285 thread_.join();
286 }
287
288 ~async_shared_state() override
289 {
290 destroy();
291 }
292
293 protected:
294 future_status timed_wait_(aux::time_unit_t) override
295 {
296 // TODO: have to sleep and check
297 return future_status::timeout;
298 }
299
300 private:
301 thread thread_;
302 };
303
304 template<class R, class F, class... Args>
305 class deferred_shared_state: public shared_state<R>
306 {
307 public:
308 template<class G>
309 deferred_shared_state(G&& f, Args&&... args)
310 : shared_state<R>{}, func_{forward<F>(f)},
311 args_{forward<Args>(args)...}
312 { /* DUMMY BODY */ }
313
314 void destroy() override
315 {
316 aux::threading::mutex::lock(this->mutex_);
317 if (!this->is_set())
318 invoke_(make_index_sequence<sizeof...(Args)>{});
319 aux::threading::mutex::unlock(this->mutex_);
320 }
321
322 void wait() override
323 {
324 /**
325 * Note: Synchronization done in invoke_ -> set_value.
326 */
327 if (!this->is_set())
328 invoke_(make_index_sequence<sizeof...(Args)>{});
329 }
330
331 ~deferred_shared_state() override
332 {
333 destroy();
334 }
335
336 protected:
337 function<R(decay_t<Args>...)> func_;
338 tuple<decay_t<Args>...> args_;
339
340 template<size_t... Is>
341 void invoke_(index_sequence<Is...>)
342 {
343 try
344 {
345 if constexpr (!is_same_v<R, void>)
346 this->set_value(invoke(move(func_), get<Is>(move(args_))...));
347 else
348 {
349 invoke(move(func_), get<Is>(move(args_))...);
350 this->mark_set(true);
351 }
352 }
353 catch(...)
354 {
355 // TODO: Store it.
356 }
357 }
358
359 future_status timed_wait_(aux::time_unit_t) override
360 {
361 /**
362 * Note: Neither of the wait_ functions has any effect
363 * for deferred functions spawned by async (which
364 * are the only users of this state type).
365 */
366 return future_status::deferred;
367 }
368 };
369}
370
371#endif
Note: See TracBrowser for help on using the repository browser.