| // |
| // 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 <type_traits> |
| #include <vector> |
| |
| #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 <typename ContextMutexType> |
| class ContextMapImpl; |
| |
| template <typename ObjectType, typename MutexType = angle::SimpleMutex> |
| class ObjectMap : angle::NonCopyable |
| { |
| public: |
| using Map = angle::HashMap<GLuint, ObjectType *>; |
| using ResourceVector = angle::FastVector<ObjectType *, 8>; |
| |
| ObjectMap() = default; |
| ~ObjectMap() = default; |
| |
| bool empty() const |
| { |
| std::lock_guard<MutexType> lock(mMutex); |
| return mObjects.empty(); |
| } |
| |
| ObjectType *first() const |
| { |
| std::lock_guard<MutexType> lock(mMutex); |
| return mObjects.empty() ? nullptr : mObjects.begin()->second; |
| } |
| |
| ObjectType *find(typename ResourceTypeToID<ObjectType>::IDType handle) const |
| { |
| std::lock_guard<MutexType> lock(mMutex); |
| auto iter = mObjects.find(handle.value); |
| return iter != mObjects.end() ? iter->second : nullptr; |
| } |
| |
| void insert(typename ResourceTypeToID<ObjectType>::IDType handle, ObjectType *object) |
| { |
| std::lock_guard<MutexType> lock(mMutex); |
| mObjects.insert(std::pair(handle.value, object)); |
| } |
| |
| bool erase(typename ResourceTypeToID<ObjectType>::IDType handle) |
| { |
| std::lock_guard<MutexType> lock(mMutex); |
| auto iter = mObjects.find(handle.value); |
| if (iter != mObjects.end()) |
| { |
| mObjects.erase(iter); |
| return true; |
| } |
| return false; |
| } |
| |
| ResourceVector extractAll() |
| { |
| std::lock_guard<MutexType> lock(mMutex); |
| ResourceVector objects; |
| objects.reserve(mObjects.size()); |
| for (const auto &pair : mObjects) |
| { |
| objects.push_back(pair.second); |
| } |
| mObjects.clear(); |
| return objects; |
| } |
| |
| template <typename OtherMutexType> |
| void moveTo(ObjectMap<ObjectType, OtherMutexType> *destination) |
| { |
| if (static_cast<void *>(this) == static_cast<void *>(destination)) |
| { |
| return; |
| } |
| |
| Map temp; |
| { |
| std::lock_guard<MutexType> lock(mMutex); |
| temp = std::move(mObjects); |
| mObjects.clear(); |
| } |
| |
| if (!temp.empty()) |
| { |
| std::lock_guard<OtherMutexType> destLock(destination->mMutex); |
| destination->mObjects.insert(temp.begin(), temp.end()); |
| } |
| } |
| |
| template <typename Callback> |
| auto forEach(Callback &&callback) const |
| { |
| using ReturnType = std::invoke_result_t<Callback, ObjectType *>; |
| constexpr bool kReturnsError = std::is_same_v<ReturnType, egl::Error> || |
| std::is_same_v<ReturnType, angle::Result> || |
| std::is_same_v<ReturnType, bool>; |
| |
| using ErrorType = std::conditional_t<kReturnsError, ReturnType, egl::Error>; |
| |
| 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<ErrorType, angle::Result>) |
| { |
| return angle::Result::Continue; |
| } |
| else if constexpr (std::is_same_v<ErrorType, bool>) |
| { |
| return true; |
| } |
| else |
| { |
| return egl::NoError(); |
| } |
| }(); |
| |
| if constexpr (std::is_same_v<MutexType, angle::NoOpMutex>) |
| { |
| // This lock really is a no-op. It is there to satisfy the template |
| // requirement. |
| std::lock_guard<MutexType> 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<MutexType> 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 <typename OtherObjectType, typename OtherMutexType> |
| friend class ObjectMap; |
| |
| template <typename ContextMutexType> |
| friend class ContextMapImpl; |
| |
| mutable MutexType mMutex; |
| Map mObjects; |
| }; |
| |
| } // namespace priv |
| } // namespace egl |
| |
| #endif // LIBANGLE_OBJECT_MAP_H_ |