clang 24.0.0git
InProcessModuleCache.cpp
Go to the documentation of this file.
1//===- InProcessModuleCache.cpp - Implicit Module Cache ---------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10
14#include "llvm/Support/AdvisoryLock.h"
15#include "llvm/Support/Chrono.h"
16#include "llvm/Support/Error.h"
17#include "llvm/Support/FileSystem.h"
18#include "llvm/Support/IOSandbox.h"
19#include "llvm/Support/MemoryBuffer.h"
20#include "llvm/Support/Path.h"
21
22using namespace clang;
23using namespace dependencies;
24
26 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
27 for (auto &[Path, Entry] : Map) {
28 if (Entry->State == ModuleCacheEntry::S_Written) {
29 assert(Entry->WrittenBuffer && "Wrote PCM with no contents");
30 // Note: We could propagate Entry->ModTime to the on-disk file, but
31 // implicitly-built modules (unlike explicitly-built modules) don't use
32 // that metadata to refer to imports, rendering this unnecessary.
33 off_t Size;
34 time_t ModTime;
35 // Best-effort: ignore errors (e.g. read-only cache directory).
36 (void)writeImpl(Path, *Entry->WrittenBuffer, Size, ModTime);
37 }
38 }
39}
40
41namespace {
42class ReaderWriterLock : public llvm::AdvisoryLock {
43 ModuleCacheEntry &Entry;
44 std::optional<unsigned> OwnedGeneration;
45
46public:
47 ReaderWriterLock(ModuleCacheEntry &Entry) : Entry(Entry) {}
48
49 Expected<bool> tryLock() override {
50 std::lock_guard<std::mutex> Lock(Entry.Mutex);
51 if (Entry.Locked)
52 return false;
53 Entry.Locked = true;
54 OwnedGeneration = Entry.Generation;
55 return true;
56 }
57
58 llvm::WaitForUnlockResult
59 waitForUnlockFor(std::chrono::seconds MaxSeconds) override {
60 assert(!OwnedGeneration);
61 std::unique_lock<std::mutex> Lock(Entry.Mutex);
62 unsigned CurrentGeneration = Entry.Generation;
63 bool Success = Entry.CondVar.wait_for(Lock, MaxSeconds, [&] {
64 // We check not only Locked, but also Generation to break the wait in case
65 // of unsafeUnlock() and successful tryLock().
66 return !Entry.Locked || Entry.Generation != CurrentGeneration;
67 });
68 return Success ? llvm::WaitForUnlockResult::Success
69 : llvm::WaitForUnlockResult::Timeout;
70 }
71
72 std::error_code unsafeUnlock() override {
73 {
74 std::lock_guard<std::mutex> Lock(Entry.Mutex);
75 Entry.Generation += 1;
76 Entry.Locked = false;
77 }
78 Entry.CondVar.notify_all();
79 return {};
80 }
81
82 ~ReaderWriterLock() override {
83 if (OwnedGeneration) {
84 {
85 std::lock_guard<std::mutex> Lock(Entry.Mutex);
86 // Avoid stomping over the state managed by someone else after
87 // unsafeUnlock() and successful tryLock().
88 if (*OwnedGeneration == Entry.Generation)
89 Entry.Locked = false;
90 }
91 Entry.CondVar.notify_all();
92 }
93 }
94};
95
96class InProcessModuleCache : public ModuleCache {
97 ModuleCacheEntries &Entries;
98
99 // TODO: If we changed the InMemoryModuleCache API and relied on strict
100 // context hash, we could probably create more efficient thread-safe
101 // implementation of the InMemoryModuleCache such that it doesn't need to be
102 // recreated for each translation unit.
103 InMemoryModuleCache InMemory;
104
105 ModuleCacheEntry &getOrCreateEntry(StringRef Filename) {
106 std::lock_guard<std::mutex> Lock(Entries.Mutex);
107 auto &Entry = Entries.Map[Filename];
108 if (!Entry)
109 Entry = std::make_unique<ModuleCacheEntry>();
110 return *Entry;
111 }
112
113public:
114 InProcessModuleCache(ModuleCacheEntries &Entries, AtomicLineLogger &Logger)
115 : ModuleCache(Logger), Entries(Entries), InMemory(Logger) {}
116
117 std::unique_ptr<llvm::AdvisoryLock> getLock(StringRef Filename) override {
118 auto &Entry = getOrCreateEntry(Filename);
119 return std::make_unique<ReaderWriterLock>(Entry);
120 }
121
122 std::time_t getModuleTimestamp(StringRef Filename) override {
123 auto &Timestamp = getOrCreateEntry(Filename).Timestamp;
124
125 Logger.log() << "timestamp_read: " << Filename;
126 return Timestamp.load();
127 }
128
129 void updateModuleTimestamp(StringRef Filename) override {
130 // Note: This essentially replaces FS contention with mutex contention.
131 auto &Timestamp = getOrCreateEntry(Filename).Timestamp;
132
133 Logger.log() << "timestamp_write: " << Filename;
134 Timestamp.store(llvm::sys::toTimeT(std::chrono::system_clock::now()));
135 }
136
137 void maybePrune(StringRef Path, time_t PruneInterval,
138 time_t PruneAfter) override {
139 // FIXME: This only needs to be ran once per build, not in every
140 // compilation. Call it once per service.
141 maybePruneImpl(Path, PruneInterval, PruneAfter);
142 }
143
144 InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
145 const InMemoryModuleCache &getInMemoryModuleCache() const override {
146 return InMemory;
147 }
148
149 std::error_code write(StringRef Path, llvm::MemoryBufferRef Buffer,
150 off_t &Size, time_t &ModTime) override {
151 ModuleCacheEntry &Entry = getOrCreateEntry(Path);
152 std::lock_guard<std::mutex> Lock(Entry.Mutex);
153 Logger.log() << "pcm_write: " << Path;
154 if (Entry.State == ModuleCacheEntry::S_Written) {
155 assert(Entry.WrittenBuffer && "Wrote PCM with no contents");
156 assert(Entry.WrittenBuffer->getBuffer() == Buffer.getBuffer() &&
157 "Wrote the same PCM with different contents");
158 Size = Entry.WrittenBuffer->getBufferSize();
159 ModTime = Entry.ModTime;
160 return {};
161 }
162 Entry.WrittenBuffer =
163 llvm::MemoryBuffer::getMemBufferCopy(Buffer.getBuffer(), Path);
164 Entry.ModTime = llvm::sys::toTimeT(std::chrono::system_clock::now());
166 Size = Entry.WrittenBuffer->getBufferSize();
167 ModTime = Entry.ModTime;
168 return {};
169 }
170
171 Expected<std::unique_ptr<llvm::MemoryBuffer>>
172 read(StringRef FileName, off_t &Size, time_t &ModTime) override {
173 Logger.log() << "pcm_read_disk: " << FileName;
174 ModuleCacheEntry &Entry = getOrCreateEntry(FileName);
175 std::lock_guard<std::mutex> Lock(Entry.Mutex);
176 if (Entry.State == ModuleCacheEntry::S_Unknown) {
177 // This is a compiler-internal input/output, let's bypass the sandbox.
178 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
179 off_t ReadSize;
180 time_t ReadModTime;
181 auto ReadBuffer = readImpl(FileName, ReadSize, ReadModTime);
182 if (!ReadBuffer)
183 return ReadBuffer.takeError();
184 Entry.ReadBuffer = std::move(*ReadBuffer);
185 Entry.ModTime = ReadModTime;
187 }
188 // The written buffer takes precedence over any read buffer.
189 llvm::MemoryBuffer *Buffer = Entry.WrittenBuffer ? Entry.WrittenBuffer.get()
190 : Entry.ReadBuffer.get();
191 Size = Buffer->getBufferSize();
192 ModTime = Entry.ModTime;
193 // Note: Creates a reference to ReadBuffer or WrittenBuffer.
194 return llvm::MemoryBuffer::getMemBuffer(*Buffer,
195 /*RequiresNullTerminator=*/false);
196 }
197};
198} // namespace
199
200std::shared_ptr<ModuleCache>
202 AtomicLineLogger &Logger) {
203 return std::make_shared<InProcessModuleCache>(Entries, Logger);
204}
Defines a logger where each line is written atomically to the file.
std::shared_ptr< ModuleCache > makeInProcessModuleCache(ModuleCacheEntries &Entries, AtomicLineLogger &Logger)
The JSON file list parser is used to communicate input to InstallAPI.
Expected< std::unique_ptr< llvm::MemoryBuffer > > readImpl(StringRef FileName, off_t &Size, time_t &ModTime)
Shared implementation of ModuleCache::read().
@ Success
Annotation was successful.
Definition Parser.h:65
std::error_code writeImpl(StringRef Path, llvm::MemoryBufferRef Buffer, off_t &Size, time_t &ModTime)
Shared implementation of ModuleCache::write().
void maybePruneImpl(StringRef Path, time_t PruneInterval, time_t PruneAfter, bool PruneTopLevel=false, llvm::function_ref< void(StringRef)> OnPrune={})
Shared implementation of ModuleCache::maybePrune().
llvm::StringMap< std::unique_ptr< ModuleCacheEntry > > Map
void flush()
Flushes all PCMs built in-process to disk.
enum clang::dependencies::ModuleCacheEntry::@010254116217305143125116322142262271134241253225 State
time_t ModTime
The modification time of the entry.
std::unique_ptr< llvm::MemoryBuffer > WrittenBuffer
The buffer we've written to module cache, if any.
std::unique_ptr< llvm::MemoryBuffer > ReadBuffer
The buffer we've read from disk, if any.