forked from Qortal/qortal
More reliable start-up by removing some race conditions in Controller and Network
This commit is contained in:
parent
e9c94eb83b
commit
d01504a541
@ -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;
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user