1 //===--- FileCache.cpp ----------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "support/FileCache.h"
10 #include "llvm/ADT/ScopeExit.h"
16 // Sentinel values for the Size cache key. In both cases, a successful stat of
17 // the file will never result in the cached value being reused.
19 // The cached value does not reflect the current content on disk.
20 static constexpr uint64_t CacheDiskMismatch
=
21 std::numeric_limits
<uint64_t>::max();
22 // The cached value reflects that the file doesn't exist.
23 static constexpr uint64_t FileNotFound
= CacheDiskMismatch
- 1;
25 FileCache::FileCache(llvm::StringRef Path
)
26 : Path(Path
), ValidTime(std::chrono::steady_clock::time_point::min()),
27 ModifiedTime(), Size(CacheDiskMismatch
) {
28 assert(llvm::sys::path::is_absolute(Path
));
32 const ThreadsafeFS
&TFS
, std::chrono::steady_clock::time_point FreshTime
,
33 llvm::function_ref
<void(std::optional
<llvm::StringRef
>)> Parse
,
34 llvm::function_ref
<void()> Read
) const {
36 std::lock_guard
<std::mutex
> Lock(Mu
);
37 // We're going to update the cache and return whatever's in it.
38 auto Return
= llvm::make_scope_exit(Read
);
40 // Return any sufficiently recent result without doing any further work.
41 if (ValidTime
> FreshTime
)
44 // Ensure we always bump ValidTime, so that FreshTime imposes a hard limit on
45 // how often we do IO.
46 auto BumpValidTime
= llvm::make_scope_exit(
47 [&] { ValidTime
= std::chrono::steady_clock::now(); });
49 // stat is cheaper than opening the file. It's usually unchanged.
50 assert(llvm::sys::path::is_absolute(Path
));
51 auto FS
= TFS
.view(/*CWD=*/std::nullopt
);
52 auto Stat
= FS
->status(Path
);
53 if (!Stat
|| !Stat
->isRegularFile()) {
54 if (Size
!= FileNotFound
) // Allow "not found" value to be cached.
56 // Ensure the cache key won't match any future stat().
60 // If the modified-time and size match, assume the content does too.
61 if (Size
== Stat
->getSize() &&
62 ModifiedTime
== Stat
->getLastModificationTime())
65 // OK, the file has actually changed. Update cache key, compute new value.
66 Size
= Stat
->getSize();
67 ModifiedTime
= Stat
->getLastModificationTime();
68 // Now read the file from disk.
69 if (auto Buf
= FS
->getBufferForFile(Path
)) {
70 Parse(Buf
->get()->getBuffer());
71 // Result is cacheable if the actual read size matches the new cache key.
72 // (We can't update the cache key, because we don't know the new mtime).
73 if (Buf
->get()->getBufferSize() != Size
)
74 Size
= CacheDiskMismatch
;
76 // File was unreadable. Keep the old value and try again next time.
77 Size
= CacheDiskMismatch
;