1 /*
2     Copyright (c) 2005-2022 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_concurrent_lru_cache_common
18 #define __TBB_test_common_concurrent_lru_cache_common
19 
20 
21 #include "test.h"
22 #include "utils.h"
23 #include <tbb/concurrent_lru_cache.h>
24 
25 //-----------------------------------------------------------------------------
26 // Concurrent LRU Cache Tests: Cache Helpers
27 //-----------------------------------------------------------------------------
28 
29 namespace concurrent_lru_cache_helpers {
30 
31     // call counter
32 
33     template<std::size_t id>
34     struct tag{};
35 
36     template<typename Tag, typename Type>
37     struct call_counter {
38         static int calls;
39 
callcall_counter40         static Type call(Type t) {
41             ++calls;
42             return t;
43         }
44     };
45 
46     template<typename Tag, typename Type>
47     int call_counter<Tag, Type>::calls = 0;
48 
49     // cloner
50 
51     template<typename ValueType>
52     struct cloner {
53         ValueType& my_ref_to_original;
54 
clonercloner55         cloner(ValueType& ref_to_original) : my_ref_to_original(ref_to_original) {}
56 
57         template<typename KeyType>
operatorcloner58         ValueType operator()(KeyType) const { return my_ref_to_original; }
59     };
60 
61     // map searcher
62 
63     template <typename KeyType, typename ValueType>
64     struct map_searcher {
65         using map_type = std::map<KeyType, ValueType>;
66         using map_iter = typename map_type::iterator;
67 
68         map_type& my_map_ref;
69 
map_searchermap_searcher70         map_searcher(map_type& map_ref) : my_map_ref(map_ref) {}
71 
operatormap_searcher72         ValueType& operator()(KeyType k) {
73             map_iter it = my_map_ref.find(k);
74             if (it == my_map_ref.end())
75                 it = my_map_ref.insert(it, std::make_pair(k, ValueType()));
76 
77             return it->second;
78         }
79     };
80 
81     // array searcher
82 
83     template<typename key_type, typename value_type, std::size_t array_size>
84     struct array_searcher {
85         using array_type = value_type[array_size];
86 
87         array_type const& my_array_ref;
88 
array_searcherarray_searcher89         array_searcher(array_type const& array_ref) : my_array_ref(array_ref) {}
90 
operatorarray_searcher91         const value_type& operator()(key_type k) const {
92             std::size_t index = k;
93             REQUIRE_MESSAGE(k < array_size, "incorrect test setup");
94             return my_array_ref[index];
95         }
96     };
97 
98     // instance counter
99 
100     template<typename CounterType = std::size_t>
101     struct instance_counter {
102         CounterType* my_p_count;
103 
instance_counterinstance_counter104         instance_counter() : my_p_count(new CounterType) {
105             *my_p_count = 1;
106         }
107 
instance_counterinstance_counter108         instance_counter(instance_counter const& other) : my_p_count(other.my_p_count) {
109             ++(*my_p_count);
110         }
111 
112         instance_counter& operator=(instance_counter other) {
113             std::swap(this->my_p_count, other.my_p_count);
114             return *this;
115         }
116 
~instance_counterinstance_counter117         ~instance_counter() {
118             bool is_last = ! --(*my_p_count);
119 #if __GNUC__ == 12
120         // GCC 12 warns about using my_p_count after delete.
121         // The test was investigated and no problems were detected
122         // The following statement silence the warning
123         static bool unused_is_last = is_last;
124         utils::suppress_unused_warning(unused_is_last);
125 #endif
126             if (is_last)
127                 delete(my_p_count);
128         }
instances_countinstance_counter129         std::size_t instances_count() const { return *my_p_count; }
130     };
131 
132     using instance_serial_counter = instance_counter<>;
133     using instance_concurrent_counter = instance_counter<std::atomic<std::size_t>>;
134 };
135 
136 //-----------------------------------------------------------------------------
137 // Concurrent LRU Cache Tests: Cache Presets
138 //-----------------------------------------------------------------------------
139 
140 namespace concurrent_lru_cache_presets {
141 
142     namespace helpers = concurrent_lru_cache_helpers;
143 
144     // base preset with common typedefs and fields
145 
146     template<typename... CacheTypes>
147     struct preset_base {
148         using cache_type = tbb::concurrent_lru_cache<CacheTypes...>;
149         using handle_type = typename cache_type::handle;
150 
preset_basepreset_base151         preset_base() {}
152         preset_base(const preset_base&) = delete;
153         preset_base(preset_base&&) = delete;
154         preset_base& operator=(const preset_base&) = delete;
155         preset_base& operator=(preset_base&&) = delete;
156     };
157 
158     // default preset
159 
160     template<typename Key, typename Value>
161     struct preset_default : preset_base<Key, Value> {
162         using cache_type = typename preset_base<Key, Value>::cache_type;
163         using handle_type = typename cache_type::handle;
164         using callback_type = typename cache_type::value_function_type;
165 
166         const std::size_t number_of_lru_history_items;
167         cache_type cache;
168 
preset_defaultpreset_default169         preset_default(callback_type callback, std::size_t history_items) :
170             number_of_lru_history_items(history_items),
171             cache(callback, number_of_lru_history_items) {};
172     };
173 
174     // preset1
175 
176     struct preset1 : preset_base<std::string, std::string> {
177         const std::size_t number_of_lru_history_items;
178         cache_type cache;
179         handle_type default_ctor_check;
180 
callbackpreset1181         static std::string callback(std::string key) { return key; }
182 
preset1preset1183         preset1() :
184             number_of_lru_history_items(1),
185             cache(&callback, number_of_lru_history_items) {};
186     };
187 
188     // preset for call counting
189 
190     template<std::size_t tag_id>
191     struct preset_call_count : preset_base<int, int> {
192         using cache_miss_tag = helpers::tag<tag_id>;
193         using counter_type = helpers::call_counter<cache_miss_tag, int>;
194 
195         const std::size_t number_of_lru_history_items;
196         cache_type cache;
197 
preset_call_countpreset_call_count198         preset_call_count() :
199             number_of_lru_history_items(8),
200             cache(&counter_type::call, number_of_lru_history_items) {}
201     };
202 
203     // preset for instance counting
204 
205     struct preset_instance_count : preset_base<
206         std::size_t, helpers::instance_serial_counter,
207         helpers::cloner<helpers::instance_serial_counter>> {
208 
209         using cloner_type = helpers::cloner<helpers::instance_serial_counter>;
210 
211         helpers::instance_serial_counter source;
212         cloner_type cloner;
213         const std::size_t number_of_lru_history_items;
214         cache_type cache;
215 
preset_instance_countpreset_instance_count216         preset_instance_count() :
217             cloner(source),
218             number_of_lru_history_items(8),
219             cache(cloner, number_of_lru_history_items) {}
220     };
221 
222     // preset for instance counting with external map
223 
224     struct preset_map_instance_count : preset_base<
225         std::size_t, helpers::instance_serial_counter,
226         helpers::map_searcher<std::size_t, helpers::instance_serial_counter>> {
227 
228         using map_searcher_type = helpers::map_searcher<std::size_t, helpers::instance_serial_counter>;
229         using objects_map_type = map_searcher_type::map_type;
230 
231         static const std::size_t number_of_lru_history_items;
232         map_searcher_type::map_type objects_map;
233         cache_type cache;
234 
preset_map_instance_countpreset_map_instance_count235         preset_map_instance_count() :
236             cache(map_searcher_type(objects_map), number_of_lru_history_items) {}
237 
is_evictedpreset_map_instance_count238         bool is_evicted(std::size_t key) {
239             objects_map_type::iterator it = objects_map.find(key);
240 
241             REQUIRE_MESSAGE(
242                 it != objects_map.end(),
243                 "no value for key - error in test logic ?");
244 
245             return it->second.instances_count() == 1;
246         }
247 
fill_up_cachepreset_map_instance_count248         void fill_up_cache(std::size_t lower_bound, std::size_t upper_bound) {
249             for (std::size_t i = lower_bound; i < upper_bound; ++i)
250                 cache[i];
251         }
252     };
253 
254     const std::size_t preset_map_instance_count::number_of_lru_history_items = 8;
255 };
256 
257 #endif // __TBB_test_common_concurrent_lru_cache_common
258