@ -17,6 +17,7 @@ import java.util.Random;
import java.util.concurrent.ExecutorService ;
import java.util.concurrent.Executors ;
import java.util.concurrent.RejectedExecutionException ;
import java.util.concurrent.TimeUnit ;
import java.util.concurrent.locks.Lock ;
import java.util.concurrent.locks.ReentrantLock ;
import java.util.function.Function ;
@ -84,6 +85,7 @@ public class Network extends Thread {
public static final int PEER_ID_LENGTH = 128 ;
private final byte [ ] ourPeerId ;
private volatile boolean isStopping = false ;
private List < Peer > connectedPeers ;
private List < PeerAddress > selfPeers ;
private ServerSocket listenSocket ;
@ -91,6 +93,7 @@ public class Network extends Thread {
private int maxPeers ;
private ExecutorService peerExecutor ;
private ExecutorService mergePeersExecutor ;
private ExecutorService broadcastExecutor ;
private long nextBroadcast ;
private Lock mergePeersLock ;
@ -128,6 +131,8 @@ public class Network extends Thread {
maxPeers = Settings . getInstance ( ) . getMaxPeers ( ) ;
peerExecutor = Executors . newCachedThreadPool ( ) ;
broadcastExecutor = Executors . newCachedThreadPool ( ) ;
nextBroadcast = System . currentTimeMillis ( ) ;
mergePeersLock = new ReentrantLock ( ) ;
@ -192,7 +197,7 @@ public class Network extends Thread {
// Maintain long-term connections to various peers' API applications
try {
while ( true ) {
while ( ! isStopping ) {
acceptConnections ( ) ;
pruneOldPeers ( ) ;
@ -215,14 +220,6 @@ public class Network extends Thread {
LOGGER . warn ( "Repository issue while running network" , e ) ;
// Fall-through to shutdown
}
// Shutdown
if ( ! this . listenSocket . isClosed ( ) )
try {
this . listenSocket . close ( ) ;
} catch ( IOException e ) {
// Not important
}
}
@SuppressWarnings ( "resource" )
@ -240,6 +237,7 @@ public class Network extends Thread {
return ;
}
Peer newPeer = null ;
synchronized ( this . connectedPeers ) {
if ( connectedPeers . size ( ) > = maxPeers ) {
// We have enough peers
@ -255,9 +253,14 @@ public class Network extends Thread {
}
LOGGER . debug ( String . format ( "Connection accepted from peer %s" , socket . getRemoteSocketAddress ( ) ) ) ;
Peer newPeer = new Peer ( socket ) ;
newPeer = new Peer ( socket ) ;
this . connectedPeers . add ( newPeer ) ;
}
try {
peerExecutor . execute ( newPeer ) ;
} catch ( RejectedExecutionException e ) {
// Can't execute - probably because we're shutting down, so ignore
}
} while ( true ) ;
}
@ -369,7 +372,11 @@ public class Network extends Thread {
this . connectedPeers . add ( newPeer ) ;
}
peerExecutor . execute ( newPeer ) ;
try {
peerExecutor . execute ( newPeer ) ;
} catch ( RejectedExecutionException e ) {
// Can't execute - probably because we're shutting down, so ignore
}
}
// Peer callbacks
@ -586,8 +593,23 @@ public class Network extends Thread {
List < PeerData > knownPeers = repository . getNetworkRepository ( ) . getAllPeers ( ) ;
// Filter out peers that we've not connected to ever or within X milliseconds
long connectionThreshold = NTP . getTime ( ) - RECENT_CONNECTION_THRESHOLD ;
knownPeers . removeIf ( peerData - > peerData . getLastConnected ( ) = = null | | peerData . getLastConnected ( ) < connectionThreshold ) ;
final long connectionThreshold = NTP . getTime ( ) - RECENT_CONNECTION_THRESHOLD ;
Predicate < PeerData > notRecentlyConnected = peerData - > {
final Long lastAttempted = peerData . getLastAttempted ( ) ;
final Long lastConnected = peerData . getLastConnected ( ) ;
if ( lastAttempted = = null | | lastConnected = = null )
return true ;
if ( lastConnected < lastAttempted )
return true ;
if ( lastConnected < connectionThreshold )
return true ;
return false ;
} ;
knownPeers . removeIf ( notRecentlyConnected ) ;
if ( peer . getVersion ( ) > = 2 ) {
List < PeerAddress > peerAddresses = new ArrayList < > ( ) ;
@ -778,7 +800,11 @@ public class Network extends Thread {
}
}
mergePeersExecutor . execute ( new PeersMerger ( peerAddresses ) ) ;
try {
mergePeersExecutor . execute ( new PeersMerger ( peerAddresses ) ) ;
} catch ( RejectedExecutionException e ) {
// Can't execute - probably because we're shutting down, so ignore
}
}
public void broadcast ( Function < Peer , Message > peerMessageBuilder ) {
@ -817,21 +843,54 @@ public class Network extends Thread {
}
try {
peer Executor. execute ( new Broadcaster ( this . getUniqueHandshakedPeers ( ) , peerMessageBuilder ) ) ;
broadcast Executor. execute ( new Broadcaster ( this . getUniqueHandshakedPeers ( ) , peerMessageBuilder ) ) ;
} catch ( RejectedExecutionException e ) {
// Can't execute - probably because we're shutting down, so ignore
}
}
public void shutdown ( ) {
peerExecutor . shutdownNow ( ) ;
this . isStopping = true ;
// Close listen socket to prevent more incoming connections
if ( ! this . listenSocket . isClosed ( ) )
try {
this . listenSocket . close ( ) ;
} catch ( IOException e ) {
// Not important
}
// Stop our run() thread
this . interrupt ( ) ;
try {
this . join ( ) ;
} catch ( InterruptedException e ) {
// We were interrupted while waiting for thread to join
LOGGER . debug ( "Interrupted while waiting for networking thread to terminate" ) ;
}
}
// Give up merging peer lists
this . mergePeersExecutor . shutdownNow ( ) ;
try {
if ( ! this . mergePeersExecutor . awaitTermination ( 1000 , TimeUnit . MILLISECONDS ) )
LOGGER . debug ( "Peer-list merging threads failed to terminate" ) ;
} catch ( InterruptedException e ) {
LOGGER . debug ( "Interrupted while waiting for peer-list merging threads failed to terminate" ) ;
}
// Stop broadcasts
this . broadcastExecutor . shutdownNow ( ) ;
try {
if ( ! this . broadcastExecutor . awaitTermination ( 1000 , TimeUnit . MILLISECONDS ) )
LOGGER . debug ( "Broadcast threads failed to terminate" ) ;
} catch ( InterruptedException e ) {
LOGGER . debug ( "Interrupted while waiting for broadcast threads failed to terminate" ) ;
}
// Close all peer connections
synchronized ( this . connectedPeers ) {
for ( Peer peer : this . connectedPeers )
peer . shutdown ( ) ;
}
}
}