================
@@ -151,68 +156,162 @@ 
DependencyScanningFilesystemSharedCache::getOutOfDateEntries(
   return InvalidDiagInfo;
 }
 
-const CachedFileSystemEntry *
-DependencyScanningFilesystemSharedCache::CacheShard::findEntryByFilename(
-    StringRef Filename) const {
-  assert(llvm::sys::path::is_absolute_gnu(Filename));
-  std::lock_guard<std::mutex> LockGuard(CacheLock);
-  auto It = CacheByFilename.find(Filename);
-  return It == CacheByFilename.end() ? nullptr : It->getValue().first;
-}
-
-const CachedFileSystemEntry *
-DependencyScanningFilesystemSharedCache::CacheShard::findEntryByUID(
-    llvm::sys::fs::UniqueID UID) const {
-  std::lock_guard<std::mutex> LockGuard(CacheLock);
-  auto It = EntriesByUID.find(UID);
-  return It == EntriesByUID.end() ? nullptr : It->getSecond();
-}
-
 const CachedFileSystemEntry &
 DependencyScanningFilesystemSharedCache::CacheShard::
     getOrEmplaceEntryForFilename(StringRef Filename,
                                  llvm::ErrorOr<llvm::vfs::Status> Stat) {
   std::lock_guard<std::mutex> LockGuard(CacheLock);
-  auto [It, Inserted] = CacheByFilename.insert({Filename, {nullptr, nullptr}});
-  auto &[CachedEntry, CachedRealPath] = It->getValue();
-  if (!CachedEntry) {
-    // The entry is not present in the shared cache. Either the cache doesn't
-    // know about the file at all, or it only knows about its real path.
-    assert((Inserted || CachedRealPath) && "existing file with empty pair");
-    CachedEntry =
+  auto [It, Inserted] = CacheByFilename.try_emplace(Filename);
+  auto &State = It->getValue();
+  if (!State.Entry) {
+    // The entry is not present in the shared cache. This method runs only
+    // from inside a producer slot held by the caller, so either the cache
+    // state was just freshly inserted or it already carries an in-flight
+    // slot.
+    assert((Inserted || State.InProgress) &&
+           "cache state should be fresh or carry an in-flight slot held by "
+           "the caller");
+    State.Entry =
         new (EntryStorage.Allocate()) CachedFileSystemEntry(std::move(Stat));
   }
-  return *CachedEntry;
+  return *State.Entry;
+}
+
+DependencyScanningFilesystemSharedCache::SlotAcquisitionResult
+DependencyScanningFilesystemSharedCache::CacheShard::acquireFilenameSlot(
+    StringRef Filename) {
+  assert(llvm::sys::path::is_absolute_gnu(Filename));
+  std::shared_ptr<InProgressEntry> Pending;
+  {
+    std::lock_guard<std::mutex> ShardLock(CacheLock);
+    auto &State = CacheByFilename[Filename];
+
+    // Cache hit.
+    if (State.Entry)
+      return SlotAcquisitionResult{State.Entry, nullptr};
+
+    // No outstanding query: install an in-progress entry and become the
+    // producer.
+    if (!State.InProgress) {
+      State.InProgress = std::make_shared<InProgressEntry>();
+      return SlotAcquisitionResult{std::error_code{}, State.InProgress};
+    }
+
+    // Another worker is producing for this filename. Capture the shared_ptr
+    // by copy so the slot stays alive for us to wait on after we release the
+    // shard lock (the producer resets State.InProgress on publish).
+    Pending = State.InProgress;
+  }
+
+  // Wait for the producer on the slot's own mutex/CV, with the shard lock
+  // released so unrelated keys in this shard are not blocked by our wait, then
+  // adopt its outcome. This holds even for a negative result the producer
+  // chose not to cache: our query overlapped the producer's in time, so we can
+  // share its answer without that constituting caching across separate 
queries.
+  std::unique_lock<std::mutex> EntryLock(Pending->Mutex);
+  Pending->CondVar.wait(EntryLock, [&] { return Pending->Done; });
+  return SlotAcquisitionResult{Pending->Result, nullptr};
+}
+
+DependencyScanningFilesystemSharedCache::SlotAcquisitionResult
+DependencyScanningFilesystemSharedCache::CacheShard::acquireUIDSlot(
+    llvm::sys::fs::UniqueID UID) {
+  std::shared_ptr<InProgressEntry> Pending;
+  {
+    std::lock_guard<std::mutex> ShardLock(CacheLock);
+    auto &State = EntriesByUID[UID];
+
+    // Cache hit.
+    if (State.Entry)
+      return SlotAcquisitionResult{State.Entry, nullptr};
+
+    // No outstanding query: install an in-progress entry and become the
+    // producer.
+    if (!State.InProgress) {
+      State.InProgress = std::make_shared<InProgressEntry>();
+      return SlotAcquisitionResult{std::error_code{}, State.InProgress};
+    }
+
+    // Another worker is producing for this UID. Capture the shared_ptr by copy
+    // so the slot stays alive for us to wait on after we release the shard
+    // lock.
+    Pending = State.InProgress;
+  }
+
+  // Wait for the producer on the slot's own mutex/CV, with the shard lock
+  // released. Unlike the filename slot, a UID producer always publishes a
+  // non-null entry, so the adopted result is never an error.
+  std::unique_lock<std::mutex> EntryLock(Pending->Mutex);
+  Pending->CondVar.wait(EntryLock, [&] { return Pending->Done; });
+  assert(Pending->Result && *Pending->Result &&
+         "in-progress UID slot fulfilled without an entry");
+  return SlotAcquisitionResult{Pending->Result, nullptr};
+}
+
+void DependencyScanningFilesystemSharedCache::CacheShard::fulfillFilenameSlot(
+    StringRef Filename,
+    const std::shared_ptr<
+        DependencyScanningFilesystemSharedCache::InProgressEntry> &IPE,
+    llvm::ErrorOr<const CachedFileSystemEntry *> Result) {
+  {
+    std::lock_guard<std::mutex> ShardLock(CacheLock);
+    auto &State = CacheByFilename[Filename];
+    // Only a resolved entry is recorded for later queries; an uncached
+    // negative stat (an error) is shared with current waiters but not
+    // persisted in the shard.
+    if (Result && !State.Entry)
+      State.Entry = *Result;
+    State.InProgress.reset();
+  }
+  // Publish the result under the slot's own mutex, then notify waiters. 
Waiters
+  // read Done/Result under this same mutex, so the predicate wait cannot miss
+  // the wakeup. The producer's shared_ptr keeps the slot (and CondVar) alive
+  // across notify_all.
+  {
+    std::lock_guard<std::mutex> EntryLock(IPE->Mutex);
+    IPE->Result = Result;
+    IPE->Done = true;
+  }
+  IPE->CondVar.notify_all();
+}
+
+void DependencyScanningFilesystemSharedCache::CacheShard::fulfillUIDSlot(
+    llvm::sys::fs::UniqueID UID,
+    const std::shared_ptr<
+        DependencyScanningFilesystemSharedCache::InProgressEntry> &IPE,
+    const CachedFileSystemEntry *Result) {
+  {
+    std::lock_guard<std::mutex> ShardLock(CacheLock);
----------------
jansvoboda11 wrote:

(Also fine to do it in a follow-up.)

https://github.com/llvm/llvm-project/pull/199680
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to