1 /** 2 * Copyright (c) 2021-2022 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 panda { 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 : METHOD_CACHE_SIZE(DEFAULT_METHOD_CACHE_SIZE), 54 FIELD_CACHE_SIZE(DEFAULT_FIELD_CACHE_SIZE), 55 CLASS_CACHE_SIZE(DEFAULT_CLASS_CACHE_SIZE) 56 { 57 method_cache_.resize(METHOD_CACHE_SIZE, MethodCachePair()); 58 field_cache_.resize(FIELD_CACHE_SIZE, FieldCachePair()); 59 class_cache_.resize(CLASS_CACHE_SIZE, ClassCachePair()); 60 } 61 62 ~PandaCache() = default; 63 GetMethodIndex(File::EntityId id)64 inline uint32_t GetMethodIndex(File::EntityId id) const 65 { 66 return panda::helpers::math::PowerOfTwoTableSlot(id.GetOffset(), METHOD_CACHE_SIZE); 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 panda::helpers::math::PowerOfTwoTableSlot(id.GetOffset(), FIELD_CACHE_SIZE, 2U); 73 } 74 GetClassIndex(File::EntityId id)75 inline uint32_t GetClassIndex(File::EntityId id) const 76 { 77 return panda::helpers::math::PowerOfTwoTableSlot(id.GetOffset(), CLASS_CACHE_SIZE); 78 } 79 GetMethodFromCache(File::EntityId id)80 inline Method *GetMethodFromCache(File::EntityId id) const 81 { 82 uint32_t index = GetMethodIndex(id); 83 auto *pair_ptr = 84 reinterpret_cast<std::atomic<MethodCachePair> *>(reinterpret_cast<uintptr_t>(&(method_cache_[index]))); 85 // Atomic with acquire order reason: fixes a data race with method_cache_ 86 auto pair = pair_ptr->load(std::memory_order_acquire); 87 TSAN_ANNOTATE_HAPPENS_AFTER(pair_ptr); 88 if (pair.id_ == id) { 89 return pair.ptr_; 90 } 91 return nullptr; 92 } 93 SetMethodCache(File::EntityId id,Method * method)94 inline void SetMethodCache(File::EntityId id, Method *method) 95 { 96 MethodCachePair pair; 97 pair.id_ = id; 98 pair.ptr_ = method; 99 uint32_t index = GetMethodIndex(id); 100 auto *pair_ptr = 101 reinterpret_cast<std::atomic<MethodCachePair> *>(reinterpret_cast<uintptr_t>(&(method_cache_[index]))); 102 TSAN_ANNOTATE_HAPPENS_BEFORE(pair_ptr); 103 // Atomic with release order reason: fixes a data race with method_cache_ 104 pair_ptr->store(pair, std::memory_order_release); 105 } 106 GetFieldFromCache(File::EntityId id)107 inline Field *GetFieldFromCache(File::EntityId id) const 108 { 109 uint32_t index = GetFieldIndex(id); 110 auto *pair_ptr = 111 reinterpret_cast<std::atomic<FieldCachePair> *>(reinterpret_cast<uintptr_t>(&(field_cache_[index]))); 112 // Atomic with acquire order reason: fixes a data race with field_cache_ 113 auto pair = pair_ptr->load(std::memory_order_acquire); 114 TSAN_ANNOTATE_HAPPENS_AFTER(pair_ptr); 115 if (pair.id_ == id) { 116 return pair.ptr_; 117 } 118 return nullptr; 119 } 120 SetFieldCache(File::EntityId id,Field * field)121 inline void SetFieldCache(File::EntityId id, Field *field) 122 { 123 uint32_t index = GetFieldIndex(id); 124 auto *pair_ptr = 125 reinterpret_cast<std::atomic<FieldCachePair> *>(reinterpret_cast<uintptr_t>(&(field_cache_[index]))); 126 FieldCachePair pair; 127 pair.id_ = id; 128 pair.ptr_ = field; 129 TSAN_ANNOTATE_HAPPENS_BEFORE(pair_ptr); 130 // Atomic with release order reason: fixes a data race with field_cache_ 131 pair_ptr->store(pair, std::memory_order_release); 132 } 133 GetClassFromCache(File::EntityId id)134 inline Class *GetClassFromCache(File::EntityId id) const 135 { 136 uint32_t index = GetClassIndex(id); 137 auto *pair_ptr = 138 reinterpret_cast<std::atomic<ClassCachePair> *>(reinterpret_cast<uintptr_t>(&(class_cache_[index]))); 139 // Atomic with acquire order reason: fixes a data race with class_cache_ 140 auto pair = pair_ptr->load(std::memory_order_acquire); 141 TSAN_ANNOTATE_HAPPENS_AFTER(pair_ptr); 142 if (pair.id_ == id) { 143 return pair.ptr_; 144 } 145 return nullptr; 146 } 147 SetClassCache(File::EntityId id,Class * clazz)148 inline void SetClassCache(File::EntityId id, Class *clazz) 149 { 150 ClassCachePair pair; 151 pair.id_ = id; 152 pair.ptr_ = clazz; 153 uint32_t index = GetClassIndex(id); 154 auto *pair_ptr = 155 reinterpret_cast<std::atomic<ClassCachePair> *>(reinterpret_cast<uintptr_t>(&(class_cache_[index]))); 156 TSAN_ANNOTATE_HAPPENS_BEFORE(pair_ptr); 157 // Atomic with release order reason: fixes a data race with class_cache_ 158 pair_ptr->store(pair, std::memory_order_release); 159 } 160 Clear()161 inline void Clear() 162 { 163 method_cache_.clear(); 164 field_cache_.clear(); 165 class_cache_.clear(); 166 167 method_cache_.resize(METHOD_CACHE_SIZE, MethodCachePair()); 168 field_cache_.resize(FIELD_CACHE_SIZE, FieldCachePair()); 169 class_cache_.resize(CLASS_CACHE_SIZE, ClassCachePair()); 170 } 171 172 template <class Callback> EnumerateCachedClasses(const Callback & cb)173 bool EnumerateCachedClasses(const Callback &cb) 174 { 175 for (uint32_t i = 0; i < CLASS_CACHE_SIZE; i++) { 176 auto *pair_ptr = 177 reinterpret_cast<std::atomic<ClassCachePair> *>(reinterpret_cast<uintptr_t>(&(class_cache_[i]))); 178 // Atomic with acquire order reason: fixes a data race with class_cache_ 179 auto pair = pair_ptr->load(std::memory_order_acquire); 180 TSAN_ANNOTATE_HAPPENS_AFTER(pair_ptr); 181 if (pair.ptr_ != nullptr) { 182 if (!cb(pair.ptr_)) { 183 return false; 184 } 185 } 186 } 187 return true; 188 } 189 190 private: 191 static constexpr uint32_t DEFAULT_FIELD_CACHE_SIZE = 1024U; 192 static constexpr uint32_t DEFAULT_METHOD_CACHE_SIZE = 1024U; 193 static constexpr uint32_t DEFAULT_CLASS_CACHE_SIZE = 1024U; 194 static_assert(panda::helpers::math::IsPowerOfTwo(DEFAULT_FIELD_CACHE_SIZE)); 195 static_assert(panda::helpers::math::IsPowerOfTwo(DEFAULT_METHOD_CACHE_SIZE)); 196 static_assert(panda::helpers::math::IsPowerOfTwo(DEFAULT_CLASS_CACHE_SIZE)); 197 198 const uint32_t METHOD_CACHE_SIZE; 199 const uint32_t FIELD_CACHE_SIZE; 200 const uint32_t CLASS_CACHE_SIZE; 201 202 std::vector<MethodCachePair> method_cache_; 203 std::vector<FieldCachePair> field_cache_; 204 std::vector<ClassCachePair> class_cache_; 205 }; 206 207 } // namespace panda_file 208 } // namespace panda 209 210 #endif // LIBPANDAFILE_PANDA_CACHE_H 211