diff --git a/main.cpp b/main.cpp
index 8b0ef299c91eb65c7ba57c52112fe7fd4c2db8dc..b0b86680e467d2d68a3325e28fedbc86a7ee7737 100644
--- a/main.cpp
+++ b/main.cpp
@@ -26,6 +26,57 @@ static void collect_paths(const std::string& root) {
 		paths.emplace_back(new std::string(p.path()));
 }
 
+static auto submit_and_wait(struct io_uring* ring) -> int {
+#ifdef NDEBUG
+	__attribute__((unused))
+#endif
+	int submitted = io_uring_submit_and_wait(ring, 1);
+	assert(submitted == 1);
+
+	struct io_uring_cqe* cqe;
+	if (io_uring_peek_cqe(ring, &cqe) < 0) err(EXIT_FAILURE, "failed to peek cqe");
+
+	int res = cqe->res;
+	if (res < 0) {
+		errno = -res;
+		err(EXIT_FAILURE, "opening failed");
+	}
+
+	io_uring_cqe_seen(ring, cqe);
+	return res;
+}
+
+static void close_fd(struct io_uring* ring, int fd) {
+	struct io_uring_sqe* sqe = io_uring_get_sqe(ring);
+	assert(sqe);
+
+	io_uring_prep_close(sqe, fd);
+	sqe->flags |= IOSQE_CQE_SKIP_SUCCESS;
+
+#ifdef NDEBUG
+	__attribute__((unused))
+#endif
+	int submitted = io_uring_submit(ring);
+	assert(submitted == 1);
+}
+
+static void open_global(struct io_uring* ring, const char* path) {
+	struct io_uring_sqe* sqe = io_uring_get_sqe(ring);
+	assert(sqe);
+	io_uring_prep_openat(sqe, AT_FDCWD, path, O_RDONLY, 0);
+
+	int fd = submit_and_wait(ring);
+	close_fd(ring, fd);
+}
+
+static void open_direct(struct io_uring* ring, const char* path, unsigned slot) {
+	struct io_uring_sqe* sqe = io_uring_get_sqe(ring);
+	assert(sqe);
+	io_uring_prep_openat_direct(sqe, AT_FDCWD, path, O_RDONLY, 0, slot);
+
+	submit_and_wait(ring);
+}
+
 static void thread_func(pthread_barrier_t& init_barrier, unsigned id, bool direct) {
 	struct io_uring ring;
 	if (io_uring_queue_init(URING_ENTRIES, &ring, 0) < 0)
@@ -48,41 +99,10 @@ static void thread_func(pthread_barrier_t& init_barrier, unsigned id, bool direc
 		const auto* p = paths[paths_offset + i];
 		assert(p->c_str());
 
-		struct io_uring_sqe* sqe = io_uring_get_sqe(&ring);
-		assert(sqe);
-
 		if (direct) {
-			io_uring_prep_openat_direct(sqe, AT_FDCWD, p->c_str(), O_RDONLY, 0,
-																	i % URING_FILE_TABLE_SIZE);
+			open_direct(&ring, p->c_str(), i % URING_FILE_TABLE_SIZE);
 		} else {
-			io_uring_prep_openat(sqe, AT_FDCWD, p->c_str(), O_RDONLY, 0);
-		}
-
-#ifdef NDEBUG
-		__attribute__((unused))
-#endif
-		int submitted = io_uring_submit_and_wait(&ring, 1);
-		assert(submitted == 1);
-
-		struct io_uring_cqe* cqe;
-		if (io_uring_peek_cqe(&ring, &cqe) < 0) err(EXIT_FAILURE, "failed to peek cqe");
-
-		int res = cqe->res;
-		if (res < 0) {
-			errno = -res;
-			err(EXIT_FAILURE, "%d: %sopening %s failed", id, direct ? "directly " : "", p->c_str());
-		}
-
-		io_uring_cqe_seen(&ring, cqe);
-
-		if (!direct) {
-			sqe = io_uring_get_sqe(&ring);
-			assert(sqe);
-			io_uring_prep_close(sqe, res);
-			sqe->flags |= IOSQE_CQE_SKIP_SUCCESS;
-
-			submitted = io_uring_submit(&ring);
-			assert(submitted == 1);
+			open_global(&ring, p->c_str());
 		}
 	}
 }