Blender V5.0
memory_cache_file_load.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <optional>
6
7#include "BLI_fileops.hh"
8#include "BLI_hash.hh"
9#include "BLI_map.hh"
11#include "BLI_mutex.hh"
12#include "BLI_task.hh"
13#include "BLI_vector.hh"
14#include "BLI_vector_set.hh"
15
16namespace blender::memory_cache {
17
21class LoadFileKey : public GenericKey {
22 private:
24 Vector<std::string> file_paths_;
29 std::shared_ptr<const GenericKey> loader_key_;
30
31 public:
32 LoadFileKey(Vector<std::string> file_paths, std::shared_ptr<const GenericKey> loader_key)
33 : file_paths_(std::move(file_paths)), loader_key_(std::move(loader_key))
34 {
35 }
36
38 {
39 return file_paths_;
40 }
41
42 uint64_t hash() const override
43 {
44 return get_default_hash(file_paths_, *loader_key_);
45 }
46
47 friend bool operator==(const LoadFileKey &a, const LoadFileKey &b)
48 {
49 return a.file_paths_ == b.file_paths_ && *a.loader_key_ == *b.loader_key_;
50 }
51
52 bool equal_to(const GenericKey &other) const override
53 {
54 if (const auto *other_typed = dynamic_cast<const LoadFileKey *>(&other)) {
55 return *this == *other_typed;
56 }
57 return false;
58 }
59
60 std::unique_ptr<GenericKey> to_storable() const override
61 {
62 /* Currently #LoadFileKey is always storable, i.e. it owns all the data it references. A
63 * potential future optimization could be to support just referencing the paths and loader key,
64 * but that causes some boilerplate now that is not worth it. */
65 return std::make_unique<LoadFileKey>(*this);
66 }
67};
68
69static std::optional<int64_t> get_file_modification_time(const StringRefNull path)
70{
71 BLI_stat_t stat;
72 if (BLI_stat(path.c_str(), &stat) == -1) {
73 return std::nullopt;
74 }
75 return stat.st_mtime;
76}
77
82
84{
85 static FileStatMap file_stat_map;
86 return file_stat_map;
87}
88
90{
91 FileStatMap &file_stat_map = get_file_stat_map();
92
93 /* Retrieve the file modification times before the lock because there is no need for the lock
94 * yet. While not guaranteed, retrieving the modification time is often optimized by the OS so
95 * that no actual access to the hard drive is necessary. */
96 Array<std::optional<int64_t>> new_times(file_paths.size());
97 for (const int i : file_paths.index_range()) {
98 new_times[i] = get_file_modification_time(file_paths[i]);
99 }
100
101 std::lock_guard lock{file_stat_map.mutex};
102
103 /* Find all paths that have changed on disk. */
104 VectorSet<StringRefNull> outdated_paths;
105 for (const int i : file_paths.index_range()) {
106 const StringRefNull path = file_paths[i];
107 const std::optional<int64_t> new_time = new_times[i];
108 std::optional<int64_t> &old_time = file_stat_map.map.lookup_or_add_as(path, new_time);
109 if (old_time != new_time) {
110 outdated_paths.add(path);
111 old_time = new_time;
112 }
113 }
114 /* If any referenced file was changed, invalidate the caches that use it. */
115 if (!outdated_paths.is_empty()) {
116 /* Isolate because a mutex is locked. */
118 /* Invalidation is done while the mutex is locked so that other threads won't see the old
119 * cached value anymore after we've detected that it's outdated. */
120 memory_cache::remove_if([&](const GenericKey &other_key) {
121 if (const auto *other_key_typed = dynamic_cast<const LoadFileKey *>(&other_key)) {
122 const Span<std::string> other_key_paths = other_key_typed->file_paths();
123 return std::any_of(
124 other_key_paths.begin(), other_key_paths.end(), [&](const StringRefNull path) {
125 return outdated_paths.contains(path);
126 });
127 }
128 return false;
129 });
130 });
131 }
132}
133
134std::shared_ptr<CachedValue> get_loaded_base(const GenericKey &loader_key,
135 Span<StringRefNull> file_paths,
136 FunctionRef<std::unique_ptr<CachedValue>()> load_fn)
137{
139 const LoadFileKey key{file_paths, loader_key.to_storable()};
140 return memory_cache::get_base(key, load_fn);
141}
142
143} // namespace blender::memory_cache
int BLI_stat(const char *path, BLI_stat_t *buffer) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
struct stat BLI_stat_t
File and directory operations.
volatile int lock
unsigned long long int uint64_t
Value & lookup_or_add_as(ForwardKey &&key, ForwardValue &&...value)
Definition BLI_map.hh:605
virtual std::unique_ptr< GenericKey > to_storable() const =0
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr const T * begin() const
Definition BLI_span.hh:220
constexpr const char * c_str() const
bool add(const Key &key)
bool equal_to(const GenericKey &other) const override
friend bool operator==(const LoadFileKey &a, const LoadFileKey &b)
LoadFileKey(Vector< std::string > file_paths, std::shared_ptr< const GenericKey > loader_key)
std::unique_ptr< GenericKey > to_storable() const override
std::shared_ptr< CachedValue > get_base(const GenericKey &key, FunctionRef< std::unique_ptr< CachedValue >()> compute_fn)
void remove_if(FunctionRef< bool(const GenericKey &)> predicate)
static FileStatMap & get_file_stat_map()
std::shared_ptr< CachedValue > get_loaded_base(const GenericKey &loader_key, Span< StringRefNull > file_paths, FunctionRef< std::unique_ptr< CachedValue >()> load_fn)
static std::optional< int64_t > get_file_modification_time(const StringRefNull path)
static void invalidate_outdated_caches_if_necessary(const Span< StringRefNull > file_paths)
void isolate_task(const Function &function)
Definition BLI_task.hh:248
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
std::mutex Mutex
Definition BLI_mutex.hh:47
Map< std::string, std::optional< int64_t > > map
i
Definition text_draw.cc:230