mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-07 14:54:15 +00:00
Use finer grained locking in the BlockChain to make accessing the chain head responsive even whilst the chain is downloading and the object is under heavy load. This takes getChainHead() out of the profiles for ToyWallet and allows for removing the code added there to work around this issue.
This commit is contained in:
parent
7369c398ff
commit
b43b686264
@ -52,7 +52,7 @@ public class BlockChain {
|
||||
private static final Logger log = LoggerFactory.getLogger(BlockChain.class);
|
||||
|
||||
/** Keeps a map of block hashes to StoredBlocks. */
|
||||
protected BlockStore blockStore;
|
||||
protected final BlockStore blockStore;
|
||||
|
||||
/**
|
||||
* Tracks the top of the best known chain.<p>
|
||||
@ -63,6 +63,10 @@ public class BlockChain {
|
||||
* potentially invalidating transactions in our wallet.
|
||||
*/
|
||||
protected StoredBlock chainHead;
|
||||
// chainHead is accessed under this lock rather than the BlockChain lock. This is to try and keep accessors
|
||||
// responsive whilst the chain is downloading, without free-threading the entire chain add process (which could
|
||||
// get very confusing in the case of multiple blocks being added simultaneously).
|
||||
protected final Object chainHeadLock = new Object();
|
||||
|
||||
protected final NetworkParameters params;
|
||||
protected final List<Wallet> wallets;
|
||||
@ -134,9 +138,15 @@ public class BlockChain {
|
||||
|
||||
private synchronized boolean add(Block block, boolean tryConnecting)
|
||||
throws BlockStoreException, VerificationException, ScriptException {
|
||||
// Note on locking: this method runs with the block chain locked. All mutations to the chain are serialized.
|
||||
// This has the undesirable consequence that during block chain download, it's slow to read the current chain
|
||||
// head and other chain info because the accessors are constantly waiting for the chain to become free. To
|
||||
// solve this things viewable via accessors must use fine-grained locking as well as being mutated under the
|
||||
// chain lock.
|
||||
if (System.currentTimeMillis() - statsLastTime > 1000) {
|
||||
// More than a second passed since last stats logging.
|
||||
log.info("{} blocks per second", statsBlocksAdded);
|
||||
if (statsBlocksAdded > 1)
|
||||
log.info("{} blocks per second", statsBlocksAdded);
|
||||
statsLastTime = System.currentTimeMillis();
|
||||
statsBlocksAdded = 0;
|
||||
}
|
||||
@ -331,7 +341,9 @@ public class BlockChain {
|
||||
|
||||
private void setChainHead(StoredBlock chainHead) throws BlockStoreException {
|
||||
blockStore.setChainHead(chainHead);
|
||||
this.chainHead = chainHead;
|
||||
synchronized (chainHeadLock) {
|
||||
this.chainHead = chainHead;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -457,14 +469,17 @@ public class BlockChain {
|
||||
* Returns the block at the head of the current best chain. This is the block which represents the greatest
|
||||
* amount of cumulative work done.
|
||||
*/
|
||||
public synchronized StoredBlock getChainHead() {
|
||||
return chainHead;
|
||||
public StoredBlock getChainHead() {
|
||||
synchronized (chainHeadLock) {
|
||||
return chainHead;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most recent unconnected block or null if there are none. This will all have to change.
|
||||
* Returns the most recent unconnected block or null if there are none. This will all have to change. It's used
|
||||
* only in processing of inv messages.
|
||||
*/
|
||||
public synchronized Block getUnconnectedBlock() {
|
||||
synchronized Block getUnconnectedBlock() {
|
||||
if (unconnectedBlocks.size() == 0)
|
||||
return null;
|
||||
return unconnectedBlocks.get(unconnectedBlocks.size() - 1);
|
||||
|
@ -296,7 +296,7 @@ public class Peer {
|
||||
}
|
||||
|
||||
private void processBlock(Block m) throws IOException {
|
||||
log.info("Received broadcast block {}", m.getHashAsString());
|
||||
log.trace("Received broadcast block {}", m.getHashAsString());
|
||||
try {
|
||||
// Was this block requested by getBlock()?
|
||||
synchronized (pendingGetBlockFutures) {
|
||||
@ -352,7 +352,7 @@ public class Peer {
|
||||
if (!downloadData)
|
||||
return;
|
||||
|
||||
// The peer told us about some blocks or transactions they have. For now we only care about blocks.
|
||||
// The peer told us about some blocks or transactions they have.
|
||||
Block topBlock = blockChain.getUnconnectedBlock();
|
||||
Sha256Hash topHash = (topBlock != null ? topBlock.getHash() : null);
|
||||
if (isNewBlockTickle(topHash, items)) {
|
||||
|
@ -120,22 +120,19 @@ public class ToyWallet {
|
||||
@Override
|
||||
public void onPeerConnected(Peer peer, int peerCount) {
|
||||
super.onPeerConnected(peer, peerCount);
|
||||
triggerNetworkStatsUpdate(null, -1);
|
||||
triggerNetworkStatsUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPeerDisconnected(Peer peer, int peerCount) {
|
||||
super.onPeerDisconnected(peer, peerCount);
|
||||
triggerNetworkStatsUpdate(null, -1);
|
||||
triggerNetworkStatsUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlocksDownloaded(Peer peer, Block block, int blocksLeft) {
|
||||
super.onBlocksDownloaded(peer, block, blocksLeft);
|
||||
// Calculate chain height on this thread to avoid the UI and peer threads contending on the chain.
|
||||
int chainHeight = chain.getBestChainHeight();
|
||||
String blockDate = block.getTime().toString();
|
||||
triggerNetworkStatsUpdate(blockDate, chainHeight);
|
||||
triggerNetworkStatsUpdate();
|
||||
handleNewBlock();
|
||||
}
|
||||
});
|
||||
@ -205,19 +202,15 @@ public class ToyWallet {
|
||||
});
|
||||
}
|
||||
|
||||
private String blockDate;
|
||||
private int chainHeight;
|
||||
private void triggerNetworkStatsUpdate(String blockDate, int chainHeight) {
|
||||
// Running on a peer thread, switch to Swing thread before updating the peer count label.
|
||||
if (blockDate != null) this.blockDate = blockDate;
|
||||
if (chainHeight != -1) this.chainHeight = chainHeight;
|
||||
|
||||
private void triggerNetworkStatsUpdate() {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
int numPeers = peerGroup.numConnectedPeers();
|
||||
StoredBlock chainHead = chain.getChainHead();
|
||||
String date = chainHead.getHeader().getTime().toString();
|
||||
String plural = numPeers > 1 ? "peers" : "peer";
|
||||
String status = String.format("%d %s connected. %d blocks: %s",
|
||||
numPeers, plural, ToyWallet.this.chainHeight, ToyWallet.this.blockDate);
|
||||
numPeers, plural, chainHead.getHeight(), date);
|
||||
networkStats.setText(status);
|
||||
}
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user