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: ReaderWriterLock(std::shared_mutex & Mutex)27 ReaderWriterLock(std::shared_mutex &Mutex) 28 : OwningLock(Mutex, std::defer_lock) {} 29 tryLock()30 Expected<bool> tryLock() override { return OwningLock.try_lock(); } 31 32 llvm::WaitForUnlockResult waitForUnlockFor(std::chrono::seconds MaxSeconds)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 unsafeMaybeUnlock()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: InProcessModuleCache(ModuleCacheEntries & Entries)63 InProcessModuleCache(ModuleCacheEntries &Entries) : Entries(Entries) {} 64 prepareForGetLock(StringRef Filename)65 void prepareForGetLock(StringRef Filename) override {} 66 getLock(StringRef Filename)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 getModuleTimestamp(StringRef Filename)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 updateModuleTimestamp(StringRef Filename)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 getInMemoryModuleCache()103 InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; } getInMemoryModuleCache() const104 const InMemoryModuleCache &getInMemoryModuleCache() const override { 105 return InMemory; 106 } 107 }; 108 } // namespace 109 110 IntrusiveRefCntPtr<ModuleCache> makeInProcessModuleCache(ModuleCacheEntries & Entries)111dependencies::makeInProcessModuleCache(ModuleCacheEntries &Entries) { 112 return llvm::makeIntrusiveRefCnt<InProcessModuleCache>(Entries); 113 } 114