1 /*
2     Copyright (c) 2020-2021 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #ifndef __TBB_test_common_parallel_invoke_common_H
18 #define __TBB_test_common_parallel_invoke_common_H
19 
20 #include "config.h"
21 
22 #include <cstddef>
23 #include <tuple>
24 
25 #include "oneapi/tbb/parallel_invoke.h"
26 #include "oneapi/tbb/global_control.h"
27 
28 #include "dummy_body.h"
29 
30 // Helps generate a tuple of functional objects
31 template<std::size_t Counter, template <std::size_t> class Functor, class Generator, typename... Fs>
32 struct generate_tuple_impl :
33     public generate_tuple_impl<Counter - 1, Functor, Generator, typename Generator::template type<Functor, Counter - 1>, Fs...> {};
34 
35 template<template <std::size_t> class Functor, class Generator, typename... Fs>
36 struct generate_tuple_impl <0, Functor, Generator, Fs...> {
37     using type = std::tuple<Fs...>;
38 };
39 
40 template<std::size_t Size, template <std::size_t> class Functor, class Generator>
41 using generate_tuple = typename generate_tuple_impl<Size, Functor, Generator>::type;
42 
43 // Defines how generate_tuple should propagate Counter to the tuple members
44 struct generator {
45     template <template <std::size_t> class Functor, std::size_t NonTypeArg>
46     using type = Functor<NonTypeArg>;
47 };
48 
49 template <std::size_t Fixed>
50 struct fixed_generator {
51     template <template <std::size_t> class Functor, std::size_t NonTypeArg>
52     using type = Functor<Fixed>;
53 };
54 
55 template<std::size_t LevelTaskCount, std::size_t MaxDepth, std::size_t WorkSize>
56 struct invoke_tree {
57     struct invoke_tree_leaf {
58         static constexpr std::size_t size = WorkSize;
59         void operator()() const { utils::doDummyWork(size); }
60     };
61 
62     // Make invoke_tree_leaf composable with generate_tuple interface
63     template <std::size_t NonTypeArg>
64     using generating_leaf_functor = invoke_tree_leaf;
65 
66     template<std::size_t CurrentDepth>
67     struct invoke_tree_node {
68         template<typename... Fs>
69         void invoker(const std::tuple<Fs...>&) const {
70             tbb::parallel_invoke(Fs()...);
71         }
72 
73         // Template alias to workaround the strange Visual Studio issue
74         template<std::size_t Depth>
75         using generating_node_functor = invoke_tree_node<Depth>;
76 
77         void create_level(/*is_final_level*/std::false_type) const {
78             invoker(generate_tuple<LevelTaskCount, generating_node_functor, fixed_generator<CurrentDepth + 1>>{});
79         }
80 
81         void create_level(/*is_final_level*/std::true_type) const {
82             invoker(generate_tuple<LevelTaskCount, generating_leaf_functor, generator>());
83         }
84 
85         void operator()() const {
86             create_level(/*is_final_level*/std::integral_constant<bool, CurrentDepth == MaxDepth>());
87         }
88     };
89 
90     static void generate_and_run() {
91         invoke_tree_node<1> tree_root;
92         tree_root();
93     }
94 };
95 
96 template<std::size_t TaskCount, template<std::size_t> class Functor>
97 class parallel_invoke_call {
98     template<typename... Fs>
99     static void invoke_tuple_args(tbb::task_group_context* context_ptr, const std::tuple<Fs...>&) {
100         if (context_ptr != nullptr) {
101             tbb::parallel_invoke(Fs()..., *context_ptr);
102         } else {
103             tbb::parallel_invoke(Fs()...);
104         }
105     }
106 
107 public:
108     static void perform(tbb::task_group_context* context_ptr = nullptr) {
109         invoke_tuple_args(context_ptr, generate_tuple<TaskCount, Functor, generator>{});
110     }
111 };
112 
113 #endif // __TBB_test_common_parallel_invoke_common_H
114