source: mainline/uspace/lib/cpp/src/__bits/test/future.cpp

Last change on this file was 46c66f8, checked in by Jaroslav Jindrak <dzejrou@…>, 6 years ago

cpp: apply requested changes

  • Property mode set to 100644
File size: 10.8 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#include <__bits/test/mock.hpp>
30#include <__bits/test/tests.hpp>
31#include <chrono>
32#include <exception>
33#include <future>
34#include <tuple>
35#include <utility>
36
37using namespace std::chrono_literals;
38
39namespace
40{
41 template<class R>
42 auto prepare()
43 {
44 auto res = std::tuple<
45 std::promise<R>, std::future<R>,
46 std::aux::shared_state<R>*
47 >{};
48 std::get<0>(res) = std::promise<R>{};
49 std::get<1>(res) = std::get<0>(res).get_future();
50 std::get<2>(res) = std::get<1>(res).__state();
51
52 return res;
53 }
54}
55
56namespace std::test
57{
58 bool future_test::run(bool report)
59 {
60 report_ = report;
61 start();
62
63 test_future();
64 test_promise();
65 test_future_promise();
66 test_async();
67 test_packaged_task();
68 test_shared_future();
69
70 return end();
71 }
72
73 const char* future_test::name()
74 {
75 return "future";
76 }
77
78 void future_test::test_future()
79 {
80 std::future<int> f1{};
81 test("default constructed invalid", !f1.valid());
82
83 std::future<int> f2{new std::aux::shared_state<int>{}};
84 test("state constructed valid", f2.valid());
85
86 f1 = std::move(f2);
87 test("move assignment source invalid", !f2.valid());
88 test("move assignment destination valid", f1.valid());
89
90 std::future<int> f3{std::move(f1)};
91 test("move construction source invalid", !f1.valid());
92 test("move construction destination valid", f3.valid());
93 }
94
95 void future_test::test_promise()
96 {
97 std::promise<int> p1{};
98 test("default constructed promise has state", p1.__state());
99
100 std::promise<int> p2{};
101 auto* s1 = p1.__state();
102 auto* s2 = p2.__state();
103 p2.swap(p1);
104 std::swap(s1, s2);
105
106 test_eq("swap switches states pt1", s1, p1.__state());
107 test_eq("swap switches states pt2", s2, p2.__state());
108
109 std::promise<int> p3{std::move(p1)};
110 test_eq("move construction state moved", s1, p3.__state());
111 test_eq("move construction source empty", p1.__state(), nullptr);
112
113 p1 = std::move(p3);
114 test_eq("move assignment state move", s1, p1.__state());
115 test_eq("move assignment source empty", p3.__state(), nullptr);
116
117 p1.set_value(42);
118 test("set_value marks state as ready", s1->is_set());
119 test_eq("set_value sets value", s1->get(), 42);
120 }
121
122 void future_test::test_future_promise()
123 {
124 /**
125 * Note: As we currently have no exception
126 * propagation support, we do not test
127 * exceptions here. However, the logic there
128 * is basically identical to that of the value
129 * setting.
130 */
131 auto [p1, f1, s1] = prepare<int>();
132 test_eq("refcount in basic case", s1->refs(), 2);
133
134 p1.set_value(1);
135 test("simple case valid", f1.valid());
136 test_eq("simple case get", f1.get(), 1);
137
138 auto [p2, f2, s2] = prepare<int>();
139 std::thread t2{
140 [&p2](){
141 std::this_thread::sleep_for(20ms);
142 p2.set_value(42);
143 }
144 };
145
146 test_eq("parallel get waits and has correct value", f2.get(), 42);
147
148 auto [p3, f3, s3] = prepare<int>();
149 std::thread t3{
150 [&p3](){
151 std::this_thread::sleep_for(20ms);
152 p3.set_value(42);
153 }
154 };
155
156 f3.wait();
157 test("after wait value is set", s3->is_set());
158 test_eq("after wait value is correct", s3->get(), 42);
159
160 auto [p4, f4, s4] = prepare<int>();
161 std::thread t4{
162 [&p4](){
163 /* p4.set_value_at_thread_exit(42); */
164 }
165 };
166 std::this_thread::sleep_for(10ms); // Let the value be set inside state.
167
168 /* test("shared state marked as ready at thread exit", s4->is_set()); */
169 /* test_eq("value set inside state while in thread", s4->get(), 42); */
170 /* test_eq("value set at thread exit", f4.get(), 42); */
171
172 mock::clear();
173 std::aux::shared_state<std::test::mock>* s5{};
174 {
175 std::promise<std::test::mock> p5{};
176 s5 = p5.__state();
177 test_eq("refcount with just promise", s5->refs(), 1);
178 {
179 auto f5 = p5.get_future();
180 test_eq("refcount after creating future", s5->refs(), 2);
181 }
182 test_eq("refcount after future is destroyed", s5->refs(), 1);
183 test_eq("state not destroyed with future", mock::destructor_calls, 0U);
184 }
185 test_eq("state destroyed with promise", mock::destructor_calls, 1U);
186
187 mock::clear();
188 {
189 std::aux::shared_state<std::test::mock>* s6{};
190 std::future<std::test::mock> f6{};
191 {
192 std::promise<std::test::mock> p6{};
193 s6 = p6.__state();
194 {
195 f6 = p6.get_future();
196 test_eq("move construction only increments refcount once", s6->refs(), 2);
197 }
198 }
199 test_eq("refcount after promise is destroyed", s6->refs(), 1);
200 test_eq("state not destroyed with promise", mock::destructor_calls, 0U);
201 }
202 test_eq("state destroyed with future", mock::destructor_calls, 1U);
203
204 auto [p7, f7, s7] = prepare<int>();
205 auto res7 = f7.wait_for(5ms);
206 test_eq("wait_for timeout", res7, std::future_status::timeout);
207
208 res7 = f7.wait_until(std::chrono::system_clock::now() + 5ms);
209 test_eq("wait_until timeout", res7, std::future_status::timeout);
210
211 std::thread t7{
212 [&p7](){
213 std::this_thread::sleep_for(5ms);
214 p7.set_value(42);
215 }
216 };
217 res7 = f7.wait_for(10ms);
218 test_eq("wait_for ready", res7, std::future_status::ready);
219
220 auto [p8, f8, s8] = prepare<int>();
221 std::thread t8{
222 [&p8](){
223 std::this_thread::sleep_for(5ms);
224 p8.set_value(42);
225 }
226 };
227
228 auto res8 = f8.wait_until(std::chrono::system_clock::now() + 10ms);
229 test_eq("wait_until ready", res8, std::future_status::ready);
230
231 int x{};
232 std::promise<int&> p9{};
233 std::future<int&> f9 = p9.get_future();
234 p9.set_value(x);
235 int& y = f9.get();
236
237 test_eq("reference equal to original", x, y);
238
239 ++x;
240 test_eq("equal after modifying original", x, y);
241
242 ++y;
243 test_eq("equal after modifying reference", x, y);
244 }
245
246 void future_test::test_async()
247 {
248 auto res1 = std::async(
249 [](){
250 return 42;
251 }
252 );
253 test_eq("ret async default policy", res1.get(), 42);
254
255 auto res2 = std::async(
256 std::launch::deferred, [](){
257 return 42;
258 }
259 );
260 test_eq("ret async deferred policy", res2.get(), 42);
261
262 auto res3 = std::async(
263 std::launch::async, [](){
264 return 42;
265 }
266 );
267 test_eq("ret async async policy", res3.get(), 42);
268
269 int x{};
270 auto res4 = std::async(
271 [&x](){
272 x = 42;
273 }
274 );
275
276 res4.get();
277 test_eq("void async", x, 42);
278 }
279
280 void future_test::test_packaged_task()
281 {
282 std::packaged_task<int(int)> pt1{};
283 test("default constructed packaged_task not valid", !pt1.valid());
284
285 pt1 = std::packaged_task<int(int)>{
286 [](int x){
287 return x + 1;
288 }
289 };
290 test("packaged_task default constructed and move assigned valid", pt1.valid());
291
292 auto f1 = pt1.get_future();
293 test("future from valid packaged_task valid", f1.valid());
294
295 pt1(10);
296 test_eq("result stored in future correct", f1.get(), 11);
297
298 std::packaged_task<int()> pt2{
299 [](){
300 return 42;
301 }
302 };
303 auto f2 = pt2.get_future();
304 pt2();
305 test_eq("no argument packaged_task return value correct", f2.get(), 42);
306
307 pt2.reset();
308 test_eq("reset causes refcount decrement", f2.__state()->refs(), 1);
309
310 auto f3 = pt2.get_future();
311 pt2();
312 test_eq("invocation after reset returns correct value", f3.get(), 42);
313 test("reset recreates state", (f2.__state() != f3.__state()));
314 }
315
316 void future_test::test_shared_future()
317 {
318 auto [p1, f1, s1] = prepare<int>();
319 auto sf1 = f1.share();
320
321 test("future invalid after share", !f1.valid());
322 test_eq("shared state moved on share", sf1.__state(), s1);
323 test_eq("no refcount increment on share", s1->refs(), 2);
324
325 {
326 auto sf2 = sf1;
327 test_eq("refcount increment on copy", s1->refs(), 3);
328 test_eq("shared state shared between copies", sf1.__state(), sf2.__state());
329 }
330 test_eq("refcount decrement when copy gets destroyed", s1->refs(), 2);
331
332 auto sf2 = sf1;
333 int res1{}, res2{};
334 std::thread t1{
335 [&](){
336 res1 = sf1.get();
337 }
338 };
339 std::thread t2{
340 [&](){
341 res2 = sf2.get();
342 }
343 };
344
345 std::this_thread::sleep_for(20ms);
346 p1.set_value(42);
347 std::this_thread::sleep_for(20ms);
348 test_eq("first result correct", res1, 42);
349 test_eq("second result correct", res2, 42);
350 }
351}
Note: See TracBrowser for help on using the repository browser.