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 #define TBB_PREVIEW_CONCURRENT_LRU_CACHE 1
18 
19 #if __INTEL_COMPILER && _MSC_VER
20 #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
21 #endif
22 
23 #include "common/test.h"
24 #include "common/utils.h"
25 #include <tbb/concurrent_lru_cache.h>
26 #include <common/concurrent_lru_cache_common.h>
27 
28 //! \file test_concurrent_lru_cache.cpp
29 //! \brief Test for [preview] functionality
30 
31 //-----------------------------------------------------------------------------
32 // Concurrent LRU Cache Tests: Cache Test Cases
33 //-----------------------------------------------------------------------------
34 
35 //! \brief \ref error_guessing
36 TEST_CASE("basic test for return value") {
37     using preset = concurrent_lru_cache_presets::preset_default<int, int>;
38 
__anoncd5487920102(int ) 39     auto dummy_f = [](int /*key*/) -> int { return 0xDEADBEEF; };
40     std::size_t number_of_lru_history_items = 8;
41 
42     preset preset_object{dummy_f, number_of_lru_history_items};
43     preset::cache_type& cache = preset_object.cache;
44 
45     int dummy_key = 1;
46     REQUIRE_MESSAGE(
47         dummy_f(dummy_key) == cache[dummy_key].value(),
48         "cache operator() must return only values obtained from value function");
49 }
50 
51 //! \brief \ref error_guessing
52 TEST_CASE("basic test for unused objects") {
53     using preset = concurrent_lru_cache_presets::preset_instance_count;
54     preset preset_object{};
55 
56     for (std::size_t i = 0; i < preset_object.number_of_lru_history_items; ++i)
57         preset_object.cache[i];
58 
59     REQUIRE_MESSAGE(
60         preset_object.source.instances_count() > 1,
61         "cache should store some unused objects");
62 }
63 
64 //! \brief \ref error_guessing
65 TEST_CASE("basic test for unused object limit") {
66     using preset = concurrent_lru_cache_presets::preset_instance_count;
67     preset preset_object{};
68 
69     for (std::size_t i = 0; i < preset_object.number_of_lru_history_items + 1; ++i)
70         preset_object.cache[i];
71 
72     REQUIRE_MESSAGE(
73         preset_object.source.instances_count() == preset_object.number_of_lru_history_items + 1,
74         "cache should respect number of stored unused objects to number passed in constructor");
75 }
76 
77 //! \brief \ref error_guessing
78 TEST_CASE("basic test for eviction order") {
79     using preset = concurrent_lru_cache_presets::preset_map_instance_count;
80     preset preset_object{};
81 
82     REQUIRE_MESSAGE(
83         preset_object.number_of_lru_history_items > 2,
84         "incorrect test setup");
85 
86     preset_object.fill_up_cache(0, preset_object.number_of_lru_history_items);
87 
88     // heat up first element
89     preset_object.cache[0];
90 
91     // cause eviction;
92     preset_object.cache[preset_object.number_of_lru_history_items];
93 
94     bool is_correct = preset_object.is_evicted(1) && !preset_object.is_evicted(0);
95     REQUIRE_MESSAGE(is_correct, "cache should evict items in lru order");
96 }
97 
98 //! \brief \ref error_guessing
99 TEST_CASE("basic test for eviction of only unused items") {
100     using preset = concurrent_lru_cache_presets::preset_map_instance_count;
101     preset preset_object{};
102 
103     preset::handle_type h = preset_object.cache[0];
104 
105     //cause eviction
106     preset_object.fill_up_cache(1, preset_object.number_of_lru_history_items+2);
107 
108     bool is_correct = preset_object.is_evicted(1) && !preset_object.is_evicted(0);
109     REQUIRE_MESSAGE(is_correct, "cache should not evict items in use");
110 }
111 
112 //! \brief \ref error_guessing
113 TEST_CASE("basic test for eviction of only unused items 2") {
114     using preset = concurrent_lru_cache_presets::preset_map_instance_count;
115     preset preset_object{};
116 
117     preset::handle_type h = preset_object.cache[0];
118     {
119         preset::handle_type h1 = preset_object.cache[0];
120     }
121 
122     //cause eviction
123     preset_object.fill_up_cache(1,preset_object.number_of_lru_history_items+2);
124 
125     bool is_correct = preset_object.is_evicted(1) && !preset_object.is_evicted(0);
126     REQUIRE_MESSAGE(is_correct, "cache should not evict items in use");
127 }
128 
129 //! \brief \ref error_guessing
130 TEST_CASE("basic test for handling case when number_of_lru_history_items is zero") {
__anoncd5487920202(int) 131     auto foo = [] (int) {
132         return utils::LifeTrackableObject{};
133     };
134     using cache_type =  tbb::concurrent_lru_cache<int, utils::LifeTrackableObject, decltype(foo)>;
135     cache_type cache{foo, 0};
136 
137     for(int i = 0; i < 10; ++i) {
138         // Check that no history is stored when my_history_list_capacity is 0.
139         // In this case, when trying to fill the cache, the items will be deleted if reference was not taken.
140         const utils::LifeTrackableObject* obj_addr = &cache[1].value();
141         REQUIRE_MESSAGE(utils::LifeTrackableObject::is_alive(obj_addr) == false, "when number_of_lru_history_items is zero, element must be erased after use");
142     }
143 
144     cache_type::handle h = cache[1];
145     const utils::LifeTrackableObject* obj_addr = &h.value();
146     auto& object_set = utils::LifeTrackableObject::set();
147     for(int i = 0; i < 10; ++i) {
148         // Verify that item will still be alive if there is a handle holding that item.
149         cache[1];
150         REQUIRE_MESSAGE(utils::LifeTrackableObject::is_alive(obj_addr), "the object with the key=1 was destroyed but should not");
151         REQUIRE_MESSAGE(object_set.size() == 1, "no other values should be added");
152     }
153 }
154