• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef LIBPANDAFILE_PANDA_CACHE_H_
17 #define LIBPANDAFILE_PANDA_CACHE_H_
18 
19 #include "file.h"
20 #include "os/mutex.h"
21 #include "libpandabase/utils/math_helpers.h"
22 
23 #include <atomic>
24 #include <vector>
25 
26 namespace ark {
27 
28 class Method;
29 class Field;
30 class Class;
31 
32 namespace panda_file {
33 
34 // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions)
35 class PandaCache {
36 public:
37     struct MethodCachePair {
38         File::EntityId id;
39         Method *ptr {nullptr};
40     };
41 
42     struct FieldCachePair {
43         File::EntityId id;
44         Field *ptr {nullptr};
45     };
46 
47     struct ClassCachePair {
48         File::EntityId id;
49         Class *ptr {nullptr};
50     };
51 
PandaCache()52     PandaCache()
53         : methodCacheSize_(DEFAULT_METHOD_CACHE_SIZE),
54           fieldCacheSize_(DEFAULT_FIELD_CACHE_SIZE),
55           classCacheSize_(DEFAULT_CLASS_CACHE_SIZE)
56     {
57         methodCache_.resize(methodCacheSize_, MethodCachePair());
58         fieldCache_.resize(fieldCacheSize_, FieldCachePair());
59         classCache_.resize(classCacheSize_, ClassCachePair());
60     }
61 
62     ~PandaCache() = default;
63 
GetMethodIndex(File::EntityId id)64     inline uint32_t GetMethodIndex(File::EntityId id) const
65     {
66         return ark::helpers::math::PowerOfTwoTableSlot(id.GetOffset(), methodCacheSize_);
67     }
68 
GetFieldIndex(File::EntityId id)69     inline uint32_t GetFieldIndex(File::EntityId id) const
70     {
71         // lowest one or two bits is very likely same between different fields
72         return ark::helpers::math::PowerOfTwoTableSlot(id.GetOffset(), fieldCacheSize_, 2U);
73     }
74 
GetClassIndex(File::EntityId id)75     inline uint32_t GetClassIndex(File::EntityId id) const
76     {
77         return ark::helpers::math::PowerOfTwoTableSlot(id.GetOffset(), classCacheSize_);
78     }
79 
GetMethodFromCache(File::EntityId id)80     inline Method *GetMethodFromCache(File::EntityId id) const
81     {
82         // Emulator target doesn't support atomic operations with 128bit structures like MethodCachePair.
83         // Compiler __atomic_load call which is not implemented in emulator target.
84 #ifndef PANDA_TARGET_EMULATOR
85         uint32_t index = GetMethodIndex(id);
86         auto *pairPtr =
87             reinterpret_cast<std::atomic<MethodCachePair> *>(reinterpret_cast<uintptr_t>(&(methodCache_[index])));
88         // Atomic with acquire order reason: fixes a data race with method_cache_
89         auto pair = pairPtr->load(std::memory_order_acquire);
90         TSAN_ANNOTATE_HAPPENS_AFTER(pairPtr);
91         if (pair.id == id) {
92             return pair.ptr;
93         }
94 #endif
95         return nullptr;
96     }
97 
SetMethodCache(File::EntityId id,Method * method)98     inline void SetMethodCache(File::EntityId id, Method *method)
99     {
100         // Emulator target doesn't support atomic operations with 128bit structures like MethodCachePair.
101         // Compiler __atomic_load call which is not implemented in emulator target.
102 #ifndef PANDA_TARGET_EMULATOR
103         MethodCachePair pair;
104         pair.id = id;
105         pair.ptr = method;
106         uint32_t index = GetMethodIndex(id);
107         auto *pairPtr =
108             reinterpret_cast<std::atomic<MethodCachePair> *>(reinterpret_cast<uintptr_t>(&(methodCache_[index])));
109         TSAN_ANNOTATE_HAPPENS_BEFORE(pairPtr);
110         // Atomic with release order reason: fixes a data race with method_cache_
111         pairPtr->store(pair, std::memory_order_release);
112 #endif
113     }
114 
GetFieldFromCache(File::EntityId id)115     inline Field *GetFieldFromCache(File::EntityId id) const
116     {
117         // Emulator target doesn't support atomic operations with 128bit structures like FieldCachePair.
118         // Compiler __atomic_load call which is not implemented in emulator target.
119 #ifndef PANDA_TARGET_EMULATOR
120         uint32_t index = GetFieldIndex(id);
121         auto *pairPtr =
122             reinterpret_cast<std::atomic<FieldCachePair> *>(reinterpret_cast<uintptr_t>(&(fieldCache_[index])));
123         // Atomic with acquire order reason: fixes a data race with field_cache_
124         auto pair = pairPtr->load(std::memory_order_acquire);
125         TSAN_ANNOTATE_HAPPENS_AFTER(pairPtr);
126         if (pair.id == id) {
127             return pair.ptr;
128         }
129 #endif
130         return nullptr;
131     }
132 
SetFieldCache(File::EntityId id,Field * field)133     inline void SetFieldCache(File::EntityId id, Field *field)
134     {
135         // Emulator target doesn't support atomic operations with 128bit structures like FieldCachePair.
136         // Compiler __atomic_load call which is not implemented in emulator target.
137 #ifndef PANDA_TARGET_EMULATOR
138         uint32_t index = GetFieldIndex(id);
139         auto *pairPtr =
140             reinterpret_cast<std::atomic<FieldCachePair> *>(reinterpret_cast<uintptr_t>(&(fieldCache_[index])));
141         FieldCachePair pair;
142         pair.id = id;
143         pair.ptr = field;
144         TSAN_ANNOTATE_HAPPENS_BEFORE(pairPtr);
145         // Atomic with release order reason: fixes a data race with field_cache_
146         pairPtr->store(pair, std::memory_order_release);
147 #endif
148     }
149 
GetClassFromCache(File::EntityId id)150     inline Class *GetClassFromCache(File::EntityId id) const
151     {
152         // Emulator target doesn't support atomic operations with 128bit structures like ClassCachePair.
153         // Compiler __atomic_load call which is not implemented in emulator target.
154 #ifndef PANDA_TARGET_EMULATOR
155         uint32_t index = GetClassIndex(id);
156         auto *pairPtr =
157             reinterpret_cast<std::atomic<ClassCachePair> *>(reinterpret_cast<uintptr_t>(&(classCache_[index])));
158         // Atomic with acquire order reason: fixes a data race with class_cache_
159         auto pair = pairPtr->load(std::memory_order_acquire);
160         TSAN_ANNOTATE_HAPPENS_AFTER(pairPtr);
161         if (pair.id == id) {
162             return pair.ptr;
163         }
164 #endif
165         return nullptr;
166     }
167 
SetClassCache(File::EntityId id,Class * clazz)168     inline void SetClassCache(File::EntityId id, Class *clazz)
169     {
170         // Emulator target doesn't support atomic operations with 128bit structures like ClassCachePair.
171         // Compiler __atomic_load call which is not implemented in emulator target.
172 #ifndef PANDA_TARGET_EMULATOR
173         ClassCachePair pair;
174         pair.id = id;
175         pair.ptr = clazz;
176         uint32_t index = GetClassIndex(id);
177         auto *pairPtr =
178             reinterpret_cast<std::atomic<ClassCachePair> *>(reinterpret_cast<uintptr_t>(&(classCache_[index])));
179         TSAN_ANNOTATE_HAPPENS_BEFORE(pairPtr);
180         // Atomic with release order reason: fixes a data race with class_cache_
181         pairPtr->store(pair, std::memory_order_release);
182 #endif
183     }
184 
Clear()185     inline void Clear()
186     {
187         methodCache_.clear();
188         fieldCache_.clear();
189         classCache_.clear();
190 
191         methodCache_.resize(methodCacheSize_, MethodCachePair());
192         fieldCache_.resize(fieldCacheSize_, FieldCachePair());
193         classCache_.resize(classCacheSize_, ClassCachePair());
194     }
195 
196     template <class Callback>
EnumerateCachedClasses(const Callback & cb)197     bool EnumerateCachedClasses(const Callback &cb)
198     {
199         for (uint32_t i = 0; i < classCacheSize_; i++) {
200             auto *pairPtr =
201                 reinterpret_cast<std::atomic<ClassCachePair> *>(reinterpret_cast<uintptr_t>(&(classCache_[i])));
202             // Atomic with acquire order reason: fixes a data race with class_cache_
203             auto pair = pairPtr->load(std::memory_order_acquire);
204             TSAN_ANNOTATE_HAPPENS_AFTER(pairPtr);
205             if (pair.ptr != nullptr) {
206                 if (!cb(pair.ptr)) {
207                     return false;
208                 }
209             }
210         }
211         return true;
212     }
213 
214 private:
215     static constexpr uint32_t DEFAULT_FIELD_CACHE_SIZE = 1024U;
216     static constexpr uint32_t DEFAULT_METHOD_CACHE_SIZE = 1024U;
217     static constexpr uint32_t DEFAULT_CLASS_CACHE_SIZE = 1024U;
218     static_assert(ark::helpers::math::IsPowerOfTwo(DEFAULT_FIELD_CACHE_SIZE));
219     static_assert(ark::helpers::math::IsPowerOfTwo(DEFAULT_METHOD_CACHE_SIZE));
220     static_assert(ark::helpers::math::IsPowerOfTwo(DEFAULT_CLASS_CACHE_SIZE));
221 
222     const uint32_t methodCacheSize_;
223     const uint32_t fieldCacheSize_;
224     const uint32_t classCacheSize_;
225 
226     std::vector<MethodCachePair> methodCache_;
227     std::vector<FieldCachePair> fieldCache_;
228     std::vector<ClassCachePair> classCache_;
229 };
230 
231 }  // namespace panda_file
232 }  // namespace ark
233 
234 #endif
235