// // Copyright 2026 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // ObjectMap.h: Defines the egl::ObjectMap template class for thread-safe EGL object maps. #ifndef LIBANGLE_OBJECT_MAP_H_ #define LIBANGLE_OBJECT_MAP_H_ #include #include #include "common/FastVector.h" #include "common/PackedEnums.h" #include "common/SimpleMutex.h" #include "common/hash_containers.h" #include "libANGLE/Error.h" namespace egl { namespace priv { template class ContextMapImpl; template class ObjectMap : angle::NonCopyable { public: using Map = angle::HashMap; using ResourceVector = angle::FastVector; ObjectMap() = default; ~ObjectMap() = default; bool empty() const { std::lock_guard lock(mMutex); return mObjects.empty(); } ObjectType *first() const { std::lock_guard lock(mMutex); return mObjects.empty() ? nullptr : mObjects.begin()->second; } ObjectType *find(typename ResourceTypeToID::IDType handle) const { std::lock_guard lock(mMutex); auto iter = mObjects.find(handle.value); return iter != mObjects.end() ? iter->second : nullptr; } void insert(typename ResourceTypeToID::IDType handle, ObjectType *object) { std::lock_guard lock(mMutex); mObjects.insert(std::pair(handle.value, object)); } bool erase(typename ResourceTypeToID::IDType handle) { std::lock_guard lock(mMutex); auto iter = mObjects.find(handle.value); if (iter != mObjects.end()) { mObjects.erase(iter); return true; } return false; } ResourceVector extractAll() { std::lock_guard lock(mMutex); ResourceVector objects; objects.reserve(mObjects.size()); for (const auto &pair : mObjects) { objects.push_back(pair.second); } mObjects.clear(); return objects; } template void moveTo(ObjectMap *destination) { if (static_cast(this) == static_cast(destination)) { return; } Map temp; { std::lock_guard lock(mMutex); temp = std::move(mObjects); mObjects.clear(); } if (!temp.empty()) { std::lock_guard destLock(destination->mMutex); destination->mObjects.insert(temp.begin(), temp.end()); } } template auto forEach(Callback &&callback) const { using ReturnType = std::invoke_result_t; constexpr bool kReturnsError = std::is_same_v || std::is_same_v || std::is_same_v; using ErrorType = std::conditional_t; auto invoke = [&](ObjectType *object) -> ErrorType { if constexpr (kReturnsError) { return callback(object); } else { callback(object); return egl::NoError(); } }; ErrorType result = []() { if constexpr (std::is_same_v) { return angle::Result::Continue; } else if constexpr (std::is_same_v) { return true; } else { return egl::NoError(); } }(); if constexpr (std::is_same_v) { // This lock really is a no-op. It is there to satisfy the template // requirement. std::lock_guard lock(mMutex); for (const auto &pair : mObjects) { if (!IsError(result)) { result = invoke(pair.second); } } } else { // Collect objects while holding the lock to avoid holding it for the entire // duration of the callback which may end up with deadlock if callback ends up calling // into this ObjectMap. ResourceVector objects; { std::lock_guard lock(mMutex); objects.reserve(mObjects.size()); for (const auto &pair : mObjects) { pair.second->addRef(); objects.push_back(pair.second); } } for (ObjectType *object : objects) { if (!IsError(result)) { result = invoke(object); } object->release(); } } if constexpr (kReturnsError) { return result; } } protected: template friend class ObjectMap; template friend class ContextMapImpl; mutable MutexType mMutex; Map mObjects; }; } // namespace priv } // namespace egl #endif // LIBANGLE_OBJECT_MAP_H_