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