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
15using namespace clang;
16using namespace dependencies;
17
18namespace {
19class ReaderWriterLock : public llvm::AdvisoryLock {
20 ModuleCacheEntry &Entry;
21 std::optional<unsigned> OwnedGeneration;
22
23public:
24 ReaderWriterLock(ModuleCacheEntry &Entry) : Entry(Entry) {}
25
26 Expected<bool> tryLock() override {
27 std::lock_guard<std::mutex> Lock(Entry.Mutex);
28 if (Entry.Locked)
29 return false;
30 Entry.Locked = true;
31 OwnedGeneration = Entry.Generation;
32 return true;
33 }
34
35 llvm::WaitForUnlockResult
36 waitForUnlockFor(std::chrono::seconds MaxSeconds) override {
37 assert(!OwnedGeneration);
38 std::unique_lock<std::mutex> Lock(Entry.Mutex);
39 unsigned CurrentGeneration = Entry.Generation;
40 bool Success = Entry.CondVar.wait_for(Lock, MaxSeconds, [&] {
41 // We check not only Locked, but also Generation to break the wait in case
42 // of unsafeUnlock() and successful tryLock().
43 return !Entry.Locked || Entry.Generation != CurrentGeneration;
44 });
45 return Success ? llvm::WaitForUnlockResult::Success
46 : llvm::WaitForUnlockResult::Timeout;
47 }
48
49 std::error_code unsafeUnlock() override {
50 {
51 std::lock_guard<std::mutex> Lock(Entry.Mutex);
52 Entry.Generation += 1;
53 Entry.Locked = false;
54 }
55 Entry.CondVar.notify_all();
56 return {};
57 }
58
59 ~ReaderWriterLock() override {
60 if (OwnedGeneration) {
61 {
62 std::lock_guard<std::mutex> Lock(Entry.Mutex);
63 // Avoid stomping over the state managed by someone else after
64 // unsafeUnlock() and successful tryLock().
65 if (*OwnedGeneration == Entry.Generation)
66 Entry.Locked = false;
67 }
68 Entry.CondVar.notify_all();
69 }
70 }
71};
72
73class InProcessModuleCache : public ModuleCache {
74 ModuleCacheEntries &Entries;
75
76 // TODO: If we changed the InMemoryModuleCache API and relied on strict
77 // context hash, we could probably create more efficient thread-safe
78 // implementation of the InMemoryModuleCache such that it doesn't need to be
79 // recreated for each translation unit.
80 InMemoryModuleCache InMemory;
81
82public:
83 InProcessModuleCache(ModuleCacheEntries &Entries) : Entries(Entries) {}
84
85 void prepareForGetLock(StringRef Filename) override {}
86
87 std::unique_ptr<llvm::AdvisoryLock> getLock(StringRef Filename) override {
88 auto &Entry = [&]() -> ModuleCacheEntry & {
89 std::lock_guard<std::mutex> Lock(Entries.Mutex);
90 auto &Entry = Entries.Map[Filename];
91 if (!Entry)
92 Entry = std::make_unique<ModuleCacheEntry>();
93 return *Entry;
94 }();
95 return std::make_unique<ReaderWriterLock>(Entry);
96 }
97
98 std::time_t getModuleTimestamp(StringRef Filename) override {
99 auto &Timestamp = [&]() -> std::atomic<std::time_t> & {
100 std::lock_guard<std::mutex> Lock(Entries.Mutex);
101 auto &Entry = Entries.Map[Filename];
102 if (!Entry)
103 Entry = std::make_unique<ModuleCacheEntry>();
104 return Entry->Timestamp;
105 }();
106
107 return Timestamp.load();
108 }
109
110 void updateModuleTimestamp(StringRef Filename) override {
111 // Note: This essentially replaces FS contention with mutex contention.
112 auto &Timestamp = [&]() -> std::atomic<std::time_t> & {
113 std::lock_guard<std::mutex> Lock(Entries.Mutex);
114 auto &Entry = Entries.Map[Filename];
115 if (!Entry)
116 Entry = std::make_unique<ModuleCacheEntry>();
117 return Entry->Timestamp;
118 }();
119
120 Timestamp.store(llvm::sys::toTimeT(std::chrono::system_clock::now()));
121 }
122
123 void maybePrune(StringRef Path, time_t PruneInterval,
124 time_t PruneAfter) override {
125 // FIXME: This only needs to be ran once per build, not in every
126 // compilation. Call it once per service.
127 maybePruneImpl(Path, PruneInterval, PruneAfter);
128 }
129
130 InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
131 const InMemoryModuleCache &getInMemoryModuleCache() const override {
132 return InMemory;
133 }
134};
135} // namespace
136
137std::shared_ptr<ModuleCache>
139 return std::make_shared<InProcessModuleCache>(Entries);
140}
The module cache used for compiling modules implicitly.
Definition ModuleCache.h:25
std::shared_ptr< ModuleCache > makeInProcessModuleCache(ModuleCacheEntries &Entries)
The JSON file list parser is used to communicate input to InstallAPI.
@ Success
Annotation was successful.
Definition Parser.h:65
void maybePruneImpl(StringRef Path, time_t PruneInterval, time_t PruneAfter)
Shared implementation of ModuleCache::maybePrune().