fix(files): back off the trash monitor's retries while a watch keeps failing

pull/18/head
reionwong 2 weeks ago
parent e96591a649
commit c36f0b983f

@ -10,6 +10,7 @@
#include <linux/capability.h>
#include <map>
#include <sched.h>
#include <sys/inotify.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
@ -931,6 +932,46 @@ TEST_F(FilesTest, TrashMonitorSeesVolumes)
EXPECT_TRUE(waitFor(loop, [&] { return !monitor.isEmpty(); }));
}
// A trash folder that cannot be watched is looked for again less and less often;
// each look reads the mount table.
TEST_F(FilesTest, TrashMonitorBacksOffFailingWatch)
{
TemporaryDirectory dir;
writeFile(dir.filePath("local"));
OperationContext ctx;
LocalFileSystem::trash(local(dir.filePath("local")), ctx);
const auto files = FileSystemUrl::trashRoot() + "/files";
ASSERT_EQ(::chmod(files.c_str(), 0), 0);
struct Restore
{
std::string path;
~Restore() { ::chmod(path.c_str(), 0700); }
} restore{files};
const int inotify = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
ASSERT_GE(inotify, 0);
ASSERT_GE(::inotify_add_watch(inotify, mountTable().c_str(), IN_OPEN), 0);
// Repeated reads of the table merge into one event until the queue is read.
int looks = 0;
{
TrashMonitor monitor;
for (int i = 0; i < 95; ++i) {
wait(loop, 100ms);
inotify_event events[64];
if (::read(inotify, events, sizeof(events)) > 0)
++looks;
}
}
::close(inotify);
// At the start, after two seconds and after four more.
EXPECT_GE(looks, 2);
EXPECT_LE(looks, 3);
}
// A tmpfs in a private user and mount namespace stands in for a second drive.
TEST_F(FilesTest, TrashOnAnotherFilesystem)
{

@ -3,6 +3,7 @@
#include "files/trashmonitor.h"
#include <algorithm>
#include <chrono>
#include <dirent.h>
#include <sys/stat.h>
@ -13,6 +14,9 @@ namespace cutefish::files {
using namespace std::chrono_literals;
static constexpr std::chrono::milliseconds FirstRetry = 2000ms;
static constexpr std::chrono::milliseconds LastRetry = 60000ms;
static bool hasEntries(const std::string& path)
{
DIR* dir = ::opendir(path.c_str());
@ -35,7 +39,8 @@ TrashMonitor::TrashMonitor()
{
m_debounce.setSingleShot(true);
m_debounce.setInterval(40ms);
m_retry.setInterval(2000ms);
m_retry.setSingleShot(true);
m_retry.setInterval(FirstRetry);
m_watchChanged = m_watch.changed.connect([this] {
m_debounce.start();
@ -82,10 +87,14 @@ void TrashMonitor::refresh()
paths.push_back(path);
}
if (m_watch.watch(paths, state.entries, true))
// A watch that keeps failing, past the inotify limit say, is tried less often.
if (m_watch.watch(paths, state.entries, true)) {
m_retry.stop();
else
m_retry.setInterval(FirstRetry);
} else {
m_retry.start();
m_retry.setInterval(std::min<std::chrono::nanoseconds>(m_retry.interval() * 2, LastRetry));
}
if (state.empty != m_empty) {
m_empty = state.empty;

Loading…
Cancel
Save