xref: /llvm-project/clang-tools-extra/clangd/support/FileCache.h (revision 2c675be9b232c1d0b5c55cbcb196e71036c681ea)
1d95db169SSam McCall //===--- FileCache.h - Revalidating cache of data from disk ------*- C++-*-===//
2d95db169SSam McCall //
3d95db169SSam McCall // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4d95db169SSam McCall // See https://llvm.org/LICENSE.txt for license information.
5d95db169SSam McCall // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d95db169SSam McCall //
7d95db169SSam McCall //===----------------------------------------------------------------------===//
8d95db169SSam McCall 
9d95db169SSam McCall #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FILECACHE_H
10d95db169SSam McCall #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FILECACHE_H
11d95db169SSam McCall 
12d95db169SSam McCall #include "Path.h"
13d95db169SSam McCall #include "ThreadsafeFS.h"
14d95db169SSam McCall #include "llvm/Support/Chrono.h"
15d95db169SSam McCall #include <mutex>
16*2c675be9SKazu Hirata #include <optional>
17d95db169SSam McCall 
18d95db169SSam McCall namespace clang {
19d95db169SSam McCall namespace clangd {
20d95db169SSam McCall 
21d95db169SSam McCall /// Base class for threadsafe cache of data read from a file on disk.
22d95db169SSam McCall ///
23d95db169SSam McCall /// We want configuration files to be "live" as much as possible.
24d95db169SSam McCall /// Reading them every time is simplest, but caching solves a few problems:
25d95db169SSam McCall ///  - reading and parsing is cheap but not free (and happens on hot paths)
26d95db169SSam McCall ///  - we can ignore invalid data and use the old value (we may see truncated
27d95db169SSam McCall ///    compile_commands.json from non-atomic writers)
28d95db169SSam McCall ///  - we avoid reporting the same errors repeatedly
29d95db169SSam McCall ///
30d95db169SSam McCall /// We still read and parse the data synchronously on demand, but skip as much
31d95db169SSam McCall /// work as possible:
32d95db169SSam McCall ///  - if not enough wall-time has elapsed, assume the data is still up-to-date
33d95db169SSam McCall ///  - if we stat the file and it has the same mtime + size, don't read it
34d95db169SSam McCall ///  - obviously we only have to parse when we re-read the file
35d95db169SSam McCall /// (Tracking OS change events is an alternative, but difficult to do portably.)
36d95db169SSam McCall ///
37d95db169SSam McCall /// Caches for particular data (e.g. compilation databases) should inherit and:
38d95db169SSam McCall ///  - add mutable storage for the cached parsed data
39d95db169SSam McCall ///  - add a public interface implemented on top of read()
40d95db169SSam McCall class FileCache {
41d95db169SSam McCall protected:
42d95db169SSam McCall   // Path must be absolute.
43d95db169SSam McCall   FileCache(PathRef Path);
44d95db169SSam McCall 
45d95db169SSam McCall   // Updates the cached value if needed, then provides threadsafe access to it.
46d95db169SSam McCall   //
47d95db169SSam McCall   // Specifically:
48d95db169SSam McCall   // - Parse() may be called (if the cache was not up-to-date)
49d95db169SSam McCall   //   The lock is held, so cache storage may be safely written.
50d95db169SSam McCall   //   Parse(None) means the file doesn't exist.
51d95db169SSam McCall   // - Read() will always be called, to provide access to the value.
52d95db169SSam McCall   //   The lock is again held, so the value can be copied or used.
53d95db169SSam McCall   //
54d95db169SSam McCall   // If the last Parse is newer than FreshTime, we don't check metadata.
55d95db169SSam McCall   //   - time_point::min() means we only do IO if we never read the file before
56d95db169SSam McCall   //   - time_point::max() means we always at least stat the file
57d95db169SSam McCall   //   - steady_clock::now() + seconds(1) means we accept 1 second of staleness
58d95db169SSam McCall   void read(const ThreadsafeFS &TFS,
59d95db169SSam McCall             std::chrono::steady_clock::time_point FreshTime,
60*2c675be9SKazu Hirata             llvm::function_ref<void(std::optional<llvm::StringRef>)> Parse,
61d95db169SSam McCall             llvm::function_ref<void()> Read) const;
62d95db169SSam McCall 
path()63d95db169SSam McCall   PathRef path() const { return Path; }
64d95db169SSam McCall 
65d95db169SSam McCall private:
66d95db169SSam McCall   std::string Path;
67d95db169SSam McCall   // Members are mutable so read() can present a const interface.
68d95db169SSam McCall   // (It is threadsafe and approximates read-through to TFS).
69d95db169SSam McCall   mutable std::mutex Mu;
70d95db169SSam McCall   // Time when the cache was known valid (reflected disk state).
71d95db169SSam McCall   mutable std::chrono::steady_clock::time_point ValidTime;
72d95db169SSam McCall   // Filesystem metadata corresponding to the currently cached data.
73a38d13edSSam McCall   mutable llvm::sys::TimePoint<> ModifiedTime;
74d95db169SSam McCall   mutable uint64_t Size;
75d95db169SSam McCall };
76d95db169SSam McCall 
77d95db169SSam McCall } // namespace clangd
78d95db169SSam McCall } // namespace clang
79d95db169SSam McCall 
80d95db169SSam McCall #endif
81