Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wallets: Fix bitcoind deadlock #2742

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@

import bisq.common.file.FileUtils;
import bisq.common.file.LogScanner;
import bisq.wallets.core.exceptions.WalletShutdownFailedException;
import bisq.wallets.core.exceptions.WalletStartupFailedException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -58,16 +58,10 @@ public void start() {

@Override
public void shutdown() {
try {
invokeStopRpcCall();
process.waitFor(2, TimeUnit.MINUTES);
} catch (InterruptedException e) {
String processName = process.info().command().orElse("<unknown process>");
throw new WalletShutdownFailedException("Cannot shutdown " + processName + ".", e);
}
invokeStopRpcCall();
}

private void waitUntilReady() {
protected void waitUntilReady() {
FutureTask<Boolean> waitingFuture = new FutureTask<>(this::waitUntilLogContainsLines);
Thread waitingThread = new Thread(waitingFuture);
waitingThread.start();
Expand All @@ -86,7 +80,9 @@ private void waitUntilReady() {
}
}

protected abstract Set<String> getIsSuccessfulStartUpLogLines();
protected Set<String> getIsSuccessfulStartUpLogLines() {
return Collections.emptySet();
}

public abstract void invokeStopRpcCall();

Expand All @@ -96,6 +92,7 @@ private Process createAndStartProcess() throws IOException {

var processBuilder = new ProcessBuilder(args);
processBuilder.redirectErrorStream(true);
processBuilder.redirectOutput(ProcessBuilder.Redirect.DISCARD);

Map<String, String> environment = processBuilder.environment();
environment.putAll(processConfig.getEnvironmentVars());
Expand All @@ -106,7 +103,9 @@ private Process createAndStartProcess() throws IOException {

public abstract ProcessConfig createProcessConfig();

protected abstract LogScanner getLogScanner();
protected LogScanner getLogScanner() {
return null;
}

protected boolean waitUntilLogContainsLines() {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

package bisq.wallets.regtest.bitcoind;

import bisq.common.file.InputStreamScanner;
import bisq.common.file.LogScanner;
import bisq.common.util.NetworkUtils;
import bisq.wallets.bitcoind.rpc.BitcoindDaemon;
import bisq.wallets.json_rpc.JsonRpcClient;
Expand All @@ -30,20 +28,25 @@
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.net.ConnectException;
import java.nio.file.Path;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.List;
import java.util.Set;

@Slf4j
public class BitcoindRegtestProcess extends DaemonProcess {

@Getter
protected final RpcConfig rpcConfig;
private final BitcoindDaemon bitcoindDaemon;

public BitcoindRegtestProcess(RpcConfig rpcConfig, Path dataDir) {
super(dataDir);
this.rpcConfig = rpcConfig;
JsonRpcClient rpcClient = RpcClientFactory.createDaemonRpcClient(rpcConfig);
bitcoindDaemon = new BitcoindDaemon(rpcClient);
}

@Override
Expand Down Expand Up @@ -74,16 +77,39 @@ public ProcessConfig createProcessConfig() {
}

@Override
protected LogScanner getLogScanner() {
return new InputStreamScanner(
getIsSuccessfulStartUpLogLines(),
process.getInputStream()
);
protected void waitUntilReady() {
Instant timeoutInstant = Instant.now().plus(2, ChronoUnit.MINUTES);
int failedAttempts = 0;
while (true) {
try {
bitcoindDaemon.listWallets();
log.info("Connected to Bitcoin Core.");
break;
} catch (RpcCallFailureException e) {
if (e.getCause() instanceof ConnectException) {
if (isAfterTimeout(timeoutInstant)) {
throw new IllegalStateException("Bitcoin Core isn't ready after 2 minutes. Giving up.");
}

failedAttempts++;
double msToWait = Math.min(300 * failedAttempts, 10_000);
log.info("Bitcoind RPC isn't ready yet. Trying again in {}ms.", msToWait);
sleepForMs(msToWait);
}
}
}
}

@Override
protected Set<String> getIsSuccessfulStartUpLogLines() {
return Set.of("init message: Done loading");
private boolean isAfterTimeout(Instant timeoutInstant) {
return Instant.now().isAfter(timeoutInstant);
}

private void sleepForMs(double ms) {
try {
Thread.sleep((long) ms);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}

@Override
Expand Down
Loading