private void finishDisconnect(final Message m, final PeerNode source) { source.disconnected(true, true); // If true, remove from active routing table, likely to be down for a while. // Otherwise just dump all current connection state and keep trying to connect. boolean remove = m.getBoolean(DMT.REMOVE); if (remove) node.peers.disconnect(source, false, false, false); // If true, purge all references to this node. Otherwise, we can keep the node // around in secondary tables etc in order to more easily reconnect later. // (Mostly used on opennet) boolean purge = m.getBoolean(DMT.PURGE); if (purge) { OpennetManager om = node.getOpennet(); if (om != null) om.purgeOldOpennetPeer(source); } // Process parting message int type = m.getInt(DMT.NODE_TO_NODE_MESSAGE_TYPE); ShortBuffer messageData = (ShortBuffer) m.getObject(DMT.NODE_TO_NODE_MESSAGE_DATA); if (messageData.getLength() == 0) return; node.receivedNodeToNodeMessage(source, type, messageData, true); }
void realRun() { ChosenBlock req = null; // The last time at which we sent a request or decided not to long cycleTime = System.currentTimeMillis(); while (true) { // Allow 5 minutes before we start killing requests due to not connecting. OpennetManager om; if (core.node.peers.countConnectedPeers() < 3 && (om = core.node.getOpennet()) != null && System.currentTimeMillis() - om.getCreationTime() < 5 * 60 * 1000) { try { synchronized (this) { wait(1000); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } continue; } if (req == null) { req = sched.grabRequest(); } if (req != null) { if (logMINOR) Logger.minor(this, "Running " + req + " priority " + req.getPriority()); if (!req.localRequestOnly) { // Wait long delay; delay = throttle.getDelay(); if (logMINOR) Logger.minor(this, "Delay=" + delay + " from " + throttle); long sleepUntil = cycleTime + delay; if (!LOCAL_REQUESTS_COMPETE_FAIRLY) { inputBucket.blockingGrab( (int) (Math.max(0, averageInputBytesPerRequest.currentValue()))); outputBucket.blockingGrab( (int) (Math.max(0, averageOutputBytesPerRequest.currentValue()))); } long now; do { now = System.currentTimeMillis(); if (now < sleepUntil) try { Thread.sleep(sleepUntil - now); if (logMINOR) Logger.minor(this, "Slept: " + (sleepUntil - now) + "ms"); } catch (InterruptedException e) { // Ignore } } while (now < sleepUntil); } // if(!doAIMD) { // // Arbitrary limit on number of local requests waiting for slots. // // Firstly, they use threads. This could be a serious problem for faster nodes. // // Secondly, it may help to prevent wider problems: // // If all queues are full, the network will die. // int[] waiting = core.node.countRequestsWaitingForSlots(); // int localRequestsWaitingForSlots = waiting[0]; // int maxWaitingForSlots = MAX_WAITING_FOR_SLOTS; // // FIXME calibrate this by the number of local timeouts. // // FIXME consider an AIMD, or some similar mechanism. // // Local timeout-waiting-for-slots is largely dependant on // // the number of requests running, due to strict round-robin, // // so we can probably do something even simpler than an AIMD. // // For now we'll just have a fixed number. // // This should partially address the problem. // // Note that while waitFor() is blocking, we need such a limit anyway. // if(localRequestsWaitingForSlots > maxWaitingForSlots) continue; // } RejectReason reason; assert (req.realTimeFlag == realTime); if (LOCAL_REQUESTS_COMPETE_FAIRLY && !req.localRequestOnly) { if ((reason = stats.shouldRejectRequest( true, isInsert, isSSK, true, false, null, false, isInsert && Node.PREFER_INSERT_DEFAULT, req.realTimeFlag, null)) != null) { if (logMINOR) Logger.minor(this, "Not sending local request: " + reason); // Wait one throttle-delay before trying again cycleTime = System.currentTimeMillis(); continue; // Let local requests compete with all the others } } else { stats.waitUntilNotOverloaded(isInsert); } } else { if (logMINOR) Logger.minor(this, "Waiting..."); // Always take the lock on RequestStarter first. AFAICS we don't synchronize on // RequestStarter anywhere else. // Nested locks here prevent extra latency when there is a race, and therefore allow us to // sleep indefinitely synchronized (this) { req = sched.grabRequest(); if (req == null) { try { wait( 1 * 1000); // this can happen when most but not all stuff is already running but // there is still stuff to fetch, so don't wait *too* long. // FIXME increase when we can be *sure* there is nothing left in the queue (especially // for transient requests). } catch (InterruptedException e) { // Ignore } } } } if (req == null) continue; if (!startRequest(req, logMINOR)) { // Don't log if it's a cancelled transient request. if (!((!req.isPersistent()) && req.isCancelled())) Logger.normal(this, "No requests to start on " + req); } if (!req.localRequestOnly) cycleTime = System.currentTimeMillis(); req = null; } }
/** * Send loop. Strategy: - Each peer can tell us when its data needs to be sent by. This is usually * 100ms after it is posted. It could vary by message type. Acknowledgements also become valid * 100ms after being queued. - If any peer's data is overdue, send the data from the most overdue * peer. - If there are peers with more than a packet's worth of data queued, send the data from * the peer with the oldest data. - If there are peers with overdue ack's, send to the peer whose * acks are oldest. * * <p>It does not attempt to ensure fairness, it attempts to minimise latency. Fairness is best * dealt with at a higher level e.g. requests, although some transfers are not part of requests, * e.g. bulk f2f transfers, so we may need to reconsider this eventually... */ private void realRun() { long now = System.currentTimeMillis(); PeerManager pm; PeerNode[] nodes; pm = node.peers; nodes = pm.myPeers(); long nextActionTime = Long.MAX_VALUE; long oldTempNow = now; boolean canSendThrottled = false; int MAX_PACKET_SIZE = node.darknetCrypto.socket.getMaxPacketSize(); long count = node.outputThrottle.getCount(); if (count > MAX_PACKET_SIZE) canSendThrottled = true; else { long canSendAt = node.outputThrottle.getNanosPerTick() * (MAX_PACKET_SIZE - count); canSendAt = MILLISECONDS.convert(canSendAt + MILLISECONDS.toNanos(1) - 1, NANOSECONDS); if (logMINOR) Logger.minor(this, "Can send throttled packets in " + canSendAt + "ms"); nextActionTime = Math.min(nextActionTime, now + canSendAt); } /** * The earliest time at which a peer needs to send a packet, which is before now. Throttled if * canSendThrottled, otherwise not throttled. Note: we only use it to sort the full-packed peers * by priority, don't rely on it when setting nextActionTime! */ long lowestUrgentSendTime = Long.MAX_VALUE; /** The peer(s) which lowestUrgentSendTime is referring to */ ArrayList<PeerNode> urgentSendPeers = null; /** * The earliest time at which a peer needs to send a packet, which is after now, where there is * a full packet's worth of data to send. Throttled if canSendThrottled, otherwise not * throttled. */ long lowestFullPacketSendTime = Long.MAX_VALUE; /** The peer(s) which lowestFullPacketSendTime is referring to */ ArrayList<PeerNode> urgentFullPacketPeers = null; /** The earliest time at which a peer needs to send an ack, before now. */ long lowestAckTime = Long.MAX_VALUE; /** The peer(s) which lowestAckTime is referring to */ ArrayList<PeerNode> ackPeers = null; /** The earliest time at which a peer needs to handshake. */ long lowestHandshakeTime = Long.MAX_VALUE; /** The peer(s) which lowestHandshakeTime is referring to */ ArrayList<PeerNode> handshakePeers = null; for (PeerNode pn : nodes) { now = System.currentTimeMillis(); // Basic peer maintenance. // For purposes of detecting not having received anything, which indicates a // serious connectivity problem, we want to look for *any* packets received, // including auth packets. lastReceivedPacketFromAnyNode = Math.max(pn.lastReceivedPacketTime(), lastReceivedPacketFromAnyNode); pn.maybeOnConnect(); if (pn.shouldDisconnectAndRemoveNow() && !pn.isDisconnecting()) { // Might as well do it properly. node.peers.disconnectAndRemove(pn, true, true, false); } if (pn.isConnected()) { boolean shouldThrottle = pn.shouldThrottle(); pn.checkForLostPackets(); // Is the node dead? // It might be disconnected in terms of FNP but trying to reconnect via JFK's, so we need to // use the time when we last got a *data* packet. if (now - pn.lastReceivedDataPacketTime() > pn.maxTimeBetweenReceivedPackets()) { Logger.normal(this, "Disconnecting from " + pn + " - haven't received packets recently"); // Hopefully this is a transient network glitch, but stuff will have already started to // timeout, so lets dump the pending messages. pn.disconnected(true, false); continue; } else if (now - pn.lastReceivedAckTime() > pn.maxTimeBetweenReceivedAcks() && !pn.isDisconnecting()) { // FIXME better to disconnect immediately??? Or check canSend()??? Logger.normal(this, "Disconnecting from " + pn + " - haven't received acks recently"); // Do it properly. // There appears to be connectivity from them to us but not from us to them. // So it is helpful for them to know that we are disconnecting. node.peers.disconnect(pn, true, true, false, true, false, SECONDS.toMillis(5)); continue; } else if (pn.isRoutable() && pn.noLongerRoutable()) { /* NOTE: Whereas isRoutable() && noLongerRoutable() are generally mutually exclusive, this code will only execute because of the scheduled-runnable in start() which executes updateVersionRoutablity() on all our peers. We don't disconnect the peer, but mark it as being incompatible. */ pn.invalidate(now); Logger.normal( this, "shouldDisconnectNow has returned true : marking the peer as incompatible: " + pn); continue; } // The peer is connected. if (canSendThrottled || !shouldThrottle) { // We can send to this peer. long sendTime = pn.getNextUrgentTime(now); if (sendTime != Long.MAX_VALUE) { if (sendTime <= now) { // Message is urgent. if (sendTime < lowestUrgentSendTime) { lowestUrgentSendTime = sendTime; if (urgentSendPeers != null) urgentSendPeers.clear(); else urgentSendPeers = new ArrayList<PeerNode>(); } if (sendTime <= lowestUrgentSendTime) urgentSendPeers.add(pn); } else if (pn.fullPacketQueued()) { if (sendTime < lowestFullPacketSendTime) { lowestFullPacketSendTime = sendTime; if (urgentFullPacketPeers != null) urgentFullPacketPeers.clear(); else urgentFullPacketPeers = new ArrayList<PeerNode>(); } if (sendTime <= lowestFullPacketSendTime) urgentFullPacketPeers.add(pn); } } } else if (shouldThrottle && !canSendThrottled) { long ackTime = pn.timeSendAcks(); if (ackTime != Long.MAX_VALUE) { if (ackTime <= now) { if (ackTime < lowestAckTime) { lowestAckTime = ackTime; if (ackPeers != null) ackPeers.clear(); else ackPeers = new ArrayList<PeerNode>(); } if (ackTime <= lowestAckTime) ackPeers.add(pn); } } } if (canSendThrottled || !shouldThrottle) { long urgentTime = pn.getNextUrgentTime(now); // Should spam the logs, unless there is a deadlock if (urgentTime < Long.MAX_VALUE && logMINOR) Logger.minor( this, "Next urgent time: " + urgentTime + "(in " + (urgentTime - now) + ") for " + pn); nextActionTime = Math.min(nextActionTime, urgentTime); } else { nextActionTime = Math.min(nextActionTime, pn.timeCheckForLostPackets()); } } else // Not connected if (pn.noContactDetails()) pn.startARKFetcher(); long handshakeTime = pn.timeSendHandshake(now); if (handshakeTime != Long.MAX_VALUE) { if (handshakeTime < lowestHandshakeTime) { lowestHandshakeTime = handshakeTime; if (handshakePeers != null) handshakePeers.clear(); else handshakePeers = new ArrayList<PeerNode>(); } if (handshakeTime <= lowestHandshakeTime) handshakePeers.add(pn); } long tempNow = System.currentTimeMillis(); if ((tempNow - oldTempNow) > SECONDS.toMillis(5)) Logger.error( this, "tempNow is more than 5 seconds past oldTempNow (" + (tempNow - oldTempNow) + ") in PacketSender working with " + pn.userToString()); oldTempNow = tempNow; } // We may send a packet, send an ack-only packet, or send a handshake. PeerNode toSendPacket = null; PeerNode toSendAckOnly = null; PeerNode toSendHandshake = null; long t = Long.MAX_VALUE; if (lowestUrgentSendTime <= now) { // We need to send a full packet. toSendPacket = urgentSendPeers.get(localRandom.nextInt(urgentSendPeers.size())); t = lowestUrgentSendTime; } else if (lowestFullPacketSendTime < Long.MAX_VALUE) { toSendPacket = urgentFullPacketPeers.get(localRandom.nextInt(urgentFullPacketPeers.size())); t = lowestFullPacketSendTime; } else if (lowestAckTime <= now) { // We need to send an ack toSendAckOnly = ackPeers.get(localRandom.nextInt(ackPeers.size())); t = lowestAckTime; } if (lowestHandshakeTime <= now && t > lowestHandshakeTime) { toSendHandshake = handshakePeers.get(localRandom.nextInt(handshakePeers.size())); toSendPacket = null; toSendAckOnly = null; } if (toSendPacket != null) { try { if (toSendPacket.maybeSendPacket(now, false)) { // Round-robin over the loop to update nextActionTime appropriately nextActionTime = now; } } catch (BlockedTooLongException e) { Logger.error( this, "Waited too long: " + TimeUtil.formatTime(e.delta) + " to allocate a packet number to send to " + toSendPacket + " : " + ("(new packet format)") + " (version " + toSendPacket.getVersionNumber() + ") - DISCONNECTING!"); toSendPacket.forceDisconnect(); } if (canSendThrottled || !toSendPacket.shouldThrottle()) { long urgentTime = toSendPacket.getNextUrgentTime(now); // Should spam the logs, unless there is a deadlock if (urgentTime < Long.MAX_VALUE && logMINOR) Logger.minor( this, "Next urgent time: " + urgentTime + "(in " + (urgentTime - now) + ") for " + toSendPacket); nextActionTime = Math.min(nextActionTime, urgentTime); } else { nextActionTime = Math.min(nextActionTime, toSendPacket.timeCheckForLostPackets()); } } else if (toSendAckOnly != null) { try { if (toSendAckOnly.maybeSendPacket(now, true)) { count = node.outputThrottle.getCount(); if (count > MAX_PACKET_SIZE) canSendThrottled = true; else { canSendThrottled = false; long canSendAt = node.outputThrottle.getNanosPerTick() * (MAX_PACKET_SIZE - count); canSendAt = MILLISECONDS.convert(canSendAt + MILLISECONDS.toNanos(1) - 1, NANOSECONDS); if (logMINOR) Logger.minor(this, "Can send throttled packets in " + canSendAt + "ms"); nextActionTime = Math.min(nextActionTime, now + canSendAt); } } } catch (BlockedTooLongException e) { Logger.error( this, "Waited too long: " + TimeUtil.formatTime(e.delta) + " to allocate a packet number to send to " + toSendAckOnly + " : " + ("(new packet format)") + " (version " + toSendAckOnly.getVersionNumber() + ") - DISCONNECTING!"); toSendAckOnly.forceDisconnect(); } if (canSendThrottled || !toSendAckOnly.shouldThrottle()) { long urgentTime = toSendAckOnly.getNextUrgentTime(now); // Should spam the logs, unless there is a deadlock if (urgentTime < Long.MAX_VALUE && logMINOR) Logger.minor( this, "Next urgent time: " + urgentTime + "(in " + (urgentTime - now) + ") for " + toSendAckOnly); nextActionTime = Math.min(nextActionTime, urgentTime); } else { nextActionTime = Math.min(nextActionTime, toSendAckOnly.timeCheckForLostPackets()); } } /* Estimating of nextActionTime logic: * FullPackets: * - A full packet available, bandwidth available -->> now * - A full packet available for non-throttled peer -->> now * - A full packet available, no bandwidth -->> wait till bandwidth available * - No packet -->> don't care, will wake up anyway when one arrives, goto Nothing * UrgentMessages: Only applies when there's enough bandwidth to send a full packet, Includes any urgent acks * - There's an urgent message, deadline(urgentMessage) > now -->> deadline(urgentMessage) * - There's an urgent message, deadline(urgentMessage) <= now -->> now * - There's an urgent message, but there's not enough bandwidth for a full packet -->> wait till bandwidth available * - There's no urgent message -->> don't care, goto Nothing * Nothing: * -->> timeCheckForLostPackets */ if (toSendHandshake != null) { // Send handshake if necessary long beforeHandshakeTime = System.currentTimeMillis(); toSendHandshake.getOutgoingMangler().sendHandshake(toSendHandshake, false); long afterHandshakeTime = System.currentTimeMillis(); if ((afterHandshakeTime - beforeHandshakeTime) > SECONDS.toMillis(2)) Logger.error( this, "afterHandshakeTime is more than 2 seconds past beforeHandshakeTime (" + (afterHandshakeTime - beforeHandshakeTime) + ") in PacketSender working with " + toSendHandshake.userToString()); } // All of these take into account whether the data can be sent already. // So we can include them in nextActionTime. nextActionTime = Math.min(nextActionTime, lowestUrgentSendTime); nextActionTime = Math.min(nextActionTime, lowestFullPacketSendTime); nextActionTime = Math.min(nextActionTime, lowestAckTime); nextActionTime = Math.min(nextActionTime, lowestHandshakeTime); // FIXME: If we send something we will have to go around the loop again. // OPTIMISATION: We could track the second best, and check how many are in the array. /* Attempt to connect to old-opennet-peers. * Constantly send handshake packets, in order to get through a NAT. * Most JFK(1)'s are less than 300 bytes. 25*300/15 = avg 500B/sec bandwidth cost. * Well worth it to allow us to reconnect more quickly. */ OpennetManager om = node.getOpennet(); if (om != null && node.getUptime() > SECONDS.toMillis(30)) { PeerNode[] peers = om.getOldPeers(); for (PeerNode pn : peers) { long lastConnected = pn.timeLastConnected(now); if (lastConnected <= 0) Logger.error(this, "Last connected is zero or negative for old-opennet-peer " + pn); // Will be removed by next line. if (now - lastConnected > OpennetManager.MAX_TIME_ON_OLD_OPENNET_PEERS) { om.purgeOldOpennetPeer(pn); if (logMINOR) Logger.minor( this, "Removing old opennet peer (too old): " + pn + " age is " + TimeUtil.formatTime(now - lastConnected)); continue; } if (pn.isConnected()) continue; // Race condition?? if (pn.noContactDetails()) { pn.startARKFetcher(); continue; } if (pn.shouldSendHandshake()) { // Send handshake if necessary long beforeHandshakeTime = System.currentTimeMillis(); pn.getOutgoingMangler().sendHandshake(pn, true); long afterHandshakeTime = System.currentTimeMillis(); if ((afterHandshakeTime - beforeHandshakeTime) > SECONDS.toMillis(2)) Logger.error( this, "afterHandshakeTime is more than 2 seconds past beforeHandshakeTime (" + (afterHandshakeTime - beforeHandshakeTime) + ") in PacketSender working with " + pn.userToString()); } } } long oldNow = now; // Send may have taken some time now = System.currentTimeMillis(); if ((now - oldNow) > SECONDS.toMillis(10)) Logger.error( this, "now is more than 10 seconds past oldNow (" + (now - oldNow) + ") in PacketSender"); long sleepTime = nextActionTime - now; // MAX_COALESCING_DELAYms maximum sleep time - same as the maximum coalescing delay sleepTime = Math.min(sleepTime, MAX_COALESCING_DELAY); if (now - node.startupTime > MINUTES.toMillis(5)) if (now - lastReceivedPacketFromAnyNode > Node.ALARM_TIME) { Logger.error( this, "Have not received any packets from any node in last " + SECONDS.convert(Node.ALARM_TIME, MILLISECONDS) + " seconds"); lastReportedNoPackets = now; } if (sleepTime > 0) { // Update logging only when have time to do so try { if (logMINOR) Logger.minor(this, "Sleeping for " + sleepTime); synchronized (this) { wait(sleepTime); } } catch (InterruptedException e) { // Ignore, just wake up. Probably we got interrupt()ed // because a new packet came in. } } else { if (logDEBUG) Logger.debug(this, "Next urgent time is " + (now - nextActionTime) + "ms in the past"); } }