121 lines · cpp
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-exception6//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 17using namespace clang;18using namespace tooling;19using namespace dependencies;20 21namespace {22class 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 26public: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::WaitForUnlockResult33 waitForUnlockFor(std::chrono::seconds MaxSeconds) override {34 assert(!OwningLock);35 // We do not respect the timeout here. It's very generous for implicit36 // modules, so we'd typically only reach it if the owner crashed (but so did37 // 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 be45 // actually called when compiling scanning modules due to the no-timeout46 // guarantee above.47 return {};48 }49 50 ~ReaderWriterLock() override = default;51};52 53class InProcessModuleCache : public ModuleCache {54 ModuleCacheEntries &Entries;55 56 // TODO: If we changed the InMemoryModuleCache API and relied on strict57 // context hash, we could probably create more efficient thread-safe58 // implementation of the InMemoryModuleCache such that it doesn't need to be59 // recreated for each translation unit.60 InMemoryModuleCache InMemory;61 62public: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 void maybePrune(StringRef Path, time_t PruneInterval,104 time_t PruneAfter) override {105 // FIXME: This only needs to be ran once per build, not in every106 // compilation. Call it once per service.107 maybePruneImpl(Path, PruneInterval, PruneAfter);108 }109 110 InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }111 const InMemoryModuleCache &getInMemoryModuleCache() const override {112 return InMemory;113 }114};115} // namespace116 117IntrusiveRefCntPtr<ModuleCache>118dependencies::makeInProcessModuleCache(ModuleCacheEntries &Entries) {119 return llvm::makeIntrusiveRefCnt<InProcessModuleCache>(Entries);120}121