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