1 // Copyright (c) 2017 Denis Blank
2 // Copyright Andrey Semashev 2007 - 2013.
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6
7 #include <hpx/config.hpp>
8 #include <hpx/util/atomic_count.hpp>
9 #include <hpx/util/lightweight_test.hpp>
10 #include <hpx/util/pack_traversal_async.hpp>
11 #include <hpx/util/tuple.hpp>
12 #include <hpx/util/unused.hpp>
13
14 #include <cstddef>
15 #include <cstdint>
16 #include <list>
17 #include <memory>
18 #include <set>
19 #include <type_traits>
20 #include <utility>
21 #include <vector>
22 #include <array>
23
24 using hpx::util::async_traverse_complete_tag;
25 using hpx::util::async_traverse_detach_tag;
26 using hpx::util::async_traverse_visit_tag;
27 using hpx::util::make_tuple;
28 using hpx::util::traverse_pack_async;
29 using hpx::util::tuple;
30
31 /// A tag which isn't accepted by any mapper
32 struct not_accepted_tag
33 {
34 };
35
36 struct thread_safe_counter
37 {
38 typedef hpx::util::atomic_count type;
39
loadthread_safe_counter40 static unsigned int load(hpx::util::atomic_count const& counter) noexcept
41 {
42 return static_cast<unsigned int>(static_cast<long>(counter));
43 }
44
incrementthread_safe_counter45 static void increment(hpx::util::atomic_count& counter) noexcept
46 {
47 ++counter;
48 }
49
decrementthread_safe_counter50 static unsigned int decrement(hpx::util::atomic_count& counter) noexcept
51 {
52 return static_cast<unsigned int>(--counter);
53 }
54 };
55
56 template <typename Derived, typename CounterPolicy = thread_safe_counter>
57 class intrusive_ref_counter;
58
59 template <typename Derived, typename CounterPolicy>
60 void intrusive_ptr_add_ref(
61 intrusive_ref_counter<Derived, CounterPolicy> const* p) noexcept;
62
63 template <typename Derived, typename CounterPolicy>
64 void intrusive_ptr_release(
65 intrusive_ref_counter<Derived, CounterPolicy> const* p) noexcept;
66
67 template <typename Derived, typename CounterPolicy>
68 class intrusive_ref_counter
69 {
70 private:
71 typedef typename CounterPolicy::type counter_type;
72
73 mutable counter_type ref_counter;
74
75 public:
intrusive_ref_counter()76 intrusive_ref_counter() noexcept : ref_counter(1) {}
77
use_count() const78 unsigned int use_count() const noexcept
79 {
80 return CounterPolicy::load(ref_counter);
81 }
82
83 protected:
84 ~intrusive_ref_counter() = default;
85
86 friend void intrusive_ptr_add_ref<Derived, CounterPolicy>(
87 intrusive_ref_counter<Derived, CounterPolicy> const* p)
88 noexcept;
89
90 friend void intrusive_ptr_release<Derived, CounterPolicy>(
91 intrusive_ref_counter<Derived, CounterPolicy> const* p)
92 noexcept;
93 };
94
95 template <typename Derived, typename CounterPolicy>
intrusive_ptr_add_ref(intrusive_ref_counter<Derived,CounterPolicy> const * p)96 inline void intrusive_ptr_add_ref(
97 intrusive_ref_counter<Derived, CounterPolicy> const* p) noexcept
98 {
99 CounterPolicy::increment(p->ref_counter);
100 }
101
102 template <typename Derived, typename CounterPolicy>
intrusive_ptr_release(intrusive_ref_counter<Derived,CounterPolicy> const * p)103 inline void intrusive_ptr_release(
104 intrusive_ref_counter<Derived, CounterPolicy> const* p) noexcept
105 {
106 if (CounterPolicy::decrement(p->ref_counter) == 0)
107 delete static_cast<Derived const*>(p);
108 }
109
110 template <typename Child>
111 class async_counter_base : public intrusive_ref_counter<Child>
112 {
113 std::size_t counter_ = 0;
114
115 public:
116 async_counter_base() = default;
117
~async_counter_base()118 virtual ~async_counter_base() {}
119
counter() const120 std::size_t const& counter() const noexcept
121 {
122 return counter_;
123 }
124
counter()125 std::size_t& counter() noexcept
126 {
127 return counter_;
128 }
129 };
130
131 template <std::size_t ArgCount>
132 struct async_increasing_int_sync_visitor
133 : async_counter_base<async_increasing_int_sync_visitor<ArgCount>>
134 {
async_increasing_int_sync_visitorasync_increasing_int_sync_visitor135 explicit async_increasing_int_sync_visitor(int dummy) {}
136
operator ()async_increasing_int_sync_visitor137 bool operator()(async_traverse_visit_tag, std::size_t i)
138 {
139 HPX_TEST_EQ(i, this->counter());
140 ++this->counter();
141 return true;
142 }
143
144 template <typename N>
operator ()async_increasing_int_sync_visitor145 void operator()(async_traverse_detach_tag, std::size_t i, N&& next)
146 {
147 HPX_UNUSED(i);
148 HPX_UNUSED(next);
149
150 // Should never be called!
151 HPX_TEST(false);
152 }
153
154 template <typename T>
operator ()async_increasing_int_sync_visitor155 void operator()(async_traverse_complete_tag, T&& pack)
156 {
157 HPX_UNUSED(pack);
158
159 HPX_TEST_EQ(this->counter(), ArgCount);
160 ++this->counter();
161 }
162 };
163
164 template <std::size_t ArgCount>
165 struct async_increasing_int_visitor
166 : async_counter_base<async_increasing_int_visitor<ArgCount>>
167 {
async_increasing_int_visitorasync_increasing_int_visitor168 explicit async_increasing_int_visitor(int dummy) {}
169
operator ()async_increasing_int_visitor170 bool operator()(async_traverse_visit_tag, std::size_t i) const
171 {
172 HPX_TEST_EQ(i, this->counter());
173 return false;
174 }
175
176 template <typename N>
operator ()async_increasing_int_visitor177 void operator()(async_traverse_detach_tag, std::size_t i, N&& next)
178 {
179 HPX_UNUSED(i);
180
181 ++this->counter();
182 std::forward<N>(next)();
183 }
184
185 template <typename T>
operator ()async_increasing_int_visitor186 void operator()(async_traverse_complete_tag, T&& pack)
187 {
188 HPX_UNUSED(pack);
189
190 HPX_TEST_EQ(this->counter(), ArgCount);
191 ++this->counter();
192 }
193 };
194
195 template <std::size_t ArgCount, typename... Args>
test_async_traversal_base(Args &&...args)196 void test_async_traversal_base(Args&&... args)
197 {
198 // Test that every element is traversed in the correct order
199 // when we detach the control flow on every visit.
200 {
201 auto result = traverse_pack_async(
202 hpx::util::async_traverse_in_place_tag<
203 async_increasing_int_sync_visitor<ArgCount>>{},
204 42, args...);
205 HPX_TEST_EQ(result->counter(), ArgCount + 1U);
206 }
207
208 // Test that every element is traversed in the correct order
209 // when we detach the control flow on every visit.
210 {
211 auto result = traverse_pack_async(
212 hpx::util::async_traverse_in_place_tag<
213 async_increasing_int_visitor<ArgCount>>{},
214 42, args...);
215 HPX_TEST_EQ(result->counter(), ArgCount + 1U);
216 }
217 }
218
test_async_traversal()219 static void test_async_traversal()
220 {
221 // Just test everything using a casual int pack
222 test_async_traversal_base<4U>(not_accepted_tag{},
223 0U,
224 1U,
225 not_accepted_tag{},
226 2U,
227 3U,
228 not_accepted_tag{});
229 }
230
231 template <typename ContainerFactory>
test_async_container_traversal_impl(ContainerFactory && container_of)232 void test_async_container_traversal_impl(ContainerFactory&& container_of)
233 {
234 // Test by passing a containers in the middle
235 test_async_traversal_base<4U>(0U, container_of(1U, 2U), 3U);
236 // Test by splitting the pack in two containers
237 test_async_traversal_base<4U>(container_of(0U, 1U), container_of(2U, 3U));
238 // Test by passing a huge containers to the traversal
239 test_async_traversal_base<4U>(container_of(0U, 1U, 2U, 3U));
240 }
241
242 template <typename T>
243 struct common_container_factory
244 {
245 template <typename... Args>
operator ()common_container_factory246 T operator()(Args&&... args)
247 {
248 return T{std::forward<Args>(args)...};
249 }
250 };
251
252 template <typename T>
253 struct array_container_factory
254 {
255 template <typename... Args, typename Array = std::array<T, sizeof...(Args)>>
operator ()array_container_factory256 Array operator()(Args&&... args)
257 {
258 return Array{{std::forward<Args>(args)...}};
259 }
260 };
261
test_async_container_traversal()262 static void test_async_container_traversal()
263 {
264 {
265 common_container_factory<std::vector<std::size_t>> factory;
266 test_async_container_traversal_impl(factory);
267 }
268
269 {
270 common_container_factory<std::list<std::size_t>> factory;
271 test_async_container_traversal_impl(factory);
272 }
273
274 {
275 common_container_factory<std::set<std::size_t>> factory;
276 test_async_container_traversal_impl(factory);
277 }
278
279 {
280 array_container_factory<std::size_t> factory;
281 test_async_container_traversal_impl(factory);
282 }
283 }
284
test_async_tuple_like_traversal()285 static void test_async_tuple_like_traversal()
286 {
287 // Test by passing a tuple in the middle
288 test_async_traversal_base<4U>(
289 not_accepted_tag{}, 0U, make_tuple(1U, not_accepted_tag{}, 2U), 3U);
290 // Test by splitting the pack in two tuples
291 test_async_traversal_base<4U>(
292 make_tuple(0U, not_accepted_tag{}, 1U), make_tuple(2U, 3U));
293 // Test by passing a huge tuple to the traversal
294 test_async_traversal_base<4U>(make_tuple(0U, 1U, 2U, 3U));
295 }
296
297 template <typename T,
298 typename... Args,
299 typename Vector = std::vector<typename std::decay<T>::type>>
vector_of(T && first,Args &&...args)300 Vector vector_of(T&& first, Args&&... args)
301 {
302 return Vector{std::forward<T>(first), std::forward<Args>(args)...};
303 }
304
test_async_mixed_traversal()305 static void test_async_mixed_traversal()
306 {
307 using container_t = std::vector<std::size_t>;
308
309 // Test hierarchies where container and tuple like types are mixed
310 test_async_traversal_base<4U>(
311 0U, hpx::util::make_tuple(container_t{1U, 2U}), 3U);
312
313 test_async_traversal_base<4U>(
314 hpx::util::make_tuple(
315 0U, vector_of(not_accepted_tag{}), vector_of(vector_of(1U))),
316 make_tuple(2U, 3U));
317
318 test_async_traversal_base<4U>(
319 vector_of(vector_of(make_tuple(0U, 1U, 2U, 3U))));
320 }
321
322 template <std::size_t ArgCount>
323 struct async_unique_sync_visitor
324 : async_counter_base<async_unique_sync_visitor<ArgCount>>
325 {
async_unique_sync_visitorasync_unique_sync_visitor326 explicit async_unique_sync_visitor(int dummy) {}
327
operator ()async_unique_sync_visitor328 bool operator()(async_traverse_visit_tag, std::unique_ptr<std::size_t>& i)
329 {
330 HPX_TEST_EQ(*i, this->counter());
331 ++this->counter();
332 return true;
333 }
334
335 template <typename N>
operator ()async_unique_sync_visitor336 void operator()(async_traverse_detach_tag,
337 std::unique_ptr<std::size_t>& i,
338 N&& next)
339 {
340 HPX_UNUSED(i);
341 HPX_UNUSED(next);
342
343 // Should never be called!
344 HPX_TEST(false);
345 }
346
347 template <typename T>
operator ()async_unique_sync_visitor348 void operator()(async_traverse_complete_tag, T&& pack)
349 {
350 HPX_UNUSED(pack);
351
352 HPX_TEST_EQ(this->counter(), ArgCount);
353 ++this->counter();
354 }
355 };
356
357 template <std::size_t ArgCount>
358 struct async_unique_visitor : async_counter_base<async_unique_visitor<ArgCount>>
359 {
async_unique_visitorasync_unique_visitor360 explicit async_unique_visitor(int dummy) {}
361
operator ()async_unique_visitor362 bool operator()(async_traverse_visit_tag,
363 std::unique_ptr<std::size_t>& i) const
364 {
365 HPX_TEST_EQ(*i, this->counter());
366 return false;
367 }
368
369 template <typename N>
operator ()async_unique_visitor370 void operator()(async_traverse_detach_tag,
371 std::unique_ptr<std::size_t>& i,
372 N&& next)
373 {
374 HPX_UNUSED(i);
375
376 ++this->counter();
377 std::forward<N>(next)();
378 }
379
380 template <typename T>
operator ()async_unique_visitor381 void operator()(async_traverse_complete_tag, T&& pack)
382 {
383 HPX_UNUSED(pack);
384
385 HPX_TEST_EQ(this->counter(), ArgCount);
386 ++this->counter();
387 }
388 };
389
test_async_move_only_traversal()390 static void test_async_move_only_traversal()
391 {
392 auto const of = [](std::size_t i) {
393 return std::unique_ptr<std::size_t>(new std::size_t(i));
394 };
395
396 {
397 auto result = traverse_pack_async(
398 hpx::util::async_traverse_in_place_tag<
399 async_unique_sync_visitor<4>>{},
400 42, of(0), of(1), of(2), of(3));
401 HPX_TEST_EQ(result->counter(), 5U);
402 }
403
404 {
405 auto result = traverse_pack_async(
406 hpx::util::async_traverse_in_place_tag<
407 async_unique_visitor<4>>{},
408 42, of(0), of(1), of(2), of(3));
409 HPX_TEST_EQ(result->counter(), 5U);
410 }
411 }
412
413 struct invalidate_visitor : async_counter_base<invalidate_visitor>
414 {
invalidate_visitorinvalidate_visitor415 explicit invalidate_visitor(int dummy) {}
416
operator ()invalidate_visitor417 bool operator()(async_traverse_visit_tag, std::shared_ptr<int>& i) const
418 {
419 HPX_TEST_EQ(*i, 22);
420 return false;
421 }
422
423 template <typename N>
operator ()invalidate_visitor424 void operator()(async_traverse_detach_tag,
425 std::shared_ptr<int>& i,
426 N&& next)
427 {
428 HPX_UNUSED(i);
429
430 std::forward<N>(next)();
431 }
432
433 // Test whether the passed pack was passed as r-value reference
operator ()invalidate_visitor434 void operator()(async_traverse_complete_tag,
435 tuple<std::shared_ptr<int>>&& pack) const
436 {
437 // Invalidate the moved object
438 tuple<std::shared_ptr<int>> moved = std::move(pack);
439
440 HPX_UNUSED(moved);
441 }
442 };
443
444 // Check whether the arguments are invalidated (moved out) when called
test_async_complete_invalidation()445 static void test_async_complete_invalidation()
446 {
447 auto value = std::make_shared<int>(22);
448
449 auto frame = traverse_pack_async(
450 hpx::util::async_traverse_in_place_tag<invalidate_visitor>{},
451 42, value);
452
453 HPX_TEST_EQ(value.use_count(), 1U);
454 }
455
main(int,char **)456 int main(int, char**)
457 {
458 test_async_traversal();
459 test_async_container_traversal();
460 test_async_tuple_like_traversal();
461 test_async_mixed_traversal();
462 test_async_move_only_traversal();
463 test_async_complete_invalidation();
464
465 return hpx::util::report_errors();
466 }
467