From 60a7412c2fc35771a803039223c20560161d7687 Mon Sep 17 00:00:00 2001 From: Alva Swanson Date: Mon, 15 Apr 2024 17:55:30 +0200 Subject: [PATCH] Implement ControlPortFilePoller --- .../tor/process/ControlPortFilePoller.java | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 network/tor/tor/src/main/java/bisq/tor/process/ControlPortFilePoller.java diff --git a/network/tor/tor/src/main/java/bisq/tor/process/ControlPortFilePoller.java b/network/tor/tor/src/main/java/bisq/tor/process/ControlPortFilePoller.java new file mode 100644 index 0000000000..eb975897dc --- /dev/null +++ b/network/tor/tor/src/main/java/bisq/tor/process/ControlPortFilePoller.java @@ -0,0 +1,76 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.tor.process; + +import java.nio.file.Path; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; + +public class ControlPortFilePoller { + private final AtomicBoolean isRunning = new AtomicBoolean(); + private final CompletableFuture portCompletableFuture = new CompletableFuture<>(); + private final Path controlPortFilePath; + + public ControlPortFilePoller(Path controlPortFilePath) { + this.controlPortFilePath = controlPortFilePath; + } + + public CompletableFuture parsePort() { + boolean isSuccess = isRunning.compareAndSet(false, true); + if (isSuccess) { + startPoller(); + } + return portCompletableFuture; + } + + private void startPoller() { + Thread thread = new Thread(() -> { + try { + while (true) { + Optional optionalPort = parsePortFromFile(); + + if (optionalPort.isPresent()) { + portCompletableFuture.complete(optionalPort.get()); + break; + } else { + // We can't use Java's WatcherService because it misses events between event processing. + // Tor writes the port to a swap file first, and renames it afterward. + // The WatcherService can miss the second operation, causing a deadlock. + //noinspection BusyWait + Thread.sleep(100); + } + } + + } catch (ControlPortFileParseFailureException | InterruptedException e) { + portCompletableFuture.completeExceptionally(e); + } + }); + + thread.start(); + } + + private Optional parsePortFromFile() { + if (!controlPortFilePath.toFile().exists()) { + return Optional.empty(); + } + + int controlPort = ControlPortFileParser.parse(controlPortFilePath); + return Optional.of(controlPort); + } +}