Changeset a7763a0


Ignore:
Timestamp:
Sep 14, 2014 1:04:48 PM (6 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
0a41052
Parents:
b9e3831
Message:

SSU OutboundMessageState? -
Fix SSU Output Queue errors due to races with PacketBuilder?:

  • Remove all buffer caching as it can't be made thread-safe. Just allocate buffer in constructor and let GC handle it
  • Do fragmenting in constructor and make all fragment fields final
  • Don't track per-fragment retransmissions as it wasn't used
  • Move ack tracking from an array to a long
  • Sync all ack methods
  • Entire class now thread-safe (thx dg)
Location:
router/java/src/net/i2p/router/transport/udp
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java

    rb9e3831 ra7763a0  
    55import net.i2p.I2PAppContext;
    66import net.i2p.data.Base64;
    7 import net.i2p.data.ByteArray;
    87import net.i2p.data.i2np.I2NPMessage;
    98import net.i2p.router.OutNetMessage;
    109import net.i2p.router.util.CDPQEntry;
    11 import net.i2p.util.ByteCache;
    1210import net.i2p.util.Log;
    1311
    1412/**
    1513 * Maintain the outbound fragmentation for resending, for a single message.
     14 *
     15 * All methods are thread-safe.
    1616 *
    1717 */
     
    2222    private final OutNetMessage _message;
    2323    private final I2NPMessage _i2npMessage;
    24     private final long _messageId;
    2524    /** will be null, unless we are part of the establishment */
    2625    private final PeerState _peer;
    2726    private final long _expiration;
    28     private ByteArray _messageBuf;
     27    private final byte[] _messageBuf;
    2928    /** fixed fragment size across the message */
    30     private int _fragmentSize;
    31     /** size of the I2NP message */
    32     private int _totalSize;
    33     /** sends[i] is how many times the fragment has been sent, or -1 if ACKed
    34      *  TODO this may not accurately track the number of retransmissions per-fragment,
    35      *  and we don't make any use of it anyway, so we should just make it a bitfield.
    36      */
    37     private short _fragmentSends[];
     29    private final int _fragmentSize;
     30    /** bitmask, 0 if acked, all 0 = complete */
     31    private long _fragmentAcks;
     32    private final int _numFragments;
    3833    private final long _startedOn;
    3934    private long _nextSendTime;
    4035    private int _pushCount;
    41     private short _maxSends;
    42     // private int _nextSendFragment;
    43     /** for tracking use-after-free bugs */
    44     private boolean _released;
    45     private Exception _releasedBy;
     36    private int _maxSends;
    4637    // we can't use the ones in _message since it is null for injections
    4738    private long _enqueueTime;
     
    4940   
    5041    public static final int MAX_MSG_SIZE = 32 * 1024;
    51     private static final int CACHE4_BYTES = MAX_MSG_SIZE;
    52     private static final int CACHE3_BYTES = CACHE4_BYTES / 4;
    53     private static final int CACHE2_BYTES = CACHE3_BYTES / 4;
    54     private static final int CACHE1_BYTES = CACHE2_BYTES / 4;
    55 
    56     private static final int CACHE1_MAX = 256;
    57     private static final int CACHE2_MAX = CACHE1_MAX / 4;
    58     private static final int CACHE3_MAX = CACHE2_MAX / 4;
    59     private static final int CACHE4_MAX = CACHE3_MAX / 4;
    60 
    61     private static final ByteCache _cache1 = ByteCache.getInstance(CACHE1_MAX, CACHE1_BYTES);
    62     private static final ByteCache _cache2 = ByteCache.getInstance(CACHE2_MAX, CACHE2_BYTES);
    63     private static final ByteCache _cache3 = ByteCache.getInstance(CACHE3_MAX, CACHE3_BYTES);
    64     private static final ByteCache _cache4 = ByteCache.getInstance(CACHE4_MAX, CACHE4_BYTES);
    6542
    6643    private static final long EXPIRATION = 10*1000;
     
    6845
    6946    /**
    70      *  Called from UDPTransport
     47     *  "injected" message from the establisher.
     48     *
     49     *  Called from UDPTransport.
    7150     *  @throws IAE if too big or if msg or peer is null
    7251     */
     
    7655   
    7756    /**
    78      *  Called from OutboundMessageFragments
     57     *  Normal constructor.
     58     *
     59     *  Called from OutboundMessageFragments.
    7960     *  @throws IAE if too big or if msg or peer is null
    8061     */
     
    9677        _i2npMessage = msg;
    9778        _peer = peer;
    98         _messageId = msg.getUniqueId();
    9979        _startedOn = _context.clock().now();
    10080        _nextSendTime = _startedOn;
     
    10282        //_expiration = msg.getExpiration();
    10383
    104         //if (_log.shouldLog(Log.DEBUG))
    105         //    _log.debug("Raw byte array for " + _messageId + ": " + Base64.encode(_messageBuf.getData(), 0, len));
    106     }
    107    
    108     /**
    109      * lazily inits the message buffer unless already inited
    110      */
    111     private synchronized void initBuf() {
    112         if (_messageBuf != null)
    113             return;
    114         final int size = _i2npMessage.getRawMessageSize();
    115         acquireBuf(size);
    116         _totalSize = _i2npMessage.toRawByteArray(_messageBuf.getData());
    117         _messageBuf.setValid(_totalSize);
    118     }
    119    
    120     /**
    121      *  @throws IAE if too big
    122      *  @since 0.9.3
    123      */
    124     private void acquireBuf(int size) {
    125         if (_messageBuf != null)
    126             releaseBuf();
    127         if (size <= CACHE1_BYTES)
    128             _messageBuf =  _cache1.acquire();
    129         else if (size <= CACHE2_BYTES)
    130             _messageBuf = _cache2.acquire();
    131         else if (size <= CACHE3_BYTES)
    132             _messageBuf = _cache3.acquire();
    133         else if (size <= CACHE4_BYTES)
    134             _messageBuf = _cache4.acquire();
    135         else
    136             throw new IllegalArgumentException("Size too large! " + size);
    137     }
    138    
    139     /**
    140      *  @since 0.9.3
    141      */
    142     private void releaseBuf() {
    143         if (_messageBuf == null)
    144             return;
    145         int size = _messageBuf.getData().length;
    146         if (size == CACHE1_BYTES)
    147             _cache1.release(_messageBuf);
    148         else if (size == CACHE2_BYTES)
    149             _cache2.release(_messageBuf);
    150         else if (size == CACHE3_BYTES)
    151             _cache3.release(_messageBuf);
    152         else if (size == CACHE4_BYTES)
    153             _cache4.release(_messageBuf);
    154         _messageBuf = null;
    155         _released = true;
    156     }
    157 
    158     /**
    159      *  This is synchronized with writeFragment(),
    160      *  so we do not release (probably due to an ack) while we are retransmitting.
    161      *  Also prevent double-free
    162      */
    163     public synchronized void releaseResources() {
    164         if (_messageBuf != null && !_released) {
    165             releaseBuf();
    166             if (_log.shouldLog(Log.WARN))
    167                 _releasedBy = new Exception ("Released on " + new Date() + " by:");
    168         }
    169         //_messageBuf = null;
    170     }
    171    
    172     public OutNetMessage getMessage() { return _message; }
    173     public long getMessageId() { return _messageId; }
    174     public PeerState getPeer() { return _peer; }
    175 
    176     public boolean isExpired() {
    177         return _expiration < _context.clock().now();
    178     }
    179 
    180     public boolean isComplete() {
    181         short sends[] = _fragmentSends;
    182         if (sends == null) return false;
    183         for (int i = 0; i < sends.length; i++)
    184             if (sends[i] >= 0)
    185                 return false;
    186         // nothing else pending ack
    187         return true;
    188     }
    189 
    190     public synchronized int getUnackedSize() {
    191         short fragmentSends[] = _fragmentSends;
    192         ByteArray messageBuf = _messageBuf;
    193         int rv = 0;
    194         if ( (messageBuf != null) && (fragmentSends != null) ) {
    195             int lastSize = _totalSize % _fragmentSize;
    196             if (lastSize == 0)
    197                 lastSize = _fragmentSize;
    198             for (int i = 0; i < fragmentSends.length; i++) {
    199                 if (fragmentSends[i] >= (short)0) {
    200                     if (i + 1 == fragmentSends.length)
    201                         rv += lastSize;
    202                     else
    203                         rv += _fragmentSize;
    204                 }
    205             }
    206         }
    207         return rv;
    208     }
    209 
    210     public boolean needsSending(int fragment) {
    211         short sends[] = _fragmentSends;
    212         if ( (sends == null) || (fragment >= sends.length) || (fragment < 0) )
    213             return false;
    214         return (sends[fragment] >= (short)0);
    215     }
    216 
    217     public long getLifetime() { return _context.clock().now() - _startedOn; }
    218    
    219     /**
    220      * Ack all the fragments in the ack list.  As a side effect, if there are
    221      * still unacked fragments, the 'next send' time will be updated under the
    222      * assumption that that all of the packets within a volley would reach the
    223      * peer within that ack frequency (2-400ms).
    224      *
    225      * @return true if the message was completely ACKed
    226      */
    227     public boolean acked(ACKBitfield bitfield) {
    228         // stupid brute force, but the cardinality should be trivial
    229         short sends[] = _fragmentSends;
    230         if (sends != null) {
    231             for (int i = 0; i < bitfield.fragmentCount() && i < sends.length; i++) {
    232                 if (bitfield.received(i))
    233                     sends[i] = (short)-1;
    234             }
    235         }
    236        
    237         boolean rv = isComplete();
    238       /****
    239         if (!rv && false) { // don't do the fast retransmit... lets give it time to get ACKed
    240             long nextTime = _context.clock().now() + Math.max(_peer.getRTT(), ACKSender.ACK_FREQUENCY);
    241             //_nextSendTime = Math.max(now, _startedOn+PeerState.MIN_RTO);
    242             if (_nextSendTime <= 0)
    243                 _nextSendTime = nextTime;
    244             else
    245                 _nextSendTime = Math.min(_nextSendTime, nextTime);
    246            
    247             //if (now + 100 > _nextSendTime)
    248             //    _nextSendTime = now + 100;
    249             //_nextSendTime = now;
    250         }
    251       ****/
    252         return rv;
    253     }
    254    
    255     public long getNextSendTime() { return _nextSendTime; }
    256     public void setNextSendTime(long when) { _nextSendTime = when; }
    257 
    258     /**
    259      *  The max number of sends for any fragment, which is the
    260      *  same as the push count, at least as it's coded now.
    261      */
    262     public int getMaxSends() { return _maxSends; }
    263 
    264     /**
    265      *  The number of times we've pushed some fragments, which is the
    266      *  same as the max sends, at least as it's coded now.
    267      */
    268     public int getPushCount() { return _pushCount; }
    269 
    270     /**
    271      * Note that we have pushed the message fragments.
    272      * Increments push count (and max sends... why?)
    273      */
    274     public void push() {
    275         // these will never be different...
    276         _pushCount++;
    277         if (_pushCount > _maxSends)
    278             _maxSends = (short)_pushCount;
    279         if (_fragmentSends != null)
    280             for (int i = 0; i < _fragmentSends.length; i++)
    281                 if (_fragmentSends[i] >= (short)0)
    282                     _fragmentSends[i]++;
    283        
    284     }
    285 
    286     /**
    287      * Whether fragment() has been called.
    288      * NOT whether it has more than one fragment.
    289      *
    290      * Caller should synchronize
    291      *
    292      * @return true iff fragment() has been called previously
    293      */
    294     public boolean isFragmented() { return _fragmentSends != null; }
    295 
    296     /**
    297      * Prepare the message for fragmented delivery, using no more than
    298      * fragmentSize bytes per fragment.
    299      *
    300      * Caller should synchronize
    301      *
    302      * @throws IllegalStateException if called more than once
    303      */
    304     public void fragment(int fragmentSize) {
    305         if (_fragmentSends != null)
    306             throw new IllegalStateException();
    307         initBuf();
    308         int numFragments = _totalSize / fragmentSize;
    309         if (numFragments * fragmentSize < _totalSize)
     84        // now "fragment" it
     85        int totalSize = _i2npMessage.getRawMessageSize();
     86        if (totalSize > MAX_MSG_SIZE)
     87            throw new IllegalArgumentException("Size too large! " + totalSize);
     88        _messageBuf = new byte[totalSize];
     89        _i2npMessage.toRawByteArray(_messageBuf);
     90        _fragmentSize = _peer.fragmentSize();
     91        int numFragments = totalSize / _fragmentSize;
     92        if (numFragments * _fragmentSize < totalSize)
    31093            numFragments++;
    31194        // This should never happen, as 534 bytes * 64 fragments > 32KB, and we won't bid on > 32KB
    31295        if (numFragments > InboundMessageState.MAX_FRAGMENTS)
    313             throw new IllegalArgumentException("Fragmenting a " + _totalSize + " message into " + numFragments + " fragments - too many!");
    314         if (_log.shouldLog(Log.DEBUG))
    315             _log.debug("Fragmenting a " + _totalSize + " message into " + numFragments + " fragments");
    316        
    317         //_fragmentEnd = new int[numFragments];
    318         _fragmentSends = new short[numFragments];
    319         //Arrays.fill(_fragmentEnd, -1);
    320         //Arrays.fill(_fragmentSends, (short)0);
    321        
    322         _fragmentSize = fragmentSize;
     96            throw new IllegalArgumentException("Fragmenting a " + totalSize + " message into " + numFragments + " fragments - too many!");
     97        _numFragments = numFragments;
     98        // all 1's where we care
     99        _fragmentAcks = _numFragments < 64 ? mask(_numFragments) - 1L : -1L;
     100    }
     101   
     102    /**
     103     *  @param fragment 0-63
     104     */
     105    private static long mask(int fragment) {
     106        return 1L << fragment;
     107    }
     108
     109    public OutNetMessage getMessage() { return _message; }
     110
     111    public long getMessageId() { return _i2npMessage.getUniqueId(); }
     112
     113    public PeerState getPeer() { return _peer; }
     114
     115    public boolean isExpired() {
     116        return _expiration < _context.clock().now();
     117    }
     118
     119    public synchronized boolean isComplete() {
     120        return _fragmentAcks == 0;
     121    }
     122
     123    public synchronized int getUnackedSize() {
     124        int rv = 0;
     125        if (isComplete())
     126            return rv;
     127        int lastSize = _messageBuf.length % _fragmentSize;
     128        if (lastSize == 0)
     129            lastSize = _fragmentSize;
     130        for (int i = 0; i < _numFragments; i++) {
     131            if (needsSending(i)) {
     132                if (i + 1 == _numFragments)
     133                    rv += lastSize;
     134                else
     135                    rv += _fragmentSize;
     136            }
     137        }
     138        return rv;
     139    }
     140
     141    public synchronized boolean needsSending(int fragment) {
     142        return (_fragmentAcks & mask(fragment)) != 0;
     143    }
     144
     145    public long getLifetime() { return _context.clock().now() - _startedOn; }
     146   
     147    /**
     148     * Ack all the fragments in the ack list.
     149     *
     150     * @return true if the message was completely ACKed
     151     */
     152    public synchronized boolean acked(ACKBitfield bitfield) {
     153        // stupid brute force, but the cardinality should be trivial
     154        for (int i = 0; i < bitfield.fragmentCount() && i < _numFragments; i++) {
     155            if (bitfield.received(i))
     156                _fragmentAcks &= ~mask(i);
     157        }
     158        return isComplete();
     159    }
     160   
     161    public long getNextSendTime() { return _nextSendTime; }
     162    public void setNextSendTime(long when) { _nextSendTime = when; }
     163
     164    /**
     165     *  The max number of sends for any fragment, which is the
     166     *  same as the push count, at least as it's coded now.
     167     */
     168    public synchronized int getMaxSends() { return _maxSends; }
     169
     170    /**
     171     *  The number of times we've pushed some fragments, which is the
     172     *  same as the max sends, at least as it's coded now.
     173     */
     174    public synchronized int getPushCount() { return _pushCount; }
     175
     176    /**
     177     * Note that we have pushed the message fragments.
     178     * Increments push count (and max sends... why?)
     179     */
     180    public synchronized void push() {
     181        // these will never be different...
     182        _pushCount++;
     183        _maxSends = _pushCount;
    323184    }
    324185
    325186    /**
    326187     * How many fragments in the message.
    327      * Only valid after fragment() has been called.
    328      * Returns -1 before then.
    329      *
    330      * Caller should synchronize
    331188     */
    332189    public int getFragmentCount() {
    333         if (_fragmentSends == null)
    334             return -1;
    335         else
    336             return _fragmentSends.length;
     190            return _numFragments;
    337191    }
    338192
    339193    /**
    340194     * The size of the I2NP message. Does not include any SSU overhead.
    341      *
    342      * Caller should synchronize
    343      */
    344     public int getMessageSize() { return _totalSize; }
    345 
    346     /**
    347      * Should we continue sending this fragment?
    348      * Only valid after fragment() has been called.
    349      * Throws NPE before then.
    350      *
    351      * Caller should synchronize
    352      *
    353      * @return true if fragment is not acked yet
    354      */
    355     public boolean shouldSend(int fragmentNum) { return _fragmentSends[fragmentNum] >= (short)0; }
    356 
    357     /**
    358      * This assumes fragment(int size) has been called
     195     */
     196    public int getMessageSize() { return _messageBuf.length; }
     197
     198    /**
     199     * The size in bytes of the fragment
     200     *
    359201     * @param fragmentNum the number of the fragment
    360      *
    361202     * @return the size of the fragment specified by the number
    362203     */
    363204    public int fragmentSize(int fragmentNum) {
    364         if (_messageBuf == null) return -1;
    365         if (fragmentNum + 1 == _fragmentSends.length) {
    366             int valid = _totalSize;
     205        if (fragmentNum + 1 == _numFragments) {
     206            int valid = _messageBuf.length;
    367207            if (valid <= _fragmentSize)
    368208                return valid;
     
    377217    /**
    378218     * Write a part of the the message onto the specified buffer.
    379      * See releaseResources() above for synchronization information.
    380      * This assumes fragment(int size) has been called.
    381219     *
    382220     * @param out target to write
     
    385223     * @return bytesWritten
    386224     */
    387     public synchronized int writeFragment(byte out[], int outOffset, int fragmentNum) {
    388         if (_messageBuf == null) return -1;
    389         if (_released) {
    390             /******
    391                 Solved by synchronization with releaseResources() and simply returning -1.
    392                 Previous output:
    393 
    394                 23:50:57.013 ERROR [acket pusher] sport.udp.OutboundMessageState: SSU OMS Use after free
    395                 java.lang.Exception: Released on Wed Dec 23 23:50:57 GMT 2009 by:
    396                         at net.i2p.router.transport.udp.OutboundMessageState.releaseResources(OutboundMessageState.java:133)
    397                         at net.i2p.router.transport.udp.PeerState.acked(PeerState.java:1391)
    398                         at net.i2p.router.transport.udp.OutboundMessageFragments.acked(OutboundMessageFragments.java:404)
    399                         at net.i2p.router.transport.udp.InboundMessageFragments.receiveACKs(InboundMessageFragments.java:191)
    400                         at net.i2p.router.transport.udp.InboundMessageFragments.receiveData(InboundMessageFragments.java:77)
    401                         at net.i2p.router.transport.udp.PacketHandler$Handler.handlePacket(PacketHandler.java:485)
    402                         at net.i2p.router.transport.udp.PacketHandler$Handler.receivePacket(PacketHandler.java:282)
    403                         at net.i2p.router.transport.udp.PacketHandler$Handler.handlePacket(PacketHandler.java:231)
    404                         at net.i2p.router.transport.udp.PacketHandler$Handler.run(PacketHandler.java:136)
    405                         at java.lang.Thread.run(Thread.java:619)
    406                         at net.i2p.util.I2PThread.run(I2PThread.java:71)
    407                 23:50:57.014 ERROR [acket pusher] ter.transport.udp.PacketPusher: SSU Output Queue Error
    408                 java.lang.RuntimeException: SSU OMS Use after free: Message 2381821417 with 4 fragments of size 0 volleys: 2 lifetime: 1258 pending fragments: 0 1 2 3
    409                         at net.i2p.router.transport.udp.OutboundMessageState.writeFragment(OutboundMessageState.java:298)
    410                         at net.i2p.router.transport.udp.PacketBuilder.buildPacket(PacketBuilder.java:170)
    411                         at net.i2p.router.transport.udp.OutboundMessageFragments.preparePackets(OutboundMessageFragments.java:332)
    412                         at net.i2p.router.transport.udp.OutboundMessageFragments.getNextVolley(OutboundMessageFragments.java:297)
    413                         at net.i2p.router.transport.udp.PacketPusher.run(PacketPusher.java:38)
    414                         at java.lang.Thread.run(Thread.java:619)
    415                         at net.i2p.util.I2PThread.run(I2PThread.java:71)
    416             *******/
    417             if (_log.shouldLog(Log.WARN))
    418                 _log.log(Log.WARN, "SSU OMS Use after free: " + toString(), _releasedBy);
    419             return -1;
    420             //throw new RuntimeException("SSU OMS Use after free: " + toString());
    421         }
     225    public int writeFragment(byte out[], int outOffset, int fragmentNum) {
    422226        int start = _fragmentSize * fragmentNum;
    423         int end = start + fragmentSize(fragmentNum);
    424         int toSend = end - start;
    425         byte buf[] = _messageBuf.getData();
    426         if ( (buf != null) && (start + toSend <= buf.length) && (outOffset + toSend <= out.length) ) {
    427             System.arraycopy(buf, start, out, outOffset, toSend);
    428             if (_log.shouldLog(Log.DEBUG))
    429                 _log.debug("Raw fragment[" + fragmentNum + "] for " + _messageId
    430                            + "[" + start + "-" + (start+toSend) + "/" + _totalSize + "/" + _fragmentSize + "]: "
    431                            + Base64.encode(out, outOffset, toSend));
     227        int toSend = fragmentSize(fragmentNum);
     228        int end = start + toSend;
     229        if (end <= _messageBuf.length && outOffset + toSend <= out.length) {
     230            System.arraycopy(_messageBuf, start, out, outOffset, toSend);
    432231            return toSend;
    433         } else if (buf == null) {
    434             if (_log.shouldLog(Log.WARN))
    435                 _log.warn("Error: null buf");
    436232        } else {
    437233            if (_log.shouldLog(Log.WARN))
     
    463259    public void drop() {
    464260        _peer.getTransport().failed(this, false);
    465         releaseResources();
    466261    }
    467262
     
    493288    @Override
    494289    public String toString() {
    495         short sends[] = _fragmentSends;
    496290        StringBuilder buf = new StringBuilder(256);
    497         buf.append("OB Message ").append(_messageId);
    498         if (sends != null)
    499             buf.append(" with ").append(sends.length).append(" fragments");
    500         buf.append(" of size ").append(_totalSize);
     291        buf.append("OB Message ").append(_i2npMessage.getUniqueId());
     292        buf.append(" with ").append(_numFragments).append(" fragments");
     293        buf.append(" of size ").append(_messageBuf.length);
    501294        buf.append(" volleys: ").append(_maxSends);
    502295        buf.append(" lifetime: ").append(getLifetime());
    503         if (sends != null) {
     296        if (!isComplete()) {
    504297            buf.append(" pending fragments: ");
    505             for (int i = 0; i < sends.length; i++)
    506                 if (sends[i] >= 0)
     298            for (int i = 0; i < _numFragments; i++) {
     299                if (needsSending(i))
    507300                    buf.append(i).append(' ');
     301            }
    508302        }
    509303        return buf.toString();
  • router/java/src/net/i2p/router/transport/udp/PeerState.java

    rb9e3831 ra7763a0  
    15421542            OutboundMessageState state = succeeded.get(i);
    15431543            _transport.succeeded(state);
    1544             state.releaseResources();
    15451544            OutNetMessage msg = state.getMessage();
    15461545            if (msg != null)
     
    15601559                    _log.warn("Unable to send a direct message: " + state);
    15611560            }
    1562             state.releaseResources();
    15631561        }
    15641562       
     
    17091707     *  @return MTU - 87, i.e. 533 or 1397 (IPv4), MTU - 107 (IPv6)
    17101708     */
    1711     private int fragmentSize() {
     1709    public int fragmentSize() {
    17121710        // 46 + 20 + 8 + 13 = 74 + 13 = 87 (IPv4)
    17131711        // 46 + 40 + 8 + 13 = 74 + 13 = 107 (IPv6)
     
    17281726        long now = _context.clock().now();
    17291727        if (state.getNextSendTime() <= now) {
    1730             if (!state.isFragmented()) {
    1731                 state.fragment(fragmentSize());
    1732                 if (state.getMessage() != null)
    1733                     state.getMessage().timestamp("fragment into " + state.getFragmentCount());
    1734 
    1735                 if (_log.shouldLog(Log.INFO))
    1736                     _log.info("Fragmenting " + state);
    1737             }
    1738 
    1739            
    17401728            OutboundMessageState retrans = _retransmitter;
    17411729            if ( (retrans != null) && ( (retrans.isExpired() || retrans.isComplete()) ) ) {
     
    18591847            //    _throttle.unchoke(peer.getRemotePeer());
    18601848           
    1861             state.releaseResources();
    18621849        } else {
    18631850            // dupack, likely
     
    19361923                //    _throttle.unchoke(state.getPeer().getRemotePeer());
    19371924
    1938                 state.releaseResources();
    19391925            } else {
    19401926                //if (state.getMessage() != null)
Note: See TracChangeset for help on using the changeset viewer.