Skip to content

Commit

Permalink
Walk: enforce MAX_STAT during ScanDirectory()
Browse files Browse the repository at this point in the history
As a quick kludge, this commit creates a copy of ScanDirectory() as a
coroutine so we can `co_await resume_stat` (we stillneed the
synchronous version for method Start()).  The coroutine copy can then
enforce `MAX_STAT` to limit the io_uring statx() concurrency; this is
necessary because cache directories with millions of cached files
would cause the kernel to consume gigabytes of memory and exceed our
memory limit.
  • Loading branch information
MaxKellermann committed Dec 12, 2024
1 parent e5b7550 commit 7d39829
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 2 deletions.
2 changes: 1 addition & 1 deletion debian/changelog
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cm4all-cash (0.5) unstable; urgency=low

*
* throttle scanning huge directories (reduce memory usage)

--

Expand Down
24 changes: 23 additions & 1 deletion src/Walk.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,34 @@ IsSpecialFilename(const char *s) noexcept

inline void
Walk::ScanDirectory(WalkDirectory &directory, UniqueFileDescriptor &&fd)
{
// TODO eliminate this method, use only CoScanDirectory

DirectoryReader r{std::move(fd)};
while (const char *name = r.Read()) {
if (IsSpecialFilename(name))
continue;

auto *item = new StatItem(*this, directory, name);
stat.push_back(*item);

item->Start(uring);
}
}

inline Co::Task<void>
Walk::CoScanDirectory(WalkDirectory &directory, UniqueFileDescriptor &&fd)
{
DirectoryReader r{std::move(fd)};
while (const char *name = r.Read()) {
if (IsSpecialFilename(name))
continue;

/* throttle if there are too many concurrent statx
system calls */
while (stat.size() > MAX_STAT) [[unlikely]]
co_await resume_stat;

auto *item = new StatItem(*this, directory, name);
stat.push_back(*item);

Expand All @@ -159,7 +181,7 @@ try {
*new WalkDirectory(uring, parent, co_await Uring::CoOpen(uring, parent.fd, name.c_str(), O_PATH|O_DIRECTORY, 0)),
};

ScanDirectory(*directory, co_await Uring::CoOpen(uring, directory->fd, ".", O_DIRECTORY, 0));
co_await CoScanDirectory(*directory, co_await Uring::CoOpen(uring, directory->fd, ".", O_DIRECTORY, 0));
} catch (...) {
fmt::print(stderr, "Failed to scan directory: {}\n", std::current_exception());
}
Expand Down
1 change: 1 addition & 0 deletions src/Walk.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ private:
FileTime atime, uint_least64_t size);

void ScanDirectory(WalkDirectory &directory, UniqueFileDescriptor &&fd);
Co::Task<void> CoScanDirectory(WalkDirectory &directory, UniqueFileDescriptor &&fd);

void OnStatCompletion(StatItem &item) noexcept;
};

0 comments on commit 7d39829

Please sign in to comment.