summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/inotify.cc
diff options
context:
space:
mode:
authorFabricio Voznika <fvoznika@google.com>2018-12-27 14:58:41 -0800
committerShentubot <shentubot@google.com>2018-12-27 14:59:50 -0800
commit46e6577014c849d7306c63905db25f3c695fa7e7 (patch)
treef0beec40f3bbda38bf209809b091cf537b66cba8 /test/syscalls/linux/inotify.cc
parentbce2f9751f415da869d04ccb53833b024373666d (diff)
Fix deadlock between epoll_wait and getdents
epoll_wait acquires EventPoll.listsMu (in EventPoll.ReadEvents) and then calls Inotify.Readiness which tries to acquire Inotify.evMu. getdents acquires Inotify.evMu (in Inotify.queueEvent) and then calls readyCallback.Callback which tries to acquire EventPoll.listsMu. The fix is to release Inotify.evMu before calling Queue.Notify. Queue is thread-safe and doesn't require Inotify.evMu to be held. Closes #121 PiperOrigin-RevId: 227066695 Change-Id: Id29364bb940d1727f33a5dff9a3c52f390c15761
Diffstat (limited to 'test/syscalls/linux/inotify.cc')
-rw-r--r--test/syscalls/linux/inotify.cc55
1 files changed, 55 insertions, 0 deletions
diff --git a/test/syscalls/linux/inotify.cc b/test/syscalls/linux/inotify.cc
index 0e361496c..167ca44a8 100644
--- a/test/syscalls/linux/inotify.cc
+++ b/test/syscalls/linux/inotify.cc
@@ -14,9 +14,12 @@
#include <fcntl.h>
#include <libgen.h>
+#include <sched.h>
+#include <sys/epoll.h>
#include <sys/inotify.h>
#include <sys/ioctl.h>
+#include <atomic>
#include <list>
#include <string>
#include <vector>
@@ -24,6 +27,9 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+#include "test/util/epoll_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
#include "test/util/temp_path.h"
@@ -1512,6 +1518,55 @@ TEST(Inotify, ControlEvents) {
ASSERT_THAT(events2, Are({}));
}
+// Regression test to ensure epoll and directory access doesn't deadlock.
+TEST(Inotify, EpollNoDeadlock) {
+ const DisableSave ds; // Too many syscalls.
+
+ const FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK));
+
+ // Create lots of directories and watch all of them.
+ const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ std::vector<TempPath> children;
+ for (size_t i = 0; i < 1000; ++i) {
+ auto child = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path()));
+ ASSERT_NO_ERRNO_AND_VALUE(
+ InotifyAddWatch(fd.get(), child.path(), IN_ACCESS));
+ children.emplace_back(std::move(child));
+ }
+
+ // Run epoll_wait constantly in a separate thread.
+ std::atomic<bool> done(false);
+ ScopedThread th([&fd, &done] {
+ for (auto start = absl::Now(); absl::Now() - start < absl::Seconds(5);) {
+ FileDescriptor epoll_fd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
+ ASSERT_NO_ERRNO(RegisterEpollFD(epoll_fd.get(), fd.get(),
+ EPOLLIN | EPOLLOUT | EPOLLET, 0));
+ struct epoll_event result[1];
+ EXPECT_THAT(RetryEINTR(epoll_wait)(epoll_fd.get(), result, 1, -1),
+ SyscallSucceedsWithValue(1));
+
+ sched_yield();
+ }
+ done = true;
+ });
+
+ // While epoll thread is running, constantly access all directories to
+ // generate inotify events.
+ while (!done) {
+ std::vector<std::string> files =
+ ASSERT_NO_ERRNO_AND_VALUE(ListDir(root.path(), false));
+ ASSERT_EQ(files.size(), 1002);
+ for (const auto& child : files) {
+ if (child == "." || child == "..") {
+ continue;
+ }
+ ASSERT_NO_ERRNO_AND_VALUE(ListDir(JoinPath(root.path(), child), false));
+ }
+ sched_yield();
+ }
+}
+
} // namespace
} // namespace testing
} // namespace gvisor