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