Author: toad
Date: 2008-11-14 15:37:44 +0000 (Fri, 14 Nov 2008)
New Revision: 23580

Added:
   trunk/freenet/src/freenet/node/BlockedTooLongException.java
Modified:
   trunk/freenet/src/freenet/l10n/freenet.l10n.en.properties
   trunk/freenet/src/freenet/node/FNPPacketMangler.java
   trunk/freenet/src/freenet/node/KeyTracker.java
   trunk/freenet/src/freenet/node/OutgoingPacketMangler.java
   trunk/freenet/src/freenet/node/PacketSender.java
   trunk/freenet/src/freenet/node/PeerNode.java
   trunk/freenet/src/freenet/support/LimitedRangeIntByteArrayMap.java
Log:
Don't try to send packets when we know it will block (throw 
WouldBlockException).
Don't busy loop waiting for that to clear either.
After we have been unable to allocate a packet number for 10 minutes, 
disconnect, log an error, and tell the user via a useralert.


Modified: trunk/freenet/src/freenet/l10n/freenet.l10n.en.properties
===================================================================
--- trunk/freenet/src/freenet/l10n/freenet.l10n.en.properties   2008-11-14 
15:12:16 UTC (rev 23579)
+++ trunk/freenet/src/freenet/l10n/freenet.l10n.en.properties   2008-11-14 
15:37:44 UTC (rev 23580)
@@ -474,6 +474,8 @@
 FileOffer.succeededReceiveShort=Successfully received ${filename} from ${node}.
 FNPPacketMangler.somePeersDisconnectedStillNotAcked=Probably a bug: please 
report: ${count} peers forcibly disconnected due to not acknowledging packets.
 FNPPacketMangler.somePeersDisconnectedStillNotAckedDetail=${count} of your 
peers are having severe problems (not acknowledging packets even after 10 
minutes). This is probably due to a bug in the code. Please report it to us at 
the bug tracker at ${link}https://bugs.freenetproject.org/${/link} or at [EMAIL 
PROTECTED] Please include this message and what version of the node you are 
running. The affected peers (you may not want to include this in your bug 
report if they are darknet peers) are:
+PacketSender.somePeersDisconnectedStillNotAcked=Probably a bug: please report: 
${count} peers forcibly disconnected due to blocking for a packet number for 
over 10 minutes.
+PacketSender.somePeersDisconnectedStillNotAckedDetail=${count} of your peers 
are having severe problems (failing to allocate a packet number after 10 
minutes). This is probably due to a bug in the code. Please report it to us at 
the bug tracker at ${link}https://bugs.freenetproject.org/${/link} or at [EMAIL 
PROTECTED] Please include this message and what version of the node you are 
running. The affected peers (you may not want to include this in your bug 
report if they are darknet peers) are:
 GIFFilter.invalidHeader=The file does not contain a valid GIF header.
 GIFFilter.invalidHeaderTitle=Invalid header
 GIFFilter.notGif=The file you tried to fetch is not a GIF. It might be some 
other file format, and your browser may do something dangerous with it, 
therefore we have blocked it.

Added: trunk/freenet/src/freenet/node/BlockedTooLongException.java
===================================================================
--- trunk/freenet/src/freenet/node/BlockedTooLongException.java                 
        (rev 0)
+++ trunk/freenet/src/freenet/node/BlockedTooLongException.java 2008-11-14 
15:37:44 UTC (rev 23580)
@@ -0,0 +1,13 @@
+package freenet.node;
+
+public class BlockedTooLongException extends Exception {
+
+       public final KeyTracker tracker;
+       public final long delta;
+       
+       public BlockedTooLongException(KeyTracker tracker, long delta) {
+               this.tracker = tracker;
+               this.delta = delta;
+       }
+
+}

Modified: trunk/freenet/src/freenet/node/FNPPacketMangler.java
===================================================================
--- trunk/freenet/src/freenet/node/FNPPacketMangler.java        2008-11-14 
15:12:16 UTC (rev 23579)
+++ trunk/freenet/src/freenet/node/FNPPacketMangler.java        2008-11-14 
15:37:44 UTC (rev 23580)
@@ -2014,7 +2014,7 @@
        /* (non-Javadoc)
         * @see 
freenet.node.OutgoingPacketMangler#processOutgoingOrRequeue(freenet.node.MessageItem[],
 freenet.node.PeerNode, boolean, boolean)
         */
-       public boolean processOutgoingOrRequeue(MessageItem[] messages, 
PeerNode pn, boolean neverWaitForPacketNumber, boolean dontRequeue, boolean 
onePacket) {
+       public boolean processOutgoingOrRequeue(MessageItem[] messages, 
PeerNode pn, boolean neverWaitForPacketNumber, boolean dontRequeue, boolean 
onePacket) throws BlockedTooLongException {
                String requeueLogString = "";
                if(!dontRequeue) {
                        requeueLogString = ", requeueing";
@@ -2027,6 +2027,10 @@
                        Logger.error(this, "Not connected while sending 
packets: "+pn);
                        return false;
                }
+               if(kt.wouldBlock(false)) {
+                       if(logMINOR) Logger.minor(this, "Would block: "+kt);
+                       return false;
+               }
                int length = 1;
                length += kt.countAcks() + kt.countAckRequests() + 
kt.countResendRequests();
                int callbacksCount = 0;

Modified: trunk/freenet/src/freenet/node/KeyTracker.java
===================================================================
--- trunk/freenet/src/freenet/node/KeyTracker.java      2008-11-14 15:12:16 UTC 
(rev 23579)
+++ trunk/freenet/src/freenet/node/KeyTracker.java      2008-11-14 15:37:44 UTC 
(rev 23580)
@@ -541,6 +541,11 @@
                }
                if(cbCount > 0 && logMINOR)
                        Logger.minor(this, "Executed " + cbCount + " 
callbacks");
+               try {
+                       wouldBlock(true);
+               } catch (BlockedTooLongException e) {
+                       // Ignore, will come up again. In any case it's rather 
unlikely...
+               }
        }
 
        /**
@@ -570,6 +575,11 @@
                                throttle.notifyOfPacketAcknowledged();
                                
throttle.setRoundTripTime(System.currentTimeMillis() - timeAdded);
                        }
+               try {
+                       wouldBlock(true);
+               } catch (BlockedTooLongException e) {
+                       // Ignore, will come up again. In any case it's rather 
unlikely...
+               }
                if(callbacks != null) {
                        for(int i = 0; i < callbacks.length; i++)
                                callbacks[i].acknowledged();
@@ -711,6 +721,36 @@
                        }
                }
        }
+       
+       private long timeWouldBlock = -1;
+       
+       static final long MAX_WOULD_BLOCK_DELTA = 10*60*1000;
+       
+       public boolean wouldBlock(boolean wakeTicker) throws 
BlockedTooLongException {
+               long now = System.currentTimeMillis();
+               synchronized(this) {
+                       if(sentPacketsContents.wouldBlock(nextPacketNumber)) {
+                               if(timeWouldBlock == -1)
+                                       timeWouldBlock = now;
+                               else {
+                                       long delta = now - timeWouldBlock;
+                                       if(delta > MAX_WOULD_BLOCK_DELTA) {
+                                               Logger.error(this, "Not been 
able to allocate a packet to tracker "+this+" for "+TimeUtil.formatTime(delta));
+                                               throw new 
BlockedTooLongException(this, delta);
+                                       }
+                               }
+                               return true;
+                       } else {
+                               long delta = now - timeWouldBlock;
+                               timeWouldBlock = -1;
+                               if(timeWouldBlock != -1 && (delta > 
PacketSender.MAX_COALESCING_DELAY)) {
+                                       Logger.error(this, "Waking 
PacketSender: have been blocking for packet ack for 
"+TimeUtil.formatTime(delta));
+                               } else return false;
+                       }
+               }
+               pn.node.ps.wakeUp();
+               return false;
+       }
 
        /**
         * @return A packet number for a new outgoing packet.
@@ -727,6 +767,7 @@
                        if(isDeprecated)
                                throw new KeyChangedException();
                        sentPacketsContents.lockNeverBlock(packetNumber);
+                       timeWouldBlock = -1;
                        nextPacketNumber = packetNumber + 1;
                        if(logMINOR)
                                Logger.minor(this, "Allocated " + packetNumber 
+ " in allocateOutgoingPacketNumberNeverBlock for " + this);

Modified: trunk/freenet/src/freenet/node/OutgoingPacketMangler.java
===================================================================
--- trunk/freenet/src/freenet/node/OutgoingPacketMangler.java   2008-11-14 
15:12:16 UTC (rev 23579)
+++ trunk/freenet/src/freenet/node/OutgoingPacketMangler.java   2008-11-14 
15:37:44 UTC (rev 23580)
@@ -27,9 +27,10 @@
         * @param onePacketOnly If true, we will only send one packet, and will 
requeue any
         * messages that don't fit in that single packet.
         * @return True if we sent a packet.
+        * @throws BlockedTooLongException 
         */
        public boolean processOutgoingOrRequeue(MessageItem[] messages, 
PeerNode pn,
-                       boolean neverWaitForPacketNumber, boolean dontRequeue, 
boolean onePacketOnly);
+                       boolean neverWaitForPacketNumber, boolean dontRequeue, 
boolean onePacketOnly) throws BlockedTooLongException;
 
        /**
         * Resend a single packet.

Modified: trunk/freenet/src/freenet/node/PacketSender.java
===================================================================
--- trunk/freenet/src/freenet/node/PacketSender.java    2008-11-14 15:12:16 UTC 
(rev 23579)
+++ trunk/freenet/src/freenet/node/PacketSender.java    2008-11-14 15:37:44 UTC 
(rev 23580)
@@ -4,15 +4,21 @@
 package freenet.node;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.TreeMap;
 import java.util.Vector;
 
 import org.tanukisoftware.wrapper.WrapperManager;
 
+import freenet.io.comm.Peer;
+import freenet.l10n.L10n;
+import freenet.node.useralerts.UserAlert;
 import freenet.support.FileLoggerHook;
+import freenet.support.HTMLNode;
 import freenet.support.Logger;
 import freenet.support.OOMHandler;
+import freenet.support.TimeUtil;
 import freenet.support.io.NativeThread;
 
 /**
@@ -251,6 +257,7 @@
                                        continue;
                                }
                                
+                               try {
                                if(pn.maybeSendPacket(now, rpiTemp, rpiIntTemp) 
&& canSendThrottled) {
                                        canSendThrottled = false;
                                        count = node.outputThrottle.getCount();
@@ -265,6 +272,11 @@
                                                newBrokeAt = i;
                                        }
                                }
+                               } catch (BlockedTooLongException e) {
+                                       Logger.error(this, "Waited too long: 
"+TimeUtil.formatTime(e.delta)+" to allocate a packet number to send to 
"+this+" on "+e.tracker+" - DISCONNECTING!");
+                                       pn.forceDisconnect(true);
+                                       onForceDisconnectBlockTooLong(pn, e);
+                               }
                                
                                long urgentTime = pn.getNextUrgentTime(now);
                                // Should spam the logs, unless there is a 
deadlock
@@ -409,6 +421,107 @@
                return brokeAt;
        }
 
+       private HashSet<Peer> peersDumpedBlockedTooLong = new HashSet<Peer>();
+       
+       private void onForceDisconnectBlockTooLong(PeerNode pn, 
BlockedTooLongException e) {
+               Peer p = pn.getPeer();
+               synchronized(peersDumpedBlockedTooLong) {
+                       peersDumpedBlockedTooLong.add(p);
+                       if(peersDumpedBlockedTooLong.size() > 1) return;
+               }
+               node.clientCore.alerts.register(peersDumpedBlockedTooLongAlert);
+       }
+       
+       private UserAlert peersDumpedBlockedTooLongAlert = new UserAlert() {
+
+               public String anchor() {
+                       return "disconnectedStillNotAcked";
+               }
+
+               public String dismissButtonText() {
+                       return null;
+               }
+
+               public short getPriorityClass() {
+                       return UserAlert.ERROR;
+               }
+
+               public String getShortText() {
+                       int sz;
+                       synchronized(peersDumpedBlockedTooLong) {
+                               sz = peersDumpedBlockedTooLong.size();
+                       }
+                       return l10n("somePeersDisconnectedBlockedTooLong", 
"count", Integer.toString(sz));
+               }
+
+               public HTMLNode getHTMLText() {
+                       HTMLNode div = new HTMLNode("div");
+                       Peer[] peers;
+                       synchronized(peersDumpedBlockedTooLong) {
+                               peers = peersDumpedBlockedTooLong.toArray(new 
Peer[peersDumpedBlockedTooLong.size()]);
+                       }
+                       L10n.addL10nSubstitution(div, 
"FNPPacketMangler.somePeersDisconnectedBlockedTooLongDetail", 
+                                       new String[] { "count", "link", "/link" 
}
+                                       , new String[] { 
Integer.toString(peers.length), "<a 
href=\"/?_CHECKED_HTTP_=https://bugs.freenetproject.org/\";>", "</a>" });
+                       HTMLNode list = div.addChild("ul");
+                       for(Peer peer : peers) {
+                               list.addChild("li", peer.toString());
+                       }
+                       return div;
+               }
+
+               public String getText() {
+                       StringBuffer sb = new StringBuffer();
+                       Peer[] peers;
+                       synchronized(peersDumpedBlockedTooLong) {
+                               peers = peersDumpedBlockedTooLong.toArray(new 
Peer[peersDumpedBlockedTooLong.size()]);
+                       }
+                       
sb.append(l10n("somePeersDisconnectedStillNotAckedDetail", 
+                                       new String[] { "count", "link", "/link" 
},
+                                       new String[] { 
Integer.toString(peers.length), "", "" } ));
+                       sb.append('\n');
+                       for(Peer peer : peers) {
+                               sb.append('\t');
+                               sb.append(peer.toString());
+                               sb.append('\n');
+                       }
+                       return sb.toString();
+               }
+               
+               public String getTitle() {
+                       return getShortText();
+               }
+
+               public Object getUserIdentifier() {
+                       return PacketSender.this;
+               }
+
+               public boolean isEventNotification() {
+                       return false;
+               }
+
+               public boolean isValid() {
+                       return true;
+               }
+
+               public void isValid(boolean validity) {
+                       // Ignore
+               }
+
+               public void onDismiss() {
+                       // Ignore
+               }
+
+               public boolean shouldUnregisterOnDismiss() {
+                       return false;
+               }
+
+               public boolean userCanDismiss() {
+                       return false;
+               }
+
+       };
+
        /** Wake up, and send any queued packets. */
        void wakeUp() {
                // Wake up if needed
@@ -417,12 +530,18 @@
                }
        }
 
+       protected String l10n(String key, String[] patterns, String[] values) {
+               return L10n.getString("PacketSender."+key, patterns, values);
+       }
+
+       protected String l10n(String key, String pattern, String value) {
+               return L10n.getString("PacketSender."+key, pattern, value);
+       }
+
        public void queueTimedJob(Runnable job, long offset) {
                queueTimedJob(job, "Scheduled job: "+job, offset, false);
        }
        
-       
-       
        public void queueTimedJob(Runnable runner, String name, long offset, 
boolean runOnTickerAnyway) {
                // Run directly *if* that won't cause any priority problems.
                if(offset <= 0 && !runOnTickerAnyway) {

Modified: trunk/freenet/src/freenet/node/PeerNode.java
===================================================================
--- trunk/freenet/src/freenet/node/PeerNode.java        2008-11-14 15:12:16 UTC 
(rev 23579)
+++ trunk/freenet/src/freenet/node/PeerNode.java        2008-11-14 15:37:44 UTC 
(rev 23580)
@@ -1256,23 +1256,35 @@
        */
        public long getNextUrgentTime(long now) {
                long t = Long.MAX_VALUE;
+               KeyTracker cur;
+               KeyTracker prev;
                synchronized(this) {
-               KeyTracker kt = currentTracker;
-               if(kt != null) {
+                       cur = currentTracker;
+                       prev = previousTracker;
+               }
+               KeyTracker kt = cur;
+               try {
+               if(kt != null && !kt.wouldBlock(false)) {
                        long next = kt.getNextUrgentTime();
                        t = Math.min(t, next);
                        if(next < now && logMINOR)
                                Logger.minor(this, "Next urgent time from 
curTracker less than now");
                        if(kt.hasPacketsToResend()) return now;
                }
-               kt = previousTracker;
-               if(kt != null) {
+               } catch (BlockedTooLongException e) {
+                       // Ignore for now, it will come back around
+               }
+               kt = prev;
+               try {
+               if(kt != null && !kt.wouldBlock(false)) {
                        long next = kt.getNextUrgentTime();
                        t = Math.min(t, next);
                        if(next < now && logMINOR)
                                Logger.minor(this, "Next urgent time from 
prevTracker less than now");
                        if(kt.hasPacketsToResend()) return now;
                }
+               } catch (BlockedTooLongException e) {
+                       // Ignore for now, it will come back around
                }
                t = messageQueue.getNextUrgentTime(t, now);
                return t;
@@ -4016,8 +4028,9 @@
         * @param now
         * @param rpiTemp
         * @param rpiTemp
+        * @throws BlockedTooLongException 
         */
-       public boolean maybeSendPacket(long now, Vector<ResendPacketItem> 
rpiTemp, int[] rpiIntTemp) {
+       public boolean maybeSendPacket(long now, Vector<ResendPacketItem> 
rpiTemp, int[] rpiIntTemp) throws BlockedTooLongException {
                // If there are any urgent notifications, we must send a packet.
                boolean mustSend = false;
                boolean mustSendPacket = false;

Modified: trunk/freenet/src/freenet/support/LimitedRangeIntByteArrayMap.java
===================================================================
--- trunk/freenet/src/freenet/support/LimitedRangeIntByteArrayMap.java  
2008-11-14 15:12:16 UTC (rev 23579)
+++ trunk/freenet/src/freenet/support/LimitedRangeIntByteArrayMap.java  
2008-11-14 15:37:44 UTC (rev 23580)
@@ -146,6 +146,11 @@
         }
     }
     
+    public boolean wouldBlock(int index) {
+       if(minValue == -1) return false;
+       return (index - minValue >= maxRange);
+    }
+    
     /**
      * Wait until add(index, whatever) would return true.
      * If this returns, add(index, whatever) will work.

_______________________________________________
cvs mailing list
[email protected]
http://emu.freenetproject.org/cgi-bin/mailman/listinfo/cvs

Reply via email to