CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
197 lines
7.0 KiB
C++
197 lines
7.0 KiB
C++
//===- InProcessModuleCache.cpp - Implicit Module Cache ---------*- 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/DependencyScanning/InProcessModuleCache.h"
|
|
|
|
#include "clang/Serialization/InMemoryModuleCache.h"
|
|
#include "llvm/Support/AdvisoryLock.h"
|
|
#include "llvm/Support/Chrono.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/IOSandbox.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Path.h"
|
|
|
|
using namespace clang;
|
|
using namespace dependencies;
|
|
|
|
void ModuleCacheEntries::flush() {
|
|
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
|
|
for (auto &[Path, Entry] : Map) {
|
|
if (Entry->State == ModuleCacheEntry::S_Written) {
|
|
assert(Entry->WrittenBuffer && "Wrote PCM with no contents");
|
|
// Note: We could propagate Entry->ModTime to the on-disk file, but
|
|
// implicitly-built modules (unlike explicitly-built modules) don't use
|
|
// that metadata to refer to imports, rendering this unnecessary.
|
|
off_t Size;
|
|
time_t ModTime;
|
|
// Best-effort: ignore errors (e.g. read-only cache directory).
|
|
(void)writeImpl(Path, *Entry->WrittenBuffer, Size, ModTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
class ReaderWriterLock : public llvm::AdvisoryLock {
|
|
ModuleCacheEntry &Entry;
|
|
std::optional<unsigned> OwnedGeneration;
|
|
|
|
public:
|
|
ReaderWriterLock(ModuleCacheEntry &Entry) : Entry(Entry) {}
|
|
|
|
Expected<bool> tryLock() override {
|
|
std::lock_guard<std::mutex> Lock(Entry.Mutex);
|
|
if (Entry.Locked)
|
|
return false;
|
|
Entry.Locked = true;
|
|
OwnedGeneration = Entry.Generation;
|
|
return true;
|
|
}
|
|
|
|
llvm::WaitForUnlockResult
|
|
waitForUnlockFor(std::chrono::seconds MaxSeconds) override {
|
|
assert(!OwnedGeneration);
|
|
std::unique_lock<std::mutex> Lock(Entry.Mutex);
|
|
unsigned CurrentGeneration = Entry.Generation;
|
|
bool Success = Entry.CondVar.wait_for(Lock, MaxSeconds, [&] {
|
|
// We check not only Locked, but also Generation to break the wait in case
|
|
// of unsafeUnlock() and successful tryLock().
|
|
return !Entry.Locked || Entry.Generation != CurrentGeneration;
|
|
});
|
|
return Success ? llvm::WaitForUnlockResult::Success
|
|
: llvm::WaitForUnlockResult::Timeout;
|
|
}
|
|
|
|
std::error_code unsafeUnlock() override {
|
|
{
|
|
std::lock_guard<std::mutex> Lock(Entry.Mutex);
|
|
Entry.Generation += 1;
|
|
Entry.Locked = false;
|
|
}
|
|
Entry.CondVar.notify_all();
|
|
return {};
|
|
}
|
|
|
|
~ReaderWriterLock() override {
|
|
if (OwnedGeneration) {
|
|
{
|
|
std::lock_guard<std::mutex> Lock(Entry.Mutex);
|
|
// Avoid stomping over the state managed by someone else after
|
|
// unsafeUnlock() and successful tryLock().
|
|
if (*OwnedGeneration == Entry.Generation)
|
|
Entry.Locked = false;
|
|
}
|
|
Entry.CondVar.notify_all();
|
|
}
|
|
}
|
|
};
|
|
|
|
class InProcessModuleCache : public ModuleCache {
|
|
ModuleCacheEntries &Entries;
|
|
|
|
// TODO: If we changed the InMemoryModuleCache API and relied on strict
|
|
// context hash, we could probably create more efficient thread-safe
|
|
// implementation of the InMemoryModuleCache such that it doesn't need to be
|
|
// recreated for each translation unit.
|
|
InMemoryModuleCache InMemory;
|
|
|
|
ModuleCacheEntry &getOrCreateEntry(StringRef Filename) {
|
|
std::lock_guard<std::mutex> Lock(Entries.Mutex);
|
|
auto &Entry = Entries.Map[Filename];
|
|
if (!Entry)
|
|
Entry = std::make_unique<ModuleCacheEntry>();
|
|
return *Entry;
|
|
}
|
|
|
|
public:
|
|
InProcessModuleCache(ModuleCacheEntries &Entries) : Entries(Entries) {}
|
|
|
|
std::unique_ptr<llvm::AdvisoryLock> getLock(StringRef Filename) override {
|
|
auto &Entry = getOrCreateEntry(Filename);
|
|
return std::make_unique<ReaderWriterLock>(Entry);
|
|
}
|
|
|
|
std::time_t getModuleTimestamp(StringRef Filename) override {
|
|
auto &Timestamp = getOrCreateEntry(Filename).Timestamp;
|
|
|
|
return Timestamp.load();
|
|
}
|
|
|
|
void updateModuleTimestamp(StringRef Filename) override {
|
|
// Note: This essentially replaces FS contention with mutex contention.
|
|
auto &Timestamp = getOrCreateEntry(Filename).Timestamp;
|
|
|
|
Timestamp.store(llvm::sys::toTimeT(std::chrono::system_clock::now()));
|
|
}
|
|
|
|
void maybePrune(StringRef Path, time_t PruneInterval,
|
|
time_t PruneAfter) override {
|
|
// FIXME: This only needs to be ran once per build, not in every
|
|
// compilation. Call it once per service.
|
|
maybePruneImpl(Path, PruneInterval, PruneAfter);
|
|
}
|
|
|
|
InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
|
|
const InMemoryModuleCache &getInMemoryModuleCache() const override {
|
|
return InMemory;
|
|
}
|
|
|
|
std::error_code write(StringRef Path, llvm::MemoryBufferRef Buffer,
|
|
off_t &Size, time_t &ModTime) override {
|
|
ModuleCacheEntry &Entry = getOrCreateEntry(Path);
|
|
std::lock_guard<std::mutex> Lock(Entry.Mutex);
|
|
if (Entry.State == ModuleCacheEntry::S_Written) {
|
|
assert(Entry.WrittenBuffer && "Wrote PCM with no contents");
|
|
assert(Entry.WrittenBuffer->getBuffer() == Buffer.getBuffer() &&
|
|
"Wrote the same PCM with different contents");
|
|
Size = Entry.WrittenBuffer->getBufferSize();
|
|
ModTime = Entry.ModTime;
|
|
return {};
|
|
}
|
|
Entry.WrittenBuffer =
|
|
llvm::MemoryBuffer::getMemBufferCopy(Buffer.getBuffer(), Path);
|
|
Entry.ModTime = llvm::sys::toTimeT(std::chrono::system_clock::now());
|
|
Entry.State = ModuleCacheEntry::S_Written;
|
|
Size = Entry.WrittenBuffer->getBufferSize();
|
|
ModTime = Entry.ModTime;
|
|
return {};
|
|
}
|
|
|
|
Expected<std::unique_ptr<llvm::MemoryBuffer>>
|
|
read(StringRef FileName, off_t &Size, time_t &ModTime) override {
|
|
ModuleCacheEntry &Entry = getOrCreateEntry(FileName);
|
|
std::lock_guard<std::mutex> Lock(Entry.Mutex);
|
|
if (Entry.State == ModuleCacheEntry::S_Unknown) {
|
|
// This is a compiler-internal input/output, let's bypass the sandbox.
|
|
auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
|
|
off_t ReadSize;
|
|
time_t ReadModTime;
|
|
auto ReadBuffer = readImpl(FileName, ReadSize, ReadModTime);
|
|
if (!ReadBuffer)
|
|
return ReadBuffer.takeError();
|
|
Entry.ReadBuffer = std::move(*ReadBuffer);
|
|
Entry.ModTime = ReadModTime;
|
|
Entry.State = ModuleCacheEntry::S_Read;
|
|
}
|
|
// The written buffer takes precedence over any read buffer.
|
|
llvm::MemoryBuffer *Buffer = Entry.WrittenBuffer ? Entry.WrittenBuffer.get()
|
|
: Entry.ReadBuffer.get();
|
|
Size = Buffer->getBufferSize();
|
|
ModTime = Entry.ModTime;
|
|
// Note: Creates a reference to ReadBuffer or WrittenBuffer.
|
|
return llvm::MemoryBuffer::getMemBuffer(*Buffer,
|
|
/*RequiresNullTerminator=*/false);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
std::shared_ptr<ModuleCache>
|
|
dependencies::makeInProcessModuleCache(ModuleCacheEntries &Entries) {
|
|
return std::make_shared<InProcessModuleCache>(Entries);
|
|
}
|