blob: 3b5d6f0f424f9b179264c2fc952df0cb819855c4 [file] [log] [blame]
//===- ThreadLocalCache.h - ThreadLocalCache class --------------*- 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
//
//===----------------------------------------------------------------------===//
//
// This file contains a definition of the ThreadLocalCache class. This class
// provides support for defining thread local objects with non-static duration.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_SUPPORT_THREADLOCALCACHE_H
#define MLIR_SUPPORT_THREADLOCALCACHE_H
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/ThreadLocal.h"
namespace mlir {
/// This class provides support for defining a thread local object with non
/// static storage duration. This is very useful for situations in which a data
/// cache has very large lock contention.
template <typename ValueT>
class ThreadLocalCache {
/// The type used for the static thread_local cache. This is a map between an
/// instance of the non-static cache and a weak reference to an instance of
/// ValueT. We use a weak reference here so that the object can be destroyed
/// without needing to lock access to the cache itself.
struct CacheType : public llvm::SmallDenseMap<ThreadLocalCache<ValueT> *,
std::weak_ptr<ValueT>> {
~CacheType() {
// Remove the values of this cache that haven't already expired.
for (auto &it : *this)
if (std::shared_ptr<ValueT> value = it.second.lock())
it.first->remove(value.get());
}
/// Clear out any unused entries within the map. This method is not
/// thread-safe, and should only be called by the same thread as the cache.
void clearExpiredEntries() {
for (auto it = this->begin(), e = this->end(); it != e;) {
auto curIt = it++;
if (curIt->second.expired())
this->erase(curIt);
}
}
};
public:
ThreadLocalCache() = default;
~ThreadLocalCache() {
// No cleanup is necessary here as the shared_pointer memory will go out of
// scope and invalidate the weak pointers held by the thread_local caches.
}
/// Return an instance of the value type for the current thread.
ValueT &get() {
// Check for an already existing instance for this thread.
CacheType &staticCache = getStaticCache();
std::weak_ptr<ValueT> &threadInstance = staticCache[this];
if (std::shared_ptr<ValueT> value = threadInstance.lock())
return *value;
// Otherwise, create a new instance for this thread.
llvm::sys::SmartScopedLock<true> threadInstanceLock(instanceMutex);
instances.push_back(std::make_shared<ValueT>());
std::shared_ptr<ValueT> &instance = instances.back();
threadInstance = instance;
// Before returning the new instance, take the chance to clear out any used
// entries in the static map. The cache is only cleared within the same
// thread to remove the need to lock the cache itself.
staticCache.clearExpiredEntries();
return *instance;
}
ValueT &operator*() { return get(); }
ValueT *operator->() { return &get(); }
private:
ThreadLocalCache(ThreadLocalCache &&) = delete;
ThreadLocalCache(const ThreadLocalCache &) = delete;
ThreadLocalCache &operator=(const ThreadLocalCache &) = delete;
/// Return the static thread local instance of the cache type.
static CacheType &getStaticCache() {
static LLVM_THREAD_LOCAL CacheType cache;
return cache;
}
/// Remove the given value entry. This is generally called when a thread local
/// cache is destructing.
void remove(ValueT *value) {
// Erase the found value directly, because it is guaranteed to be in the
// list.
llvm::sys::SmartScopedLock<true> threadInstanceLock(instanceMutex);
auto it = llvm::find_if(instances, [&](std::shared_ptr<ValueT> &instance) {
return instance.get() == value;
});
assert(it != instances.end() && "expected value to exist in cache");
instances.erase(it);
}
/// Owning pointers to all of the values that have been constructed for this
/// object in the static cache.
SmallVector<std::shared_ptr<ValueT>, 1> instances;
/// A mutex used when a new thread instance has been added to the cache for
/// this object.
llvm::sys::SmartMutex<true> instanceMutex;
};
} // end namespace mlir
#endif // MLIR_SUPPORT_THREADLOCALCACHE_H