More reliable start-up by removing some race conditions in Controller and Network

This commit is contained in:
catbref 2019-11-28 15:28:32 +00:00
parent e9c94eb83b
commit d01504a541
2 changed files with 60 additions and 58 deletions

View File

@ -16,7 +16,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Random; import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -116,14 +115,14 @@ public class Controller extends Thread {
private static volatile boolean isStopping = false; private static volatile boolean isStopping = false;
private static BlockMinter blockMinter = null; private static BlockMinter blockMinter = null;
private static volatile boolean requestSync = false; private static volatile boolean requestSync = false;
private static volatile boolean requestSysTrayUpdate = false; private static volatile boolean requestSysTrayUpdate = true;
private static Controller instance; private static Controller instance;
private final String buildVersion; private final String buildVersion;
private final long buildTimestamp; // seconds private final long buildTimestamp; // seconds
private final String[] savedArgs; private final String[] savedArgs;
private AtomicReference<BlockData> chainTip = new AtomicReference<>(); private volatile BlockData chainTip = null;
private long repositoryBackupTimestamp = startTime + REPOSITORY_BACKUP_PERIOD; // ms private long repositoryBackupTimestamp = startTime + REPOSITORY_BACKUP_PERIOD; // ms
private long ntpCheckTimestamp = startTime; // ms private long ntpCheckTimestamp = startTime; // ms
@ -188,7 +187,7 @@ public class Controller extends Thread {
this.savedArgs = args; this.savedArgs = args;
} }
private static Controller newInstance(String[] args) { private static synchronized Controller newInstance(String[] args) {
instance = new Controller(args); instance = new Controller(args);
return instance; return instance;
} }
@ -216,7 +215,7 @@ public class Controller extends Thread {
/** Returns current blockchain height, or 0 if it's not available. */ /** Returns current blockchain height, or 0 if it's not available. */
public int getChainHeight() { public int getChainHeight() {
BlockData blockData = this.chainTip.get(); BlockData blockData = this.chainTip;
if (blockData == null) if (blockData == null)
return 0; return 0;
@ -225,12 +224,12 @@ public class Controller extends Thread {
/** Returns highest block, or null if it's not available. */ /** Returns highest block, or null if it's not available. */
public BlockData getChainTip() { public BlockData getChainTip() {
return this.chainTip.get(); return this.chainTip;
} }
/** Cache new blockchain tip, and also wipe cache of online accounts. */ /** Cache new blockchain tip, and also wipe cache of online accounts. */
public void setChainTip(BlockData blockData) { public void setChainTip(BlockData blockData) {
this.chainTip.set(blockData); this.chainTip = blockData;
} }
public ReentrantLock getBlockchainLock() { public ReentrantLock getBlockchainLock() {
@ -263,6 +262,8 @@ public class Controller extends Thread {
return; // Not System.exit() so that GUI can display error return; // Not System.exit() so that GUI can display error
} }
Controller.newInstance(args);
LOGGER.info("Starting NTP"); LOGGER.info("Starting NTP");
NTP.start(); NTP.start();
@ -301,13 +302,13 @@ public class Controller extends Thread {
} }
LOGGER.info("Starting controller"); LOGGER.info("Starting controller");
Controller.newInstance(args).start(); Controller.getInstance().start();
LOGGER.info(String.format("Starting networking on port %d", Settings.getInstance().getListenPort())); LOGGER.info(String.format("Starting networking on port %d", Settings.getInstance().getListenPort()));
try { try {
Network network = Network.getInstance(); Network network = Network.getInstance();
network.start(); network.start();
} catch (Exception e) { } catch (IOException e) {
LOGGER.error("Unable to start networking", e); LOGGER.error("Unable to start networking", e);
Gui.getInstance().fatalError("Networking failure", e); Gui.getInstance().fatalError("Networking failure", e);
return; // Not System.exit() so that GUI can display error return; // Not System.exit() so that GUI can display error
@ -372,15 +373,37 @@ public class Controller extends Thread {
try { try {
while (!isStopping) { while (!isStopping) {
// Maybe update SysTray
if (requestSysTrayUpdate) {
requestSysTrayUpdate = false;
updateSysTray();
}
Thread.sleep(1000); Thread.sleep(1000);
final long now = System.currentTimeMillis();
// Check NTP status
if (now >= ntpCheckTimestamp) {
Long ntpTime = NTP.getTime();
if (ntpTime != null) {
LOGGER.info(String.format("Adjusting system time by NTP offset: %dms", ntpTime - now));
ntpCheckTimestamp = now + NTP_POST_SYNC_CHECK_PERIOD;
requestSysTrayUpdate = true;
} else {
LOGGER.info(String.format("No NTP offset yet"));
ntpCheckTimestamp = now + NTP_PRE_SYNC_CHECK_PERIOD;
// We can't do much without a valid NTP time
continue;
}
}
if (requestSync) { if (requestSync) {
requestSync = false; requestSync = false;
potentiallySynchronize(); potentiallySynchronize();
} }
final long now = System.currentTimeMillis();
// Clean up arbitrary data request cache // Clean up arbitrary data request cache
final long requestMinimumTimestamp = now - ARBITRARY_REQUEST_TIMEOUT; final long requestMinimumTimestamp = now - ARBITRARY_REQUEST_TIMEOUT;
arbitraryDataRequests.entrySet().removeIf(entry -> entry.getValue().getC() < requestMinimumTimestamp); arbitraryDataRequests.entrySet().removeIf(entry -> entry.getValue().getC() < requestMinimumTimestamp);
@ -391,21 +414,6 @@ public class Controller extends Thread {
RepositoryManager.backup(true); RepositoryManager.backup(true);
} }
// Check NTP status
if (now >= ntpCheckTimestamp) {
Long ntpTime = NTP.getTime();
if (ntpTime != null) {
LOGGER.info(String.format("Adjusting system time by NTP offset: %dms", ntpTime - now));
ntpCheckTimestamp = now + NTP_POST_SYNC_CHECK_PERIOD;
} else {
LOGGER.info(String.format("No NTP offset yet"));
ntpCheckTimestamp = now + NTP_PRE_SYNC_CHECK_PERIOD;
}
requestSysTrayUpdate = true;
}
// Prune stuck/slow/old peers // Prune stuck/slow/old peers
try { try {
Network.getInstance().prunePeers(); Network.getInstance().prunePeers();
@ -419,12 +427,6 @@ public class Controller extends Thread {
deleteExpiredTransactions(); deleteExpiredTransactions();
} }
// Maybe update SysTray
if (requestSysTrayUpdate) {
requestSysTrayUpdate = false;
updateSysTray();
}
// Perform tasks to do with managing online accounts list // Perform tasks to do with managing online accounts list
if (now >= onlineAccountsTasksTimestamp) { if (now >= onlineAccountsTasksTimestamp) {
onlineAccountsTasksTimestamp = now + ONLINE_ACCOUNTS_TASKS_INTERVAL; onlineAccountsTasksTimestamp = now + ONLINE_ACCOUNTS_TASKS_INTERVAL;

View File

@ -118,30 +118,6 @@ public class Network {
// Constructors // Constructors
private Network() { private Network() {
// Grab P2P port from settings
int listenPort = Settings.getInstance().getListenPort();
// Grab P2P bind address from settings
try {
InetAddress bindAddr = InetAddress.getByName(Settings.getInstance().getBindAddress());
InetSocketAddress endpoint = new InetSocketAddress(bindAddr, listenPort);
channelSelector = Selector.open();
// Set up listen socket
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
serverChannel.bind(endpoint, LISTEN_BACKLOG);
serverChannel.register(channelSelector, SelectionKey.OP_ACCEPT);
} catch (UnknownHostException e) {
LOGGER.error(String.format("Can't bind listen socket to address %s", Settings.getInstance().getBindAddress()));
throw new RuntimeException("Can't bind listen socket to address", e);
} catch (IOException e) {
LOGGER.error(String.format("Can't create listen socket: %s", e.getMessage()));
throw new RuntimeException("Can't create listen socket", e);
}
connectedPeers = new ArrayList<>(); connectedPeers = new ArrayList<>();
selfPeers = new ArrayList<>(); selfPeers = new ArrayList<>();
@ -169,14 +145,38 @@ public class Network {
networkEPC = new NetworkProcessor(networkExecutor); networkEPC = new NetworkProcessor(networkExecutor);
} }
public void start() { public void start() throws IOException {
// Grab P2P port from settings
int listenPort = Settings.getInstance().getListenPort();
// Grab P2P bind address from settings
try {
InetAddress bindAddr = InetAddress.getByName(Settings.getInstance().getBindAddress());
InetSocketAddress endpoint = new InetSocketAddress(bindAddr, listenPort);
channelSelector = Selector.open();
// Set up listen socket
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
serverChannel.bind(endpoint, LISTEN_BACKLOG);
serverChannel.register(channelSelector, SelectionKey.OP_ACCEPT);
} catch (UnknownHostException e) {
LOGGER.error(String.format("Can't bind listen socket to address %s", Settings.getInstance().getBindAddress()));
throw new IOException("Can't bind listen socket to address", e);
} catch (IOException e) {
LOGGER.error(String.format("Can't create listen socket: %s", e.getMessage()));
throw new IOException("Can't create listen socket", e);
}
// Start up first networking thread // Start up first networking thread
networkEPC.start(); networkEPC.start();
} }
// Getters / setters // Getters / setters
public static Network getInstance() { public static synchronized Network getInstance() {
if (instance == null) if (instance == null)
instance = new Network(); instance = new Network();