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