1 /* 2 * Copyright (c) 2025 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 ECMASCRIPT_MODULE_MANAGER_MAP_H 17 #define ECMASCRIPT_MODULE_MANAGER_MAP_H 18 19 #include <optional> 20 #include "ecmascript/mem/gc_root.h" 21 #include "ecmascript/mem/c_containers.h" 22 23 namespace panda::ecmascript { 24 /** 25 * A concurrent-safe hash map for use as a GC root container. 26 * All stored values are wrapped in GCRoot with CMCGC. 27 * 28 * Safe for concurrent access between GC threads and mutator threads 29 * 30 * IMPORTANT: Do not attempt to cache iterators or references outside 31 * of the provided interface - they can become invalid after the lock is released. 32 */ 33 template <class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>> 34 class ModuleManagerMap { 35 using UnderlyingMap = CUnorderedMap<Key, GCRoot, Hash, KeyEqual>; 36 mutable std::mutex lock_; 37 // Underlying storage with GCRoot values 38 UnderlyingMap map_; 39 40 public: 41 /** 42 * Inserts or updates a key-value pair. 43 * If the key already exists, replaces the existing value. 44 */ Insert(const Key & key,JSTaggedValue value)45 void Insert(const Key &key, JSTaggedValue value) 46 { 47 std::lock_guard<std::mutex> lock(lock_); 48 map_[key] = GCRoot(value); 49 } 50 51 /** 52 * Inserts a key-value pair only if the key doesn't exist. 53 * If the key already exists, the operation is a no-op. 54 * The try_emplace ensure a GCRoot constructor is only trigger 55 * if the insertion actually happen 56 * 57 * Note: Returns bool instead of <iterator,bool> for thread safety. 58 * Exposing iterators would be unsafe as they become invalid 59 * once the lock is released. 60 */ 61 template <typename K> Emplace(const K & key,JSTaggedValue value)62 bool Emplace(const K &key, JSTaggedValue value) 63 { 64 std::lock_guard<std::mutex> lock(lock_); 65 return map_.try_emplace(key, value).second; 66 } 67 68 /** 69 * Safely retrieves a value by key with readbarrier. 70 * 71 * The returned value is obtained via GCRoot::Read(), ensuring proper 72 * read barriers are applied for concurrent GC safety. The value is 73 * returned by copy to avoid dangling references after lock release. 74 */ 75 template <typename K> Find(const K & key)76 std::optional<JSTaggedValue> Find(const K &key) 77 { 78 std::lock_guard<std::mutex> lock(lock_); 79 auto it = map_.find(key); 80 return it == map_.end() ? std::nullopt : std::make_optional(it->second.Read()); 81 } 82 83 /** 84 * Applies a function to each key-value pair while holding the lock. 85 */ 86 template <typename Func> ForEach(Func && fn)87 void ForEach(Func &&fn) 88 { 89 std::lock_guard<std::mutex> lock(lock_); 90 for (auto it = map_.begin(); it != map_.end(); ++it) { 91 fn(it); 92 } 93 } 94 Erase(const Key & key)95 void Erase(const Key &key) 96 { 97 std::lock_guard<std::mutex> lock(lock_); 98 map_.erase(key); 99 } 100 Size()101 size_t Size() const 102 { 103 std::lock_guard<std::mutex> lock(lock_); 104 return map_.size(); 105 } 106 Clear()107 void Clear() 108 { 109 std::lock_guard<std::mutex> lock(lock_); 110 map_.clear(); 111 } 112 }; 113 114 } // namespace panda::ecmascript 115 #endif 116