1 /* 2 * Copyright (c) 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 OHOS_DISTRIBUTED_DATA_FRAMEWORKS_COMMON_CONCURRENT_MAP_H 17 #define OHOS_DISTRIBUTED_DATA_FRAMEWORKS_COMMON_CONCURRENT_MAP_H 18 #include <functional> 19 #include <map> 20 #include <shared_mutex> 21 22 namespace OHOS { 23 template<typename _Key, typename _Tp> 24 class ConcurrentMap { 25 template<typename _First, typename... _Rest> 26 static _First First(); 27 28 public: 29 using map_type = typename std::map<_Key, _Tp>; 30 using filter_type = typename std::function<bool(map_type &)>; 31 using key_type = typename std::map<_Key, _Tp>::key_type; 32 using mapped_type = typename std::map<_Key, _Tp>::mapped_type; 33 using value_type = typename std::map<_Key, _Tp>::value_type; 34 using size_type = typename std::map<_Key, _Tp>::size_type; 35 using reference = typename std::map<_Key, _Tp>::reference; 36 using const_reference = typename std::map<_Key, _Tp>::const_reference; 37 38 ConcurrentMap() = default; ~ConcurrentMap()39 ~ConcurrentMap() 40 { 41 Clear(); 42 } 43 ConcurrentMap(const ConcurrentMap & other)44 ConcurrentMap(const ConcurrentMap &other) 45 { 46 operator=(std::move(other)); 47 } 48 ConcurrentMap(std::map<_Key,_Tp> && other)49 ConcurrentMap(std::map<_Key, _Tp> &&other) noexcept 50 { 51 operator=(std::move(other)); 52 } 53 54 ConcurrentMap &operator=(std::map<_Key, _Tp> &&other) noexcept 55 { 56 if (&entries_ == &other) { 57 return *this; 58 } 59 std::unique_lock<decltype(mutex_)> lock(mutex_); 60 entries_ = std::move(other); 61 return *this; 62 } 63 64 ConcurrentMap &operator=(const ConcurrentMap &other) noexcept 65 { 66 if (this == &other) { 67 return *this; 68 } 69 auto tmp = other.Clone(); 70 std::unique_lock<decltype(mutex_)> lock(mutex_); 71 entries_ = std::move(tmp); 72 return *this; 73 } 74 ConcurrentMap(ConcurrentMap && other)75 ConcurrentMap(ConcurrentMap &&other) noexcept 76 { 77 operator=(std::move(other)); 78 } 79 80 ConcurrentMap &operator=(ConcurrentMap &&other) noexcept 81 { 82 if (this == &other) { 83 return *this; 84 } 85 auto tmp = other.Steal(); 86 std::unique_lock<decltype(mutex_)> lock(mutex_); 87 entries_ = std::move(tmp); 88 return *this; 89 } 90 Emplace()91 bool Emplace() noexcept 92 { 93 std::unique_lock<decltype(mutex_)> lock(mutex_); 94 auto it = entries_.emplace(); 95 return it.second; 96 } 97 98 template<typename... _Args> 99 typename std::enable_if<!std::is_convertible_v<decltype(First<_Args...>()), filter_type>, bool>::type 100 Emplace(_Args &&...args) noexcept 101 { 102 std::unique_lock<decltype(mutex_)> lock(mutex_); 103 auto it = entries_.emplace(std::forward<_Args>(args)...); 104 return it.second; 105 } 106 107 template<typename _Filter, typename... _Args> 108 typename std::enable_if<std::is_convertible_v<_Filter, filter_type>, bool>::type Emplace(const _Filter & filter,_Args &&...args)109 Emplace(const _Filter &filter, _Args &&...args) noexcept 110 { 111 std::unique_lock<decltype(mutex_)> lock(mutex_); 112 if (!filter(entries_)) { 113 return false; 114 } 115 auto it = entries_.emplace(std::forward<_Args>(args)...); 116 return it.second; 117 } 118 Find(const key_type & key)119 std::pair<bool, mapped_type> Find(const key_type &key) const noexcept 120 { 121 std::shared_lock<decltype(mutex_)> lock(mutex_); 122 auto it = entries_.find(key); 123 if (it == entries_.end()) { 124 return std::pair { false, mapped_type() }; 125 } 126 127 return std::pair { true, it->second }; 128 } 129 ContainIf(const key_type & key,const std::function<bool (const mapped_type & value)> & action)130 bool ContainIf(const key_type &key, const std::function<bool(const mapped_type &value)> &action) const noexcept 131 { 132 std::shared_lock<decltype(mutex_)> lock(mutex_); 133 auto it = entries_.find(key); 134 if (it == entries_.end()) { 135 return false; 136 } 137 if (action) { 138 return action(it->second); 139 } 140 return true; 141 } 142 Contains(const key_type & key)143 bool Contains(const key_type &key) const noexcept 144 { 145 std::shared_lock<decltype(mutex_)> lock(mutex_); 146 return (entries_.find(key) != entries_.end()); 147 } 148 149 template <typename _Obj> InsertOrAssign(const key_type & key,_Obj && obj)150 bool InsertOrAssign(const key_type &key, _Obj &&obj) noexcept 151 { 152 std::unique_lock<decltype(mutex_)> lock(mutex_); 153 auto it = entries_.insert_or_assign(key, std::forward<_Obj>(obj)); 154 return it.second; 155 } 156 Insert(const key_type & key,const mapped_type & value)157 bool Insert(const key_type &key, const mapped_type &value) noexcept 158 { 159 std::unique_lock<decltype(mutex_)> lock(mutex_); 160 auto it = entries_.insert(value_type { key, value }); 161 return it.second; 162 } 163 Erase(const key_type & key)164 size_type Erase(const key_type &key) noexcept 165 { 166 std::unique_lock<decltype(mutex_)> lock(mutex_); 167 return entries_.erase(key); 168 } 169 EraseIf(const key_type & key,const std::function<bool (const key_type & key,mapped_type & value)> & action)170 size_type EraseIf(const key_type &key, 171 const std::function<bool(const key_type &key, mapped_type &value)> &action) noexcept 172 { 173 if (action == nullptr) { 174 return 0; 175 } 176 std::unique_lock<decltype(mutex_)> lock(mutex_); 177 auto it = entries_.find(key); 178 if (it == entries_.end() || !action((*it).first, (*it).second)) { 179 return 0; 180 } 181 return entries_.erase(key); 182 } 183 Clear()184 void Clear() noexcept 185 { 186 std::unique_lock<decltype(mutex_)> lock(mutex_); 187 return entries_.clear(); 188 } 189 Empty()190 bool Empty() const noexcept 191 { 192 std::shared_lock<decltype(mutex_)> lock(mutex_); 193 return entries_.empty(); 194 } 195 Size()196 size_type Size() const noexcept 197 { 198 std::shared_lock<decltype(mutex_)> lock(mutex_); 199 return entries_.size(); 200 } 201 202 // The action`s return true means meeting the erase condition 203 // The action`s return false means not meeting the erase condition EraseIf(const std::function<bool (const key_type & key,mapped_type & value)> & action)204 size_type EraseIf(const std::function<bool(const key_type &key, mapped_type &value)> &action) noexcept 205 { 206 if (action == nullptr) { 207 return 0; 208 } 209 std::unique_lock<decltype(mutex_)> lock(mutex_); 210 #if __cplusplus > 201703L 211 auto count = std::erase_if(entries_, 212 [&action](value_type &value) -> bool { return action(value.first, value.second); }); 213 #else 214 auto count = entries_.size(); 215 for (auto it = entries_.begin(); it != entries_.end();) { 216 if (action((*it).first, (*it).second)) { 217 it = entries_.erase(it); 218 } else { 219 ++it; 220 } 221 } 222 count -= entries_.size(); 223 #endif 224 return count; 225 } 226 ForEach(const std::function<bool (const key_type &,mapped_type &)> & action)227 void ForEach(const std::function<bool(const key_type &, mapped_type &)> &action) 228 { 229 if (action == nullptr) { 230 return; 231 } 232 std::shared_lock<decltype(mutex_)> lock(mutex_); 233 for (auto &[key, value] : entries_) { 234 if (action(key, value)) { 235 break; 236 } 237 } 238 } 239 ForEachCopies(const std::function<bool (const key_type &,mapped_type &)> & action)240 void ForEachCopies(const std::function<bool(const key_type &, mapped_type &)> &action) 241 { 242 if (action == nullptr) { 243 return; 244 } 245 auto entries = Clone(); 246 for (auto &[key, value] : entries) { 247 if (action(key, value)) { 248 break; 249 } 250 } 251 } 252 253 // The action's return value means that the element is keep in map or not; true means keeping, false means removing. Compute(const key_type & key,const std::function<bool (const key_type &,mapped_type &)> & action)254 bool Compute(const key_type &key, const std::function<bool(const key_type &, mapped_type &)> &action) 255 { 256 if (action == nullptr) { 257 return false; 258 } 259 std::unique_lock<decltype(mutex_)> lock(mutex_); 260 auto it = entries_.find(key); 261 if (it == entries_.end()) { 262 auto result = entries_.emplace(key, mapped_type()); 263 it = result.second ? result.first : entries_.end(); 264 } 265 if (it == entries_.end()) { 266 return false; 267 } 268 if (!action(it->first, it->second)) { 269 entries_.erase(key); 270 } 271 return true; 272 } 273 274 // The action's return value means that the element is keep in map or not; true means keeping, false means removing. ComputeIfPresent(const key_type & key,const std::function<bool (const key_type &,mapped_type &)> & action)275 bool ComputeIfPresent(const key_type &key, const std::function<bool(const key_type &, mapped_type &)> &action) 276 { 277 if (action == nullptr) { 278 return false; 279 } 280 std::unique_lock<decltype(mutex_)> lock(mutex_); 281 auto it = entries_.find(key); 282 if (it == entries_.end()) { 283 return false; 284 } 285 if (!action(key, it->second)) { 286 entries_.erase(key); 287 } 288 return true; 289 } 290 ComputeIfAbsent(const key_type & key,const std::function<mapped_type (const key_type &)> & action)291 bool ComputeIfAbsent(const key_type &key, const std::function<mapped_type(const key_type &)> &action) 292 { 293 if (action == nullptr) { 294 return false; 295 } 296 std::unique_lock<decltype(mutex_)> lock(mutex_); 297 auto it = entries_.find(key); 298 if (it != entries_.end()) { 299 return false; 300 } 301 entries_.emplace(key, action(key)); 302 return true; 303 } 304 DoActionIfEmpty(const std::function<void (void)> & action)305 void DoActionIfEmpty(const std::function<void(void)> &action) 306 { 307 if (action == nullptr) { 308 return; 309 } 310 std::shared_lock<decltype(mutex_)> lock(mutex_); 311 if (entries_.empty()) { 312 action(); 313 } 314 return; 315 } 316 DoActionWhenClone(const std::function<void (const std::map<_Key,_Tp> &)> & action)317 void DoActionWhenClone(const std::function<void(const std::map<_Key, _Tp> &)> &action) 318 { 319 if (action == nullptr) { 320 return; 321 } 322 auto tmp = Clone(); 323 std::shared_lock<decltype(mutex_)> lock(mutex_); 324 action(tmp); 325 return; 326 } 327 Clone()328 std::map<_Key, _Tp> Clone() const noexcept 329 { 330 std::shared_lock<decltype(mutex_)> lock(mutex_); 331 return entries_; 332 } 333 334 private: Steal()335 std::map<_Key, _Tp> Steal() noexcept 336 { 337 std::lock_guard<decltype(mutex_)> lock(mutex_); 338 return std::move(entries_); 339 } 340 341 private: 342 mutable std::shared_mutex mutex_; 343 std::map<_Key, _Tp> entries_; 344 }; 345 } // namespace OHOS 346 #endif // OHOS_DISTRIBUTED_DATA_FRAMEWORKS_COMMON_CONCURRENT_MAP_H 347