source: mainline/uspace/lib/cpp/include/__bits/thread/promise.hpp@ 396b234

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

cpp: add missing implementation of the shared state abandoning procedure to promise

  • Property mode set to 100644
File size: 9.4 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_PROMISE
30#define LIBCPP_BITS_THREAD_PROMISE
31
32#include <__bits/thread/future.hpp>
33#include <__bits/thread/shared_state.hpp>
34#include <__bits/aux.hpp>
35#include <utility>
36
37namespace std
38{
39 /**
40 * 30.6.5, class template promise:
41 */
42
43 namespace aux
44 {
45 template<class R>
46 class promise_base
47 {
48 public:
49 promise_base()
50 : state_{new aux::shared_state<R>{}}
51 { /* DUMMY BODY */ }
52
53 template<class Allocator>
54 promise_base(allocator_arg_t, const Allocator& a)
55 : promise_base{}
56 {
57 // TODO: Use the allocator.
58 }
59
60 promise_base(promise_base&& rhs) noexcept
61 : state_{}
62 {
63 state_ = rhs.state_;
64 rhs.state_ = nullptr;
65 }
66
67 promise_base(const promise_base&) = delete;
68
69 virtual ~promise_base()
70 {
71 this->abandon_state_();
72 }
73
74 promise_base& operator=(promise_base&& rhs) noexcept
75 {
76 abandon_state_();
77 promise_base{std::move(rhs)}.swap(*this);
78
79 return *this;
80 }
81
82 promise_base& operator=(const promise_base&) = delete;
83
84 void swap(promise_base& other) noexcept
85 {
86 std::swap(state_, other.state_);
87 }
88
89 future<R> get_future()
90 {
91 return future<R>{state_};
92 }
93
94 void set_exception(exception_ptr ptr)
95 {
96 assert(state_);
97
98 state_->set_exception(ptr);
99 }
100
101 void set_exception_at_thread_exit(exception_ptr)
102 {
103 // TODO: No exception handling, no-op at this time.
104 }
105
106 protected:
107 void abandon_state_()
108 {
109 if (!state_)
110 return;
111
112 /**
113 * Note: This is the 'abandon' move described in
114 * 30.6.4 (7).
115 * 1) If state is not ready:
116 * a) Store exception of type future_error with
117 * error condition broken_promise.
118 * b) Mark state as ready.
119 * 2) Release the state.
120 */
121
122 if (!state_->is_set())
123 {
124 // TODO: Store future_error.
125 state_->mark_set(true);
126 }
127
128 if (state_->decrement())
129 {
130 state_->destroy();
131 delete state_;
132 state_ = nullptr;
133 }
134 }
135
136 aux::shared_state<R>* state_;
137 };
138 }
139
140 template<class R>
141 class promise: public aux::promise_base<R>
142 {
143 public:
144 promise()
145 : aux::promise_base<R>{}
146 { /* DUMMY BODY */ }
147
148 template<class Allocator>
149 promise(allocator_arg_t, const Allocator& a)
150 : aux::promise_base<R>{}
151 {
152 // TODO: Use the allocator.
153 }
154
155 promise(promise&& rhs) noexcept
156 : aux::promise_base<R>{move(rhs)}
157 { /* DUMMY BODY */ }
158
159 promise(const promise&) = delete;
160
161 ~promise() override = default;
162
163 promise& operator=(promise&& rhs) noexcept = default;
164
165 promise& operator=(const promise&) = delete;
166
167 void set_value(const R& val)
168 {
169 if (!this->state_)
170 throw future_error{make_error_code(future_errc::no_state)};
171 if (this->state_->is_set())
172 {
173 throw future_error{
174 make_error_code(future_errc::promise_already_satisfied)
175 };
176 }
177
178 this->state_->set_value(val, true);
179 }
180
181 void set_value(R&& val)
182 {
183 if (!this->state_)
184 throw future_error{make_error_code(future_errc::no_state)};
185 if (this->state_->is_set())
186 {
187 throw future_error{
188 make_error_code(future_errc::promise_already_satisfied)
189 };
190 }
191
192 this->state_->set_value(std::forward<R>(val), true);
193 }
194
195 void set_value_at_thread_exit(const R& val)
196 {
197 if (!this->state_)
198 throw future_error{make_error_code(future_errc::no_state)};
199 if (this->state_->is_set())
200 {
201 throw future_error{
202 make_error_code(future_errc::promise_already_satisfied)
203 };
204 }
205
206 this->state_->set_value(val, false);
207 // TODO: schedule it to be set as ready when thread exits
208 }
209
210 void set_value_at_thread_exit(R&& val)
211 {
212 if (!this->state_)
213 throw future_error{make_error_code(future_errc::no_state)};
214 if (this->state_->is_set())
215 {
216 throw future_error{
217 make_error_code(future_errc::promise_already_satisfied)
218 };
219 }
220
221 this->state_->set_value(std::forward<R>(val), false);
222 // TODO: schedule it to be set as ready when thread exits
223 }
224 };
225
226 template<class R>
227 class promise<R&>: public aux::promise_base<R>
228 { // TODO: I'm afraid we will need aux::shared_state<R&> specialization for this :/
229 public:
230 promise()
231 : aux::promise_base<R&>{}
232 { /* DUMMY BODY */ }
233
234 template<class Allocator>
235 promise(allocator_arg_t, const Allocator& a)
236 : aux::promise_base<R&>{}
237 {
238 // TODO: Use the allocator.
239 }
240
241 promise(promise&& rhs) noexcept
242 : aux::promise_base<R&>{move(rhs)}
243 { /* DUMMY BODY */ }
244
245 promise(const promise&) = delete;
246
247 ~promise() override = default;
248
249 promise& operator=(promise&& rhs) noexcept = default;
250
251 promise& operator=(const promise&) = delete;
252 };
253
254 template<>
255 class promise<void>: public aux::promise_base<void>
256 {
257 public:
258 promise()
259 : aux::promise_base<void>{}
260 { /* DUMMY BODY */ }
261
262 template<class Allocator>
263 promise(allocator_arg_t, const Allocator& a)
264 : aux::promise_base<void>{}
265 {
266 // TODO: Use the allocator.
267 }
268
269 promise(promise&& rhs) noexcept
270 : aux::promise_base<void>{move(rhs)}
271 { /* DUMMY BODY */ }
272
273 promise(const promise&) = delete;
274
275 ~promise() override = default;
276
277 promise& operator=(promise&& rhs) noexcept = default;
278
279 promise& operator=(const promise&) = delete;
280
281 void set_value()
282 {
283 if (!this->state_)
284 throw future_error{make_error_code(future_errc::no_state)};
285 if (this->state_->is_set())
286 {
287 throw future_error{
288 make_error_code(future_errc::promise_already_satisfied)
289 };
290 }
291
292 this->state_->set_value();
293 }
294 };
295
296 template<class R>
297 void swap(promise<R>& lhs, promise<R>& rhs) noexcept
298 {
299 lhs.swap(rhs);
300 }
301
302 template<class R, class Alloc>
303 struct uses_allocator<promise<R>, Alloc>: true_type
304 { /* DUMMY BODY */ };
305}
306
307#endif
Note: See TracBrowser for help on using the repository browser.