#ifndef OFFLOAD_PERTHREADTABLE_H
#define OFFLOAD_PERTHREADTABLE_H
#include <list>
#include <memory>
#include <mutex>
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;
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(); }
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