1 // Copyright (c) 2012 Bryce Adelstein-Lelbach
2 // Copyright (c) 2014 Agustin Berge
3 // Copyright (c) 2017 Denis Blank
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7
8 #include <hpx/hpx_init.hpp>
9 #include <hpx/include/async.hpp>
10 #include <hpx/include/threads.hpp>
11 #include <hpx/lcos/future.hpp>
12 #include <hpx/util/lightweight_test.hpp>
13 #include <hpx/util/tuple.hpp>
14 #include <hpx/util/unwrap.hpp>
15
16 #include <array>
17 #include <atomic>
18 #include <cstddef>
19 #include <numeric>
20 #include <string>
21 #include <type_traits>
22 #include <utility>
23 #include <vector>
24
25 using hpx::util::tuple;
26 using hpx::util::get;
27 using hpx::util::make_tuple;
28 using hpx::util::unwrap;
29 using hpx::util::unwrap_n;
30 using hpx::util::unwrap_all;
31 using hpx::util::unwrapping;
32 using hpx::util::unwrapping_n;
33 using hpx::util::unwrapping_all;
34
35 /// Since the mapping functionality is provided by the `map_pack`
36 /// API, we only need to test here for the specific behaviour of `unwrap`
37 /// and its variations.
38 /// All the test functions are instantiated for testing against future
39 /// and shared_future (which differ in their return type -> `T` vs `T const&`).
40
41 template <template <typename> class FutureType, typename FutureProvider>
test_unwrap(FutureProvider && futurize)42 void test_unwrap(FutureProvider&& futurize)
43 {
44 // Single values are unwrapped
45 {
46 int res = unwrap(futurize(0xDD));
47 HPX_TEST_EQ((res), (0xDD));
48 }
49
50 // Futures with tuples may be unwrapped
51 {
52 tuple<int, int> res = unwrap(futurize(make_tuple(0xDD, 0xDF)));
53 HPX_TEST((res == make_tuple(0xDD, 0xDF)));
54 }
55
56 // The value of multiple futures is returned inside a tuple
57 {
58 tuple<int, int> res = unwrap(futurize(0xDD), futurize(0xDF));
59 HPX_TEST((res == make_tuple(0xDD, 0xDF)));
60 }
61 }
62
63 template <template <typename> class FutureType, typename FutureProvider>
test_unwrap_n(FutureProvider && futurize)64 void test_unwrap_n(FutureProvider&& futurize)
65 {
66 // Single values are unwrapped
67 {
68 int res = unwrap_n<2>(futurize(futurize(0xDD)));
69 HPX_TEST_EQ((res), (0xDD));
70 }
71
72 // Futures with tuples may be unwrapped
73 {
74 tuple<int, int> res =
75 unwrap_n<2>(futurize(futurize(make_tuple(0xDD, 0xDF))));
76 HPX_TEST((res == make_tuple(0xDD, 0xDF)));
77 }
78
79 // The value of multiple futures is returned inside a tuple
80 {
81 tuple<int, int> res =
82 unwrap_n<2>(futurize(futurize(0xDD)), futurize(futurize(0xDF)));
83 HPX_TEST((res == make_tuple(0xDD, 0xDF)));
84 }
85
86 // Futures are not unwrapped beyond the given depth
87 {
88 FutureType<int> res =
89 unwrap_n<3>(futurize(futurize(futurize(futurize(0xDD)))));
90 HPX_TEST_EQ((res.get()), (0xDD));
91 }
92 }
93
94 template <template <typename> class FutureType, typename FutureProvider>
test_unwrap_all(FutureProvider && futurize)95 void test_unwrap_all(FutureProvider&& futurize)
96 {
97 // Single values are unwrapped
98 {
99 int res =
100 unwrap_all(futurize(futurize(futurize(futurize(futurize(0xDD))))));
101 HPX_TEST_EQ((res), (0xDD));
102 }
103
104 // Futures with tuples may be unwrapped
105 {
106 tuple<int, int> res = unwrap_all(futurize(
107 futurize(futurize(futurize(make_tuple(futurize(futurize(0xDD)),
108 futurize(futurize(futurize(0xDF)))))))));
109 HPX_TEST((res == make_tuple(0xDD, 0xDF)));
110 }
111
112 // The value of multiple futures is returned inside a tuple
113 {
114 tuple<int, int> res = unwrap_all(
115 futurize(futurize(futurize(futurize(0xDD)))), futurize(0xDF));
116 HPX_TEST((res == make_tuple(0xDD, 0xDF)));
117 }
118 }
119
120 template <template <typename> class FutureType, typename FutureProvider>
test_unwrapping(FutureProvider && futurize)121 void test_unwrapping(FutureProvider&& futurize)
122 {
123 // One argument is passed without a tuple unwrap
124 {
125 auto unwrapper = unwrapping([](int a) { return a; });
126
127 int res = unwrapper(futurize(3));
128
129 HPX_TEST_EQ((res), (3));
130 }
131
132 /// Don't unpack single tuples which were passed to the functional unwrap
133 {
134 auto unwrapper = unwrapping([](tuple<int, int> arg) {
135 // ...
136 return get<0>(arg) + get<1>(arg);
137 });
138
139 int res = unwrapper(futurize(make_tuple(1, 2)));
140
141 HPX_TEST_EQ((res), (3));
142 }
143
144 // Multiple arguments are spread across the callable
145 {
146 auto unwrapper = unwrapping([](int a, int b) { return a + b; });
147
148 int res = unwrapper(futurize(1), futurize(2));
149
150 HPX_TEST_EQ((res), (3));
151 }
152 }
153
154 /// A callable object which helps us to test deferred unwrapping like the
155 /// the immediate unwrap, because we materialize the arguments back.
156 struct back_materializer
157 {
operator ()back_materializer158 void operator()() const
159 {
160 }
161
162 template <typename First>
operator ()back_materializer163 First operator()(First&& first) const
164 {
165 return std::forward<First>(first);
166 }
167
168 template <typename First, typename Second, typename... Rest>
operator ()back_materializer169 tuple<First, Second, Rest...> operator()(First&& first,
170 Second&& second,
171 Rest&&... rest) const
172 {
173 return tuple<First, Second, Rest...>{std::forward<First>(first),
174 std::forward<Second>(second), std::forward<Rest>(rest)...};
175 }
176 };
177
178 template <template <typename> class FutureType, typename FutureProvider>
test_unwrapping_n(FutureProvider && futurize)179 void test_unwrapping_n(FutureProvider&& futurize)
180 {
181 // Single values are unwrapped
182 {
183 int res =
184 unwrapping_n<2>(back_materializer{})(futurize(futurize(0xDD)));
185 HPX_TEST_EQ((res), (0xDD));
186 }
187
188 // Futures with tuples may be unwrapped
189 {
190 tuple<int, int> res = unwrapping_n<2>(back_materializer{})(
191 futurize(futurize(make_tuple(0xDD, 0xDF))));
192 HPX_TEST((res == make_tuple(0xDD, 0xDF)));
193 }
194
195 // The value of multiple futures is returned inside a tuple
196 {
197 tuple<int, int> res = unwrapping_n<2>(back_materializer{})(
198 futurize(futurize(0xDD)), futurize(futurize(0xDF)));
199 HPX_TEST((res == make_tuple(0xDD, 0xDF)));
200 }
201
202 // Futures are not unwrapped beyond the given depth
203 {
204 FutureType<int> res = unwrapping_n<3>(back_materializer{})(
205 futurize(futurize(futurize(futurize(0xDD)))));
206 HPX_TEST_EQ((res.get()), (0xDD));
207 }
208 }
209
210 template <template <typename> class FutureType, typename FutureProvider>
test_unwrapping_all(FutureProvider && futurize)211 void test_unwrapping_all(FutureProvider&& futurize)
212 {
213 // Single values are unwrapped
214 {
215 int res = unwrapping_all(back_materializer{})(
216 futurize(futurize(futurize(futurize(futurize(0xDD))))));
217 HPX_TEST_EQ((res), (0xDD));
218 }
219
220 // Futures with tuples may be unwrapped
221 {
222 tuple<int, int> res = unwrapping_all(
223 back_materializer{})(futurize(futurize(futurize(futurize(make_tuple(
224 futurize(futurize(0xDD)), futurize(futurize(futurize(0xDF)))))))));
225 HPX_TEST((res == make_tuple(0xDD, 0xDF)));
226 }
227
228 // The value of multiple futures is returned inside a tuple
229 {
230 tuple<int, int> res = unwrapping_all(back_materializer{})(
231 futurize(futurize(futurize(futurize(0xDD)))), futurize(0xDF));
232 HPX_TEST((res == make_tuple(0xDD, 0xDF)));
233 }
234 }
235
236 /// This section declare some unit tests which ensure that issues
237 /// that occured while developing the implementation were fixed.
238 template <template <typename> class FutureType, typename FutureProvider>
test_development_regressions(FutureProvider && futurize)239 void test_development_regressions(FutureProvider&& futurize)
240 {
241 // A regression originally taken from the unwrapped tests
242 {
243 auto increment = static_cast<int (*)(int)>([](int c) -> int {
244 // ...
245 return c + 1;
246 });
247
248 FutureType<int> future = futurize(42);
249 HPX_TEST_EQ(unwrapping(increment)(future), 42 + 1);
250 }
251
252 // A future is mapped to its value
253 {
254 std::vector<FutureType<int>> f;
255 std::vector<int> res = unwrap(f);
256
257 HPX_TEST((res.empty()));
258 }
259
260 // A single void future is mapped empty
261 {
262 FutureType<void> f = futurize();
263 using Result = decltype(unwrap(f));
264 static_assert(std::is_void<Result>::value, "Failed...");
265 }
266
267 // Multiple void futures are mapped empty
268 {
269 FutureType<void> f1 = futurize();
270 FutureType<void> f2 = futurize();
271 FutureType<void> f3 = futurize();
272 using Result = decltype(unwrap(f1, f2, f3));
273 static_assert(std::is_void<Result>::value, "Failed...");
274 }
275
276 // Delete empty futures out variadic packs
277 {
278 FutureType<void> f = futurize();
279
280 auto callable = unwrapping([](int a, int b) {
281 // ...
282 return a + b;
283 });
284
285 HPX_TEST_EQ((callable(1, f, 2)), 3);
286 }
287
288 // Call callables with no arguments if the pack was mapped empty.
289 // Based on a build failure in local_dataflow_executor_v1.cpp.
290 {
291 FutureType<void> f = futurize();
292
293 auto callable = unwrapping([]() {
294 // ...
295 return true;
296 });
297
298 HPX_TEST((callable(f)));
299 }
300
301 // Map empty mappings back to void, if an empty mapping was propagated back.
302 // Based on a build failure in global_spmd_block.cpp.
303 {
304 std::vector<FutureType<void>> vec;
305 (void) vec;
306 using Result = decltype(unwrap(vec));
307 static_assert(std::is_void<Result>::value, "Failed...");
308 }
309
310 // Unwrap single tuple like types back.
311 {
312 std::array<FutureType<int>, 2> in{{futurize(1), futurize(2)}};
313
314 std::array<int, 2> result = unwrap(in);
315
316 HPX_TEST((result == std::array<int, 2>{{1, 2}}));
317 }
318
319 // Don't unwrap single tuple like types which were passed to the
320 // unwrapping callable object.
321 {
322 auto unwrapper = unwrapping([](std::array<int, 2> in) {
323 // ...
324 return in[0] + in[1];
325 });
326
327 std::array<FutureType<int>, 2> in{{futurize(1), futurize(2)}};
328
329 HPX_TEST_EQ((unwrapper(in)), 3);
330 }
331 }
332
333 /// The unit test for the original impleementation `unwrapped`
334 /// which was overtaken by `unwrap` and `unwrapping`.
335 /// Most of this functionality was taken and adapted from
336 /// the file: `tests/unit/util/unwrapped.cpp`.
337 namespace legacy_tests {
338 std::atomic<std::size_t> void_counter;
339
null_thread()340 static void null_thread()
341 {
342 ++void_counter;
343 }
344
345 std::atomic<std::size_t> result_counter;
346
null_result_thread()347 static bool null_result_thread()
348 {
349 ++result_counter;
350 return true;
351 }
352
increment(int c)353 static int increment(int c)
354 {
355 return c + 1;
356 }
357
accumulate(std::vector<int> cs)358 static int accumulate(std::vector<int> cs)
359 {
360 return std::accumulate(cs.begin(), cs.end(), 0);
361 }
362
add(tuple<int,int> result)363 static int add(tuple<int, int> result)
364 {
365 return get<0>(result) + get<1>(result);
366 }
367
368 template <template <typename> class FutureType, typename FutureProvider>
test_legacy_requirements(FutureProvider && futurize)369 void test_legacy_requirements(FutureProvider&& futurize)
370 {
371 using hpx::async;
372
373 {
374 // Sync wait, single future, void return.
375 {
376 unwrap(async(null_thread));
377
378 HPX_TEST_EQ(1U, void_counter.load());
379
380 void_counter.store(0);
381 }
382
383 // Sync wait, single future, non-void return.
384 {
385 HPX_TEST_EQ(true, unwrap(async(null_result_thread)));
386 HPX_TEST_EQ(1U, result_counter.load());
387
388 result_counter.store(0);
389 }
390
391 // Sync wait, multiple futures, void return.
392 {
393 unwrap(async(null_thread), async(null_thread), async(null_thread));
394
395 HPX_TEST_EQ(3U, void_counter.load());
396
397 void_counter.store(0);
398 }
399
400 // Sync wait, multiple futures, non-void return.
401 {
402 tuple<bool, bool, bool> r = unwrap(async(null_result_thread),
403 async(null_result_thread),
404 async(null_result_thread));
405
406 HPX_TEST_EQ(true, get<0>(r));
407 HPX_TEST_EQ(true, get<1>(r));
408 HPX_TEST_EQ(true, get<2>(r));
409 HPX_TEST_EQ(3U, result_counter.load());
410
411 result_counter.store(0);
412 }
413
414 // Sync wait, vector of futures, void return.
415 {
416 std::vector<FutureType<void>> futures;
417 futures.reserve(64);
418
419 for (std::size_t i = 0; i < 64; ++i)
420 futures.push_back(async(null_thread));
421
422 unwrap(futures);
423
424 HPX_TEST_EQ(64U, void_counter.load());
425
426 void_counter.store(0);
427 }
428
429 // Sync wait, array of futures, void return.
430 {
431 std::array<FutureType<void>, 64> futures;
432
433 for (std::size_t i = 0; i < 64; ++i)
434 futures[i] = async(null_thread);
435
436 unwrap(futures);
437
438 HPX_TEST_EQ(64U, void_counter.load());
439
440 void_counter.store(0);
441 }
442
443 // Sync wait, vector of futures, non-void return.
444 {
445 std::vector<FutureType<bool>> futures;
446 futures.reserve(64);
447
448 std::vector<bool> values;
449 values.reserve(64);
450
451 for (std::size_t i = 0; i < 64; ++i)
452 futures.push_back(async(null_result_thread));
453
454 values = unwrap(futures);
455
456 HPX_TEST_EQ(64U, result_counter.load());
457
458 for (std::size_t i = 0; i < 64; ++i)
459 HPX_TEST_EQ(true, values[i]);
460
461 result_counter.store(0);
462 }
463
464 // Sync wait, array of futures, non-void return.
465 {
466 std::array<FutureType<bool>, 64> futures;
467
468 for (std::size_t i = 0; i < 64; ++i)
469 futures[i] = async(null_result_thread);
470
471 std::array<bool, 64> values = unwrap(futures);
472
473 HPX_TEST_EQ(64U, result_counter.load());
474
475 for (std::size_t i = 0; i < 64; ++i)
476 HPX_TEST_EQ(true, values[i]);
477
478 result_counter.store(0);
479 }
480
481 // Sync wait, vector of futures, non-void return ignored.
482 {
483 std::vector<FutureType<bool>> futures;
484 futures.reserve(64);
485
486 for (std::size_t i = 0; i < 64; ++i)
487 futures.push_back(async(null_result_thread));
488
489 unwrap(futures);
490
491 HPX_TEST_EQ(64U, result_counter.load());
492
493 result_counter.store(0);
494 }
495
496 // Sync wait, array of futures, non-void return ignored.
497 {
498 std::array<FutureType<bool>, 64> futures;
499
500 for (std::size_t i = 0; i < 64; ++i)
501 futures[i] = async(null_result_thread);
502
503 unwrap(futures);
504
505 HPX_TEST_EQ(64U, result_counter.load());
506
507 result_counter.store(0);
508 }
509
510 // Functional wrapper, single future
511 {
512 FutureType<int> future = futurize(42);
513
514 HPX_TEST_EQ(unwrapping(&increment)(future), 42 + 1);
515 }
516
517 // Functional wrapper, vector of future
518 {
519 std::vector<FutureType<int>> futures;
520 futures.reserve(64);
521
522 for (std::size_t i = 0; i < 64; ++i)
523 futures.push_back(futurize(42));
524
525 HPX_TEST_EQ(unwrapping(&accumulate)(futures), 42 * 64);
526 }
527
528 // Functional wrapper, tuple of future
529 {
530 tuple<FutureType<int>, FutureType<int>> tuple =
531 make_tuple(futurize(42), futurize(42));
532
533 HPX_TEST_EQ(unwrapping(&add)(tuple), 42 + 42);
534 }
535
536 // Functional wrapper, future of tuple of future
537 {
538 FutureType<tuple<FutureType<int>, FutureType<int>>> tuple_future =
539 futurize(make_tuple(futurize(42), futurize(42)));
540
541 HPX_TEST_EQ(unwrapping_n<2>(&add)(tuple_future), 42 + 42);
542 }
543 }
544 }
545 } // end namespace legacy_tests
546
547 /// A callable object which provides a specific future type
548 template <template <typename> class FutureType>
549 struct future_factory
550 {
operator ()future_factory551 FutureType<void> operator()() const
552 {
553 return hpx::lcos::make_ready_future();
554 }
555
556 template <typename T>
operator ()future_factory557 FutureType<typename std::decay<T>::type> operator()(T&& value) const
558 {
559 return hpx::lcos::make_ready_future(std::forward<T>(value));
560 }
561 };
562
563 template <template <typename> class FutureType>
test_all()564 void test_all()
565 {
566 future_factory<FutureType> provider;
567
568 test_unwrap<FutureType>(provider);
569 test_unwrap_n<FutureType>(provider);
570 test_unwrap_all<FutureType>(provider);
571
572 test_unwrapping<FutureType>(provider);
573 test_unwrapping_n<FutureType>(provider);
574 test_unwrapping_all<FutureType>(provider);
575
576 test_development_regressions<FutureType>(provider);
577
578 legacy_tests::test_legacy_requirements<FutureType>(provider);
579 }
580
hpx_main()581 int hpx_main()
582 {
583 // Test everything using default futures
584 test_all<hpx::lcos::future>();
585 // Test everything using shared futures
586 test_all<hpx::lcos::shared_future>();
587
588 return hpx::finalize();
589 }
590
main(int argc,char * argv[])591 int main(int argc, char* argv[])
592 {
593 // Configure application-specific options.
594 boost::program_options::options_description cmdline(
595 "usage: " HPX_APPLICATION_STRING " [options]");
596
597 // We force this test to use several threads by default.
598 std::vector<std::string> const cfg = {"hpx.os_threads=all"};
599
600 // Initialize and run HPX
601 if (int result = hpx::init(cmdline, argc, argv, cfg))
602 {
603 return result;
604 }
605 // Report errors after HPX was finished
606 return hpx::util::report_errors();
607 }
608