aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Tooling/DependencyScanning/InProcessModuleCache.cpp
blob: 80db2d47d940e9e78d537a8247c9b8665c5ca440 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "clang/Tooling/DependencyScanning/InProcessModuleCache.h"

#include "clang/Serialization/InMemoryModuleCache.h"
#include "llvm/Support/AdvisoryLock.h"
#include "llvm/Support/Chrono.h"

#include <mutex>

using namespace clang;
using namespace tooling;
using namespace dependencies;

namespace {
class ReaderWriterLock : public llvm::AdvisoryLock {
  // TODO: Consider using std::atomic::{wait,notify_all} when we move to C++20.
  std::unique_lock<std::shared_mutex> OwningLock;

public:
  ReaderWriterLock(std::shared_mutex &Mutex)
      : OwningLock(Mutex, std::defer_lock) {}

  Expected<bool> tryLock() override { return OwningLock.try_lock(); }

  llvm::WaitForUnlockResult
  waitForUnlockFor(std::chrono::seconds MaxSeconds) override {
    assert(!OwningLock);
    // We do not respect the timeout here. It's very generous for implicit
    // modules, so we'd typically only reach it if the owner crashed (but so did
    // we, since we run in the same process), or encountered deadlock.
    (void)MaxSeconds;
    std::shared_lock<std::shared_mutex> Lock(*OwningLock.mutex());
    return llvm::WaitForUnlockResult::Success;
  }

  std::error_code unsafeMaybeUnlock() override {
    // Unlocking the mutex here would trigger UB and we don't expect this to be
    // actually called when compiling scanning modules due to the no-timeout
    // guarantee above.
    return {};
  }

  ~ReaderWriterLock() override = default;
};

class InProcessModuleCache : public ModuleCache {
  ModuleCacheEntries &Entries;

  // TODO: If we changed the InMemoryModuleCache API and relied on strict
  // context hash, we could probably create more efficient thread-safe
  // implementation of the InMemoryModuleCache such that it doesn't need to be
  // recreated for each translation unit.
  InMemoryModuleCache InMemory;

public:
  InProcessModuleCache(ModuleCacheEntries &Entries) : Entries(Entries) {}

  void prepareForGetLock(StringRef Filename) override {}

  std::unique_ptr<llvm::AdvisoryLock> getLock(StringRef Filename) override {
    auto &CompilationMutex = [&]() -> std::shared_mutex & {
      std::lock_guard<std::mutex> Lock(Entries.Mutex);
      auto &Entry = Entries.Map[Filename];
      if (!Entry)
        Entry = std::make_unique<ModuleCacheEntry>();
      return Entry->CompilationMutex;
    }();
    return std::make_unique<ReaderWriterLock>(CompilationMutex);
  }

  std::time_t getModuleTimestamp(StringRef Filename) override {
    auto &Timestamp = [&]() -> std::atomic<std::time_t> & {
      std::lock_guard<std::mutex> Lock(Entries.Mutex);
      auto &Entry = Entries.Map[Filename];
      if (!Entry)
        Entry = std::make_unique<ModuleCacheEntry>();
      return Entry->Timestamp;
    }();

    return Timestamp.load();
  }

  void updateModuleTimestamp(StringRef Filename) override {
    // Note: This essentially replaces FS contention with mutex contention.
    auto &Timestamp = [&]() -> std::atomic<std::time_t> & {
      std::lock_guard<std::mutex> Lock(Entries.Mutex);
      auto &Entry = Entries.Map[Filename];
      if (!Entry)
        Entry = std::make_unique<ModuleCacheEntry>();
      return Entry->Timestamp;
    }();

    Timestamp.store(llvm::sys::toTimeT(std::chrono::system_clock::now()));
  }

  InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
  const InMemoryModuleCache &getInMemoryModuleCache() const override {
    return InMemory;
  }
};
} // namespace

IntrusiveRefCntPtr<ModuleCache>
dependencies::makeInProcessModuleCache(ModuleCacheEntries &Entries) {
  return llvm::makeIntrusiveRefCnt<InProcessModuleCache>(Entries);
}