diff --git a/apps/fsearch/fsearch.cpp b/apps/fsearch/fsearch.cpp
index 09f479d1baf43222ad10a933e4ed99dcd9db069b..8ef73c0adb1eca949897986737df94fce9acfdfe 100644
--- a/apps/fsearch/fsearch.cpp
+++ b/apps/fsearch/fsearch.cpp
@@ -15,6 +15,7 @@
 #include "CountingPrivateSemaphore.hpp"
 #include "Fiber.hpp"
 #include "Runtime.hpp"
+#include "Semaphore.hpp"
 #include "emper.hpp"
 #include "io.hpp"
 
@@ -25,7 +26,13 @@ namespace fs = std::filesystem;
 const char* needle;
 size_t needle_len;
 
+emper::Semaphore* max_running;
+
 void search(const std::string& path) {
+	if (max_running) {
+		max_running->acquire();
+	}
+
 	int fd = emper::io::openAndWait(path.c_str(), O_RDONLY);
 	if (fd < 0) {
 		DIE_MSG_ERRNO("open failed");
@@ -38,7 +45,7 @@ void search(const std::string& path) {
 	while (bytes_read > 0) {
 		if (memmem(&buf[0], bytes_read, needle, needle_len)) {
 			printf("%s\n", path.c_str());
-			return;
+			goto out;
 		}
 
 		bytes_searched += bytes_read;
@@ -48,6 +55,13 @@ void search(const std::string& path) {
 	if (bytes_read < 0) {
 		DIE_MSG_ERRNO("read failed");
 	}
+
+out:
+	if (max_running) {
+		max_running->release();
+	}
+
+	emper::io::closeAndForget(fd);
 }
 
 void walk_dir() {
@@ -64,8 +78,12 @@ void walk_dir() {
 
 auto main(int argc, char* argv[]) -> int {
 	if (argc < 2) {
-		std::cerr << "Usage: " << argv[0] << " <needle>" << std::endl;
-		return EXIT_FAILURE;
+		std::cerr << "Usage: " << argv[0] << " <needle> [max fibers]" << std::endl;
+		return EXIT_SUCCESS;
+	}
+
+	if (argc == 3) {
+		max_running = new emper::Semaphore(std::stoi(argv[2]));
 	}
 
 	needle = argv[1];
@@ -77,4 +95,5 @@ auto main(int argc, char* argv[]) -> int {
 	runtime.scheduleFromAnywhere(*dirWalker);
 
 	runtime.waitUntilFinished();
+	return EXIT_SUCCESS;
 }
diff --git a/emper/Semaphore.hpp b/emper/Semaphore.hpp
index 0ec2340e90ea931b2fdf15b6bf705c4b5d93b429..d68919f4dd5e68abaf1d3af245c46eb027af6354 100644
--- a/emper/Semaphore.hpp
+++ b/emper/Semaphore.hpp
@@ -17,6 +17,9 @@ class Semaphore {
 	std::mutex mutex;
 
  public:
+	Semaphore() = default;
+	Semaphore(unsigned int count) : count(count){};
+
 	auto acquire() -> bool {
 		bool blocked;
 		mutex.lock();