1 //  Copyright (c) 2007-2014 Hartmut Kaiser
2 //
3 //  Distributed under the Boost Software License, Version 1.0. (See accompanying
4 //  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #include <hpx/hpx_init.hpp>
7 #include <hpx/hpx.hpp>
8 #include <hpx/include/parallel_rotate.hpp>
9 #include <hpx/util/lightweight_test.hpp>
10 #include <hpx/util/iterator_range.hpp>
11 
12 #include <cstddef>
13 #include <iostream>
14 #include <iterator>
15 #include <numeric>
16 #include <string>
17 #include <vector>
18 
19 #include "test_utils.hpp"
20 
21 ///////////////////////////////////////////////////////////////////////////////
22 template <typename ExPolicy, typename IteratorTag>
test_rotate(ExPolicy policy,IteratorTag)23 void test_rotate(ExPolicy policy, IteratorTag)
24 {
25     static_assert(
26         hpx::parallel::execution::is_execution_policy<ExPolicy>::value,
27         "hpx::parallel::execution::is_execution_policy<ExPolicy>::value");
28 
29     typedef std::vector<std::size_t>::iterator base_iterator;
30     typedef test::test_iterator<base_iterator, IteratorTag> iterator;
31 
32     typedef test::test_container<std::vector<std::size_t>, IteratorTag> test_vector;
33 
34     test_vector c(10007);
35     std::vector<std::size_t> d1;
36 
37     std::iota(std::begin(c.base()), std::end(c.base()), std::rand());
38     std::copy(std::begin(c.base()), std::end(c.base()), std::back_inserter(d1));
39 
40     std::size_t mid_pos = std::rand() % c.size(); //-V104
41     auto mid = std::begin(c);
42     std::advance(mid, mid_pos);
43 
44     hpx::parallel::rotate(policy, c, iterator(mid));
45 
46     base_iterator mid1 = std::begin(d1);
47     std::advance(mid1, mid_pos);
48     std::rotate(std::begin(d1), mid1, std::end(d1));
49 
50     std::size_t count = 0;
51     HPX_TEST(std::equal(std::begin(c.base()), std::end(c.base()),
52         std::begin(d1),
53         [&count](std::size_t v1, std::size_t v2) -> bool {
54             HPX_TEST_EQ(v1, v2);
55             ++count;
56             return v1 == v2;
57         }));
58     HPX_TEST_EQ(count, d1.size());
59 }
60 
61 template <typename ExPolicy, typename IteratorTag>
test_rotate_async(ExPolicy p,IteratorTag)62 void test_rotate_async(ExPolicy p, IteratorTag)
63 {
64     typedef std::vector<std::size_t>::iterator base_iterator;
65     typedef test::test_iterator<base_iterator, IteratorTag> iterator;
66 
67     typedef test::test_container<std::vector<std::size_t>, IteratorTag> test_vector;
68 
69     test_vector c(10007);
70     std::vector<std::size_t> d1;
71 
72     std::iota(std::begin(c.base()), std::end(c.base()), std::rand());
73     std::copy(std::begin(c.base()), std::end(c.base()), std::back_inserter(d1));
74 
75     std::size_t mid_pos = std::rand() % c.size(); //-V104
76 
77     auto mid = std::begin(c);
78     std::advance(mid, mid_pos);
79 
80     auto f = hpx::parallel::rotate(p, c, iterator(mid));
81     f.wait();
82 
83     base_iterator mid1 = std::begin(d1);
84     std::advance(mid1, mid_pos);
85     std::rotate(std::begin(d1), mid1, std::end(d1));
86 
87     std::size_t count = 0;
88     HPX_TEST(std::equal(std::begin(c.base()), std::end(c.base()),
89         std::begin(d1),
90         [&count](std::size_t v1, std::size_t v2) -> bool {
91             HPX_TEST_EQ(v1, v2);
92             ++count;
93             return v1 == v2;
94         }));
95     HPX_TEST_EQ(count, d1.size());
96 }
97 
98 template <typename IteratorTag>
test_rotate()99 void test_rotate()
100 {
101     using namespace hpx::parallel;
102     test_rotate(execution::seq, IteratorTag());
103     test_rotate(execution::par, IteratorTag());
104     test_rotate(execution::par_unseq, IteratorTag());
105 
106     test_rotate_async(execution::seq(execution::task), IteratorTag());
107     test_rotate_async(execution::par(execution::task), IteratorTag());
108 }
109 
rotate_test()110 void rotate_test()
111 {
112     test_rotate<std::random_access_iterator_tag>();
113     test_rotate<std::forward_iterator_tag>();
114 }
115 
116 ///////////////////////////////////////////////////////////////////////////////
117 template <typename ExPolicy, typename IteratorTag>
test_rotate_exception(ExPolicy policy,IteratorTag)118 void test_rotate_exception(ExPolicy policy, IteratorTag)
119 {
120     static_assert(
121         hpx::parallel::execution::is_execution_policy<ExPolicy>::value,
122         "hpx::parallel::execution::is_execution_policy<ExPolicy>::value");
123 
124     typedef std::vector<std::size_t>::iterator base_iterator;
125     typedef test::decorated_iterator<base_iterator, IteratorTag>
126         decorated_iterator;
127 
128     std::vector<std::size_t> c(10007);
129     std::iota(std::begin(c), std::end(c), std::rand());
130 
131     base_iterator mid = std::begin(c);
132 
133     // move at least one element to guarantee an exception to be thrown
134     std::size_t delta = (std::max)(std::rand() % c.size(), std::size_t(2)); //-V104
135     std::advance(mid, delta);
136 
137     bool caught_exception = false;
138     try {
139         hpx::parallel::rotate(policy,
140             hpx::util::make_iterator_range(
141                 decorated_iterator(
142                     std::begin(c),
143                     [](){ throw std::runtime_error("test"); }),
144                 decorated_iterator(std::end(c))),
145             decorated_iterator(mid));
146         HPX_TEST(false);
147     }
148     catch (hpx::exception_list const& e) {
149         caught_exception = true;
150         test::test_num_exceptions<ExPolicy, IteratorTag>::call(policy, e);
151     }
152     catch (...) {
153         HPX_TEST(false);
154     }
155 
156     HPX_TEST(caught_exception);
157 }
158 
159 template <typename ExPolicy, typename IteratorTag>
test_rotate_exception_async(ExPolicy p,IteratorTag)160 void test_rotate_exception_async(ExPolicy p, IteratorTag)
161 {
162     typedef std::vector<std::size_t>::iterator base_iterator;
163     typedef test::decorated_iterator<base_iterator, IteratorTag>
164         decorated_iterator;
165 
166     std::vector<std::size_t> c(10007);
167     std::iota(std::begin(c), std::end(c), std::rand());
168 
169     base_iterator mid = std::begin(c);
170 
171     // move at least one element to guarantee an exception to be thrown
172     std::size_t delta = (std::max)(std::rand() % c.size(), std::size_t(2)); //-V104
173     std::advance(mid, delta);
174 
175     bool caught_exception = false;
176     bool returned_from_algorithm = false;
177     try {
178         auto f =
179             hpx::parallel::rotate(p,
180                 hpx::util::make_iterator_range(
181                     decorated_iterator(
182                         std::begin(c),
183                         [](){ throw std::runtime_error("test"); }),
184                     decorated_iterator(std::end(c))),
185                 decorated_iterator(mid));
186         returned_from_algorithm = true;
187         f.get();
188 
189         HPX_TEST(false);
190     }
191     catch (hpx::exception_list const& e) {
192         caught_exception = true;
193         test::test_num_exceptions<ExPolicy, IteratorTag>::call(p, e);
194     }
195     catch (...) {
196         HPX_TEST(false);
197     }
198 
199     HPX_TEST(caught_exception);
200     HPX_TEST(returned_from_algorithm);
201 }
202 
203 template <typename IteratorTag>
test_rotate_exception()204 void test_rotate_exception()
205 {
206     using namespace hpx::parallel;
207 
208     // If the execution policy object is of type vector_execution_policy,
209     // std::terminate shall be called. therefore we do not test exceptions
210     // with a vector execution policy
211     test_rotate_exception(execution::seq, IteratorTag());
212     test_rotate_exception(execution::par, IteratorTag());
213 
214     test_rotate_exception_async(execution::seq(execution::task), IteratorTag());
215     test_rotate_exception_async(execution::par(execution::task), IteratorTag());
216 }
217 
rotate_exception_test()218 void rotate_exception_test()
219 {
220     test_rotate_exception<std::random_access_iterator_tag>();
221     test_rotate_exception<std::forward_iterator_tag>();
222 }
223 
224 //////////////////////////////////////////////////////////////////////////////
225 template <typename ExPolicy, typename IteratorTag>
test_rotate_bad_alloc(ExPolicy policy,IteratorTag)226 void test_rotate_bad_alloc(ExPolicy policy, IteratorTag)
227 {
228     static_assert(
229         hpx::parallel::execution::is_execution_policy<ExPolicy>::value,
230         "hpx::parallel::execution::is_execution_policy<ExPolicy>::value");
231 
232     typedef std::vector<std::size_t>::iterator base_iterator;
233     typedef test::decorated_iterator<base_iterator, IteratorTag>
234         decorated_iterator;
235 
236     std::vector<std::size_t> c(10007);
237     std::iota(std::begin(c), std::end(c), std::rand());
238 
239     base_iterator mid = std::begin(c);
240 
241     // move at least one element to guarantee an exception to be thrown
242     std::size_t delta = (std::max)(std::rand() % c.size(), std::size_t(2)); //-V104
243     std::advance(mid, delta);
244 
245     bool caught_bad_alloc = false;
246     try {
247         hpx::parallel::rotate(policy,
248             hpx::util::make_iterator_range(
249                 decorated_iterator(
250                     std::begin(c),
251                     [](){ throw std::bad_alloc(); }),
252                 decorated_iterator(std::end(c))),
253             decorated_iterator(mid));
254         HPX_TEST(false);
255     }
256     catch (std::bad_alloc const&) {
257         caught_bad_alloc = true;
258     }
259     catch (...) {
260         HPX_TEST(false);
261     }
262 
263     HPX_TEST(caught_bad_alloc);
264 }
265 
266 template <typename ExPolicy, typename IteratorTag>
test_rotate_bad_alloc_async(ExPolicy p,IteratorTag)267 void test_rotate_bad_alloc_async(ExPolicy p, IteratorTag)
268 {
269     typedef std::vector<std::size_t>::iterator base_iterator;
270     typedef test::decorated_iterator<base_iterator, IteratorTag>
271         decorated_iterator;
272 
273     std::vector<std::size_t> c(10007);
274     std::iota(std::begin(c), std::end(c), std::rand());
275 
276     base_iterator mid = std::begin(c);
277 
278     // move at least one element to guarantee an exception to be thrown
279     std::size_t delta = (std::max)(std::rand() % c.size(), std::size_t(2)); //-V104
280     std::advance(mid, delta);
281 
282     bool caught_bad_alloc = false;
283     bool returned_from_algorithm = false;
284     try {
285         auto f =
286             hpx::parallel::rotate(p,
287                 hpx::util::make_iterator_range(
288                     decorated_iterator(
289                         std::begin(c),
290                         [](){ throw std::bad_alloc(); }),
291                     decorated_iterator(std::end(c))),
292                 decorated_iterator(mid));
293         returned_from_algorithm = true;
294         f.get();
295 
296         HPX_TEST(false);
297     }
298     catch(std::bad_alloc const&) {
299         caught_bad_alloc = true;
300     }
301     catch(...) {
302         HPX_TEST(false);
303     }
304 
305     HPX_TEST(caught_bad_alloc);
306     HPX_TEST(returned_from_algorithm);
307 }
308 
309 template <typename IteratorTag>
test_rotate_bad_alloc()310 void test_rotate_bad_alloc()
311 {
312     using namespace hpx::parallel;
313 
314     // If the execution policy object is of type vector_execution_policy,
315     // std::terminate shall be called. therefore we do not test exceptions
316     // with a vector execution policy
317     test_rotate_bad_alloc(execution::seq, IteratorTag());
318     test_rotate_bad_alloc(execution::par, IteratorTag());
319 
320     test_rotate_bad_alloc_async(execution::seq(execution::task), IteratorTag());
321     test_rotate_bad_alloc_async(execution::par(execution::task), IteratorTag());
322 }
323 
rotate_bad_alloc_test()324 void rotate_bad_alloc_test()
325 {
326     test_rotate_bad_alloc<std::random_access_iterator_tag>();
327     test_rotate_bad_alloc<std::forward_iterator_tag>();
328 }
329 
hpx_main(boost::program_options::variables_map & vm)330 int hpx_main(boost::program_options::variables_map& vm)
331 {
332     unsigned int seed = (unsigned int)std::time(nullptr);
333     if (vm.count("seed"))
334         seed = vm["seed"].as<unsigned int>();
335 
336     std::cout << "using seed: " << seed << std::endl;
337     std::srand(seed);
338 
339     rotate_test();
340     rotate_exception_test();
341     rotate_bad_alloc_test();
342     return hpx::finalize();
343 }
344 
main(int argc,char * argv[])345 int main(int argc, char* argv[])
346 {
347     // add command line option which controls the random number generator seed
348     using namespace boost::program_options;
349     options_description desc_commandline(
350         "Usage: " HPX_APPLICATION_STRING " [options]");
351 
352     desc_commandline.add_options()
353         ("seed,s", value<unsigned int>(),
354         "the random number generator seed to use for this run")
355         ;
356 
357     // By default this test should run on all available cores
358     std::vector<std::string> const cfg = {
359         "hpx.os_threads=all"
360     };
361 
362     // Initialize and run HPX
363     HPX_TEST_EQ_MSG(hpx::init(desc_commandline, argc, argv, cfg), 0,
364         "HPX main exited with non-zero status");
365 
366     return hpx::util::report_errors();
367 
368 }
369