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