llvm-project/offload/include/PerThreadTable.h
Alex Duran 66d1c37eb6
[OFFLOAD][OPENMP] 6.0 compatible interop interface (#143491)
The following patch introduces a new interop interface implementation
with the following characteristics:

* It supports the new 6.0 prefer_type specification
* It supports both explicit objects (from interop constructs) and
implicit objects (from variant calls).
* Implements a per-thread reuse mechanism for implicit objects to reduce
overheads.
* It provides a plugin interface that allows selecting the supported
interop types, and managing all the backend related interop operations
(init, sync, ...).
* It enables cooperation with the OpenMP runtime to allow progress on
OpenMP synchronizations.
* It cleanups some vendor/fr_id mismatchs from the current query
routines.
* It supports extension to define interop callbacks for library cleanup.
2025-08-06 16:34:39 +02:00

115 lines
3.2 KiB
C++

//===-- 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 <memory>
#include <mutex>
// 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.
// T is the container type used to store the objects, e.g., std::vector,
// std::set, etc. by each thread. O is the type of the stored objects e.g.,
// omp_interop_val_t *, ...
template <typename ContainerType, typename ObjectType> struct PerThreadTable {
using iterator = typename ContainerType::iterator;
struct PerThreadData {
size_t NElements = 0;
std::unique_ptr<ContainerType> ThEntry;
};
std::mutex Mtx;
std::list<std::shared_ptr<PerThreadData>> ThreadDataList;
// 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() {
std::lock_guard<std::mutex> Lock(Mtx);
ThreadDataList.clear();
}
private:
PerThreadData &getThreadData() {
static thread_local std::shared_ptr<PerThreadData> ThData = nullptr;
if (!ThData) {
ThData = std::make_shared<PerThreadData>();
std::lock_guard<std::mutex> Lock(Mtx);
ThreadDataList.push_back(ThData);
}
return *ThData;
}
protected:
ContainerType &getThreadEntry() {
auto &ThData = getThreadData();
if (ThData.ThEntry)
return *ThData.ThEntry;
ThData.ThEntry = std::make_unique<ContainerType>();
return *ThData.ThEntry;
}
size_t &getThreadNElements() {
auto &ThData = getThreadData();
return ThData.NElements;
}
public:
void add(ObjectType obj) {
auto &Entry = getThreadEntry();
auto &NElements = getThreadNElements();
NElements++;
Entry.add(obj);
}
iterator erase(iterator it) {
auto &Entry = getThreadEntry();
auto &NElements = getThreadNElements();
NElements--;
return Entry.erase(it);
}
size_t size() { return getThreadNElements(); }
// Iterators to traverse objects owned by
// the current thread
iterator begin() {
auto &Entry = getThreadEntry();
return Entry.begin();
}
iterator end() {
auto &Entry = getThreadEntry();
return Entry.end();
}
template <class F> void clear(F f) {
std::lock_guard<std::mutex> Lock(Mtx);
for (auto ThData : ThreadDataList) {
if (!ThData->ThEntry || ThData->NElements == 0)
continue;
ThData->ThEntry->clear(f);
ThData->NElements = 0;
}
ThreadDataList.clear();
}
};
#endif