From 55c2f3a79b70a70410f9d1938ab07a24b0cf064f Mon Sep 17 00:00:00 2001
From: Florian Fischer <florian.fl.fischer@fau.de>
Date: Fri, 5 Feb 2021 12:44:16 +0100
Subject: [PATCH] add callback based fsearch implementation

---
 apps/fsearch/fsearch_callback.cpp | 127 ++++++++++++++++++++++++++++++
 apps/fsearch/meson.build          |   6 ++
 2 files changed, 133 insertions(+)
 create mode 100644 apps/fsearch/fsearch_callback.cpp

diff --git a/apps/fsearch/fsearch_callback.cpp b/apps/fsearch/fsearch_callback.cpp
new file mode 100644
index 00000000..b5961cf5
--- /dev/null
+++ b/apps/fsearch/fsearch_callback.cpp
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright © 2021 Florian Fischer
+#include <fcntl.h>
+
+#include <array>
+#include <cassert>
+#include <cerrno>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <filesystem>
+#include <iostream>
+#include <string>
+#include <utility>
+
+#include "Common.hpp"
+#include "CountingPrivateSemaphore.hpp"
+#include "Fiber.hpp"
+#include "Runtime.hpp"
+#include "emper.hpp"
+#include "io.hpp"
+#include "io/Future.hpp"
+
+namespace fs = std::filesystem;
+
+using emper::io::OpenatFuture;
+using emper::io::ReadFuture;
+
+const size_t EMPER_RIPGREP_BUFSIZE = 4096;
+
+const char* needle;
+size_t needle_len;
+
+class FileSearcher {
+	std::string path;
+	int fd = -1;
+	std::array<char, EMPER_RIPGREP_BUFSIZE> buf;
+	CPS& finished;
+
+ public:
+	FileSearcher(std::string path, CPS& finished) : path(std::move(path)), finished(finished) {}
+	~FileSearcher() {
+		if (fd > 0) {
+			emper::io::closeAndForget(fd);
+			fd = -1;
+		}
+
+		finished.signalAndExit();
+	}
+
+	void kickoffSearch() {
+		OpenatFuture f(AT_FDCWD, path.c_str(), O_RDONLY);
+		f.setCallback([this](int32_t fd) {
+			if (fd < 0) {
+				errno = -fd;
+				DIE_MSG_ERRNO("open failed");
+			}
+
+			this->fd = fd;
+			submitRead();
+		});
+
+		f.submit();
+	}
+
+	void onRead(int32_t bytes_read) {
+		if (bytes_read <= 0) {
+			// file was fully searched
+			if (bytes_read == 0) {
+				delete this;
+				return;
+			}
+
+			errno = -bytes_read;
+			DIE_MSG_ERRNO("read failed");
+		}
+
+		if (memmem(&buf[0], bytes_read, needle, needle_len)) {
+			printf("%s\n", path.c_str());
+			delete this;
+			return;
+		}
+
+		submitRead();
+	}
+
+	void submitRead() {
+		assert(fd > 0);
+		ReadFuture f(fd, buf.data(), EMPER_RIPGREP_BUFSIZE, -1);
+		f.setCallback([this](int32_t bytes_read) { this->onRead(bytes_read); });
+		f.submit();
+	}
+};
+
+void walk_dir() {
+	CPS cps;
+	for (const auto& p : fs::recursive_directory_iterator(".")) {
+		if (!p.is_regular_file()) {
+			continue;
+		}
+
+		cps.incrementCounterByOne();
+		auto* searcher = new FileSearcher(p.path(), cps);
+		async([searcher] { searcher->kickoffSearch(); });
+	}
+
+	cps.wait();
+	exit(EXIT_SUCCESS);
+}
+
+auto main(int argc, char* argv[]) -> int {
+	if (argc < 2) {
+		std::cerr << "Usage: " << argv[0] << " <needle>" << std::endl;
+		return EXIT_FAILURE;
+	}
+
+	needle = argv[1];
+	needle_len = strlen(needle);
+
+	Runtime runtime;
+
+	auto* dirWalker = Fiber::from(walk_dir);
+	runtime.scheduleFromAnywhere(*dirWalker);
+
+	runtime.waitUntilFinished();
+}
diff --git a/apps/fsearch/meson.build b/apps/fsearch/meson.build
index b5b548e0..9dc41f5c 100644
--- a/apps/fsearch/meson.build
+++ b/apps/fsearch/meson.build
@@ -3,3 +3,9 @@ fsearch_exe = executable(
   'fsearch.cpp',
   dependencies: emper_dep,
 )
+
+fsearch_callback_exe = executable(
+  'fsearch_callback',
+  'fsearch_callback.cpp',
+  dependencies: emper_dep,
+)
-- 
GitLab