/
pccc
/
pccc-mlir
Обзор
Документация
Войти
/
pccc
/
pccc-mlir
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
offload/include/PerThreadTable.h
269 строк
9 KB
obitotsky
fork mlir
19 июл 2026, 07:05
Верифицирован
19 июл 2026, 07:05
6422b87
Код
Авторство
О чём код?
//===-- PerThreadTable.h -- PerThread Storage Structure ----*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Table indexed with one entry per thread. // //===----------------------------------------------------------------------===// #ifndef OFFLOAD_PERTHREADTABLE_H #define OFFLOAD_PERTHREADTABLE_H #include <list> #include <llvm/ADT/SmallVector.h> #include <llvm/Support/Error.h> #include <memory> #include <mutex> #include <type_traits> template <typename ObjectType> class PerThread { std::mutex Mutex; llvm::SmallVector<std::shared_ptr<ObjectType>> ThreadDataList; ObjectType &getThreadData() { static thread_local std::shared_ptr<ObjectType> ThreadData = nullptr; if (!ThreadData) { ThreadData = std::make_shared<ObjectType>(); std::lock_guard<std::mutex> Lock(Mutex); ThreadDataList.push_back(ThreadData); } return *ThreadData; } public: // Define default constructors, disable copy and move constructors. PerThread() = default; PerThread(const PerThread &) = delete; PerThread(PerThread &&) = delete; PerThread &operator=(const PerThread &) = delete; PerThread &operator=(PerThread &&) = delete; ~PerThread() { assert(Mutex.try_lock() && (Mutex.unlock(), true) && "Cannot be deleted while other threads are adding entries"); ThreadDataList.clear(); } ObjectType &get() { return getThreadData(); } template <class ClearFuncTy> void clear(ClearFuncTy ClearFunc) { assert(Mutex.try_lock() && (Mutex.unlock(), true) && "Clear cannot be called while other threads are adding entries"); for (std::shared_ptr<ObjectType> ThreadData : ThreadDataList) { if (!ThreadData) continue; ClearFunc(*ThreadData); } ThreadDataList.clear(); } }; template <typename ContainerTy> struct ContainerConcepts { template <typename, template <typename> class, typename = std::void_t<>> struct has : std::false_type {}; template <typename Ty, template <typename> class Op> struct has<Ty, Op, std::void_t<Op<Ty>>> : std::true_type {}; template <typename Ty> using IteratorTypeCheck = typename Ty::iterator; template <typename Ty> using MappedTypeCheck = typename Ty::mapped_type; template <typename Ty> using ValueTypeCheck = typename Ty::value_type; template <typename Ty> using KeyTypeCheck = typename Ty::key_type; template <typename Ty> using SizeTypeCheck = typename Ty::size_type; template <typename Ty> using ClearCheck = decltype(std::declval<Ty>().clear()); template <typename Ty> using ReserveCheck = decltype(std::declval<Ty>().reserve(1)); template <typename Ty> using ResizeCheck = decltype(std::declval<Ty>().resize(1)); static constexpr bool hasIterator = has<ContainerTy, IteratorTypeCheck>::value; static constexpr bool hasClear = has<ContainerTy, ClearCheck>::value; static constexpr bool isAssociative = has<ContainerTy, MappedTypeCheck>::value; static constexpr bool hasReserve = has<ContainerTy, ReserveCheck>::value; static constexpr bool hasResize = has<ContainerTy, ResizeCheck>::value; template <typename, template <typename> class, typename = std::void_t<>> struct has_type { using type = void; }; template <typename Ty, template <typename> class Op> struct has_type<Ty, Op, std::void_t<Op<Ty>>> { using type = Op<Ty>; }; using iterator = typename has_type<ContainerTy, IteratorTypeCheck>::type; using value_type = typename std::conditional_t< isAssociative, typename has_type<ContainerTy, MappedTypeCheck>::type, typename has_type<ContainerTy, ValueTypeCheck>::type>; using key_type = typename std::conditional_t< isAssociative, typename has_type<ContainerTy, KeyTypeCheck>::type, typename has_type<ContainerTy, SizeTypeCheck>::type>; }; // Using an STL container (such as std::vector) indexed by thread ID has // too many race conditions issues so we store each thread entry into a // thread_local variable. // ContainerType is the container type used to store the objects, e.g., // std::vector, std::set, etc. by each thread. ObjectType is the type of the // stored objects e.g., omp_interop_val_t *, ... template <typename ContainerType, typename ObjectType> class PerThreadTable { using iterator = typename ContainerConcepts<ContainerType>::iterator; struct PerThreadData { size_t Size = 0; std::unique_ptr<ContainerType> ThreadEntry; }; std::mutex Mutex; llvm::SmallVector<std::shared_ptr<PerThreadData>> ThreadDataList; PerThreadData &getThreadData() { static thread_local std::shared_ptr<PerThreadData> ThreadData = nullptr; if (!ThreadData) { ThreadData = std::make_shared<PerThreadData>(); std::lock_guard<std::mutex> Lock(Mutex); ThreadDataList.push_back(ThreadData); } return *ThreadData; } protected: ContainerType &getThreadEntry() { PerThreadData &ThreadData = getThreadData(); if (ThreadData.ThreadEntry) return *ThreadData.ThreadEntry; ThreadData.ThreadEntry = std::make_unique<ContainerType>(); return *ThreadData.ThreadEntry; } size_t &getThreadSize() { PerThreadData &ThreadData = getThreadData(); return ThreadData.Size; } void setSize(size_t Size) { size_t &SizeRef = getThreadSize(); SizeRef = Size; } public: // define default constructors, disable copy and move constructors. PerThreadTable() = default; PerThreadTable(const PerThreadTable &) = delete; PerThreadTable(PerThreadTable &&) = delete; PerThreadTable &operator=(const PerThreadTable &) = delete; PerThreadTable &operator=(PerThreadTable &&) = delete; ~PerThreadTable() { assert(Mutex.try_lock() && (Mutex.unlock(), true) && "Cannot be deleted while other threads are adding entries"); ThreadDataList.clear(); } void add(ObjectType obj) { ContainerType &Entry = getThreadEntry(); size_t &SizeRef = getThreadSize(); SizeRef++; Entry.add(obj); } iterator erase(iterator it) { ContainerType &Entry = getThreadEntry(); size_t &SizeRef = getThreadSize(); SizeRef--; return Entry.erase(it); } size_t size() { return getThreadSize(); } // Iterators to traverse objects owned by // the current thread. iterator begin() { ContainerType &Entry = getThreadEntry(); return Entry.begin(); } iterator end() { ContainerType &Entry = getThreadEntry(); return Entry.end(); } template <class ClearFuncTy> void clear(ClearFuncTy ClearFunc) { assert(Mutex.try_lock() && (Mutex.unlock(), true) && "Clear cannot be called while other threads are adding entries"); for (std::shared_ptr<PerThreadData> ThreadData : ThreadDataList) { if (!ThreadData->ThreadEntry || ThreadData->Size == 0) continue; if constexpr (ContainerConcepts<ContainerType>::hasIterator && ContainerConcepts<ContainerType>::hasClear) { for (auto &Obj : *ThreadData->ThreadEntry) { if constexpr (ContainerConcepts<ContainerType>::isAssociative) { ClearFunc(Obj.second); } else { ClearFunc(Obj); } } ThreadData->ThreadEntry->clear(); } else { static_assert(true, "Container type not supported"); } ThreadData->Size = 0; } ThreadDataList.clear(); } template <class DeinitFuncTy> llvm::Error deinit(DeinitFuncTy DeinitFunc) { assert(Mutex.try_lock() && (Mutex.unlock(), true) && "Deinit cannot be called while other threads are adding entries"); for (std::shared_ptr<PerThreadData> ThreadData : ThreadDataList) { if (!ThreadData->ThreadEntry || ThreadData->Size == 0) continue; for (auto &Obj : *ThreadData->ThreadEntry) { if constexpr (ContainerConcepts<ContainerType>::isAssociative) { if (auto Err = DeinitFunc(Obj.second)) return Err; } else { if (auto Err = DeinitFunc(Obj)) return Err; } } } return llvm::Error::success(); } }; template <typename ContainerType, size_t ReserveSize = 0> class PerThreadContainer : public PerThreadTable<ContainerType, typename ContainerConcepts< ContainerType>::value_type> { using IndexType = typename ContainerConcepts<ContainerType>::key_type; using ObjectType = typename ContainerConcepts<ContainerType>::value_type; public: // Get the object for the given index in the current thread. ObjectType &get(IndexType Index) { ContainerType &Entry = this->getThreadEntry(); // Specialized code for vector-like containers. if constexpr (ContainerConcepts<ContainerType>::hasResize) { if (Index >= Entry.size()) { if constexpr (ContainerConcepts<ContainerType>::hasReserve && ReserveSize > 0) Entry.reserve(ReserveSize); // If the index is out of bounds, try resize the container. Entry.resize(Index + 1); } } ObjectType &Ret = Entry[Index]; this->setSize(Entry.size()); return Ret; } }; #endif