xref: /freebsd/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
2*700637cbSDimitry Andric //
3*700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric //
7*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
8*700637cbSDimitry Andric 
9*700637cbSDimitry Andric #include "clang/Tooling/DependencyScanning/InProcessModuleCache.h"
10*700637cbSDimitry Andric 
11*700637cbSDimitry Andric #include "clang/Serialization/InMemoryModuleCache.h"
12*700637cbSDimitry Andric #include "llvm/Support/AdvisoryLock.h"
13*700637cbSDimitry Andric #include "llvm/Support/Chrono.h"
14*700637cbSDimitry Andric 
15*700637cbSDimitry Andric #include <mutex>
16*700637cbSDimitry Andric 
17*700637cbSDimitry Andric using namespace clang;
18*700637cbSDimitry Andric using namespace tooling;
19*700637cbSDimitry Andric using namespace dependencies;
20*700637cbSDimitry Andric 
21*700637cbSDimitry Andric namespace {
22*700637cbSDimitry Andric class ReaderWriterLock : public llvm::AdvisoryLock {
23*700637cbSDimitry Andric   // TODO: Consider using std::atomic::{wait,notify_all} when we move to C++20.
24*700637cbSDimitry Andric   std::unique_lock<std::shared_mutex> OwningLock;
25*700637cbSDimitry Andric 
26*700637cbSDimitry Andric public:
ReaderWriterLock(std::shared_mutex & Mutex)27*700637cbSDimitry Andric   ReaderWriterLock(std::shared_mutex &Mutex)
28*700637cbSDimitry Andric       : OwningLock(Mutex, std::defer_lock) {}
29*700637cbSDimitry Andric 
tryLock()30*700637cbSDimitry Andric   Expected<bool> tryLock() override { return OwningLock.try_lock(); }
31*700637cbSDimitry Andric 
32*700637cbSDimitry Andric   llvm::WaitForUnlockResult
waitForUnlockFor(std::chrono::seconds MaxSeconds)33*700637cbSDimitry Andric   waitForUnlockFor(std::chrono::seconds MaxSeconds) override {
34*700637cbSDimitry Andric     assert(!OwningLock);
35*700637cbSDimitry Andric     // We do not respect the timeout here. It's very generous for implicit
36*700637cbSDimitry Andric     // modules, so we'd typically only reach it if the owner crashed (but so did
37*700637cbSDimitry Andric     // we, since we run in the same process), or encountered deadlock.
38*700637cbSDimitry Andric     (void)MaxSeconds;
39*700637cbSDimitry Andric     std::shared_lock<std::shared_mutex> Lock(*OwningLock.mutex());
40*700637cbSDimitry Andric     return llvm::WaitForUnlockResult::Success;
41*700637cbSDimitry Andric   }
42*700637cbSDimitry Andric 
unsafeMaybeUnlock()43*700637cbSDimitry Andric   std::error_code unsafeMaybeUnlock() override {
44*700637cbSDimitry Andric     // Unlocking the mutex here would trigger UB and we don't expect this to be
45*700637cbSDimitry Andric     // actually called when compiling scanning modules due to the no-timeout
46*700637cbSDimitry Andric     // guarantee above.
47*700637cbSDimitry Andric     return {};
48*700637cbSDimitry Andric   }
49*700637cbSDimitry Andric 
50*700637cbSDimitry Andric   ~ReaderWriterLock() override = default;
51*700637cbSDimitry Andric };
52*700637cbSDimitry Andric 
53*700637cbSDimitry Andric class InProcessModuleCache : public ModuleCache {
54*700637cbSDimitry Andric   ModuleCacheEntries &Entries;
55*700637cbSDimitry Andric 
56*700637cbSDimitry Andric   // TODO: If we changed the InMemoryModuleCache API and relied on strict
57*700637cbSDimitry Andric   // context hash, we could probably create more efficient thread-safe
58*700637cbSDimitry Andric   // implementation of the InMemoryModuleCache such that it doesn't need to be
59*700637cbSDimitry Andric   // recreated for each translation unit.
60*700637cbSDimitry Andric   InMemoryModuleCache InMemory;
61*700637cbSDimitry Andric 
62*700637cbSDimitry Andric public:
InProcessModuleCache(ModuleCacheEntries & Entries)63*700637cbSDimitry Andric   InProcessModuleCache(ModuleCacheEntries &Entries) : Entries(Entries) {}
64*700637cbSDimitry Andric 
prepareForGetLock(StringRef Filename)65*700637cbSDimitry Andric   void prepareForGetLock(StringRef Filename) override {}
66*700637cbSDimitry Andric 
getLock(StringRef Filename)67*700637cbSDimitry Andric   std::unique_ptr<llvm::AdvisoryLock> getLock(StringRef Filename) override {
68*700637cbSDimitry Andric     auto &CompilationMutex = [&]() -> std::shared_mutex & {
69*700637cbSDimitry Andric       std::lock_guard<std::mutex> Lock(Entries.Mutex);
70*700637cbSDimitry Andric       auto &Entry = Entries.Map[Filename];
71*700637cbSDimitry Andric       if (!Entry)
72*700637cbSDimitry Andric         Entry = std::make_unique<ModuleCacheEntry>();
73*700637cbSDimitry Andric       return Entry->CompilationMutex;
74*700637cbSDimitry Andric     }();
75*700637cbSDimitry Andric     return std::make_unique<ReaderWriterLock>(CompilationMutex);
76*700637cbSDimitry Andric   }
77*700637cbSDimitry Andric 
getModuleTimestamp(StringRef Filename)78*700637cbSDimitry Andric   std::time_t getModuleTimestamp(StringRef Filename) override {
79*700637cbSDimitry Andric     auto &Timestamp = [&]() -> std::atomic<std::time_t> & {
80*700637cbSDimitry Andric       std::lock_guard<std::mutex> Lock(Entries.Mutex);
81*700637cbSDimitry Andric       auto &Entry = Entries.Map[Filename];
82*700637cbSDimitry Andric       if (!Entry)
83*700637cbSDimitry Andric         Entry = std::make_unique<ModuleCacheEntry>();
84*700637cbSDimitry Andric       return Entry->Timestamp;
85*700637cbSDimitry Andric     }();
86*700637cbSDimitry Andric 
87*700637cbSDimitry Andric     return Timestamp.load();
88*700637cbSDimitry Andric   }
89*700637cbSDimitry Andric 
updateModuleTimestamp(StringRef Filename)90*700637cbSDimitry Andric   void updateModuleTimestamp(StringRef Filename) override {
91*700637cbSDimitry Andric     // Note: This essentially replaces FS contention with mutex contention.
92*700637cbSDimitry Andric     auto &Timestamp = [&]() -> std::atomic<std::time_t> & {
93*700637cbSDimitry Andric       std::lock_guard<std::mutex> Lock(Entries.Mutex);
94*700637cbSDimitry Andric       auto &Entry = Entries.Map[Filename];
95*700637cbSDimitry Andric       if (!Entry)
96*700637cbSDimitry Andric         Entry = std::make_unique<ModuleCacheEntry>();
97*700637cbSDimitry Andric       return Entry->Timestamp;
98*700637cbSDimitry Andric     }();
99*700637cbSDimitry Andric 
100*700637cbSDimitry Andric     Timestamp.store(llvm::sys::toTimeT(std::chrono::system_clock::now()));
101*700637cbSDimitry Andric   }
102*700637cbSDimitry Andric 
getInMemoryModuleCache()103*700637cbSDimitry Andric   InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
getInMemoryModuleCache() const104*700637cbSDimitry Andric   const InMemoryModuleCache &getInMemoryModuleCache() const override {
105*700637cbSDimitry Andric     return InMemory;
106*700637cbSDimitry Andric   }
107*700637cbSDimitry Andric };
108*700637cbSDimitry Andric } // namespace
109*700637cbSDimitry Andric 
110*700637cbSDimitry Andric IntrusiveRefCntPtr<ModuleCache>
makeInProcessModuleCache(ModuleCacheEntries & Entries)111*700637cbSDimitry Andric dependencies::makeInProcessModuleCache(ModuleCacheEntries &Entries) {
112*700637cbSDimitry Andric   return llvm::makeIntrusiveRefCnt<InProcessModuleCache>(Entries);
113*700637cbSDimitry Andric }
114