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 Andricdependencies::makeInProcessModuleCache(ModuleCacheEntries &Entries) { 112*700637cbSDimitry Andric return llvm::makeIntrusiveRefCnt<InProcessModuleCache>(Entries); 113*700637cbSDimitry Andric } 114