From 33ee8a38caa7f94c40f479404a7964f9e501086f Mon Sep 17 00:00:00 2001
From: zab <zab@mail.i2p>
Date: Wed, 21 Nov 2012 15:45:38 +0000
Subject: [PATCH] Ticket #765 - optimize locking during profile reorg

---
 .../i2p/router/peermanager/PeerProfile.java   | 60 +++++++++++++------
 .../router/peermanager/ProfileOrganizer.java  | 26 +++++---
 2 files changed, 62 insertions(+), 24 deletions(-)

diff --git a/router/java/src/net/i2p/router/peermanager/PeerProfile.java b/router/java/src/net/i2p/router/peermanager/PeerProfile.java
index 68b4629b46..476f549c50 100644
--- a/router/java/src/net/i2p/router/peermanager/PeerProfile.java
+++ b/router/java/src/net/i2p/router/peermanager/PeerProfile.java
@@ -54,6 +54,13 @@ public class PeerProfile {
     private double _capacityValue;
     private double _integrationValue;
     private boolean _isFailing;
+    // new calculation values, to be updated
+    private double _speedValueNew;
+    private double _capacityValueNew;
+    private double _integrationValueNew;
+    private boolean _isFailingNew;
+    // are we in coalescing state?
+    private boolean _coalescing;
     // good vs bad behavior
     private TunnelHistory _tunnelHistory;
     private DBHistory _dbHistory;
@@ -500,28 +507,47 @@ public class PeerProfile {
     /** update the stats and rates (this should be called once a minute) */
     public void coalesceStats() {
         if (!_expanded) return;
-        //_receiveSize.coalesceStats();
-        //_sendSuccessSize.coalesceStats();
-        _tunnelCreateResponseTime.coalesceStats();
-        _tunnelTestResponseTime.coalesceStats();
-        _tunnelHistory.coalesceStats();
-        if (_expandedDB) {
-            _dbIntroduction.coalesceStats();
-            _dbResponseTime.coalesceStats();
-            _dbHistory.coalesceStats();
-        }
-        
-        coalesceThroughput();
-        
-        _speedValue = calculateSpeed();
-        _capacityValue = calculateCapacity();
-        _integrationValue = calculateIntegration();
-        _isFailing = calculateIsFailing();
+
+        coalesceOnly();
+        updateValues();
         
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("Coalesced: speed [" + _speedValue + "] capacity [" + _capacityValue + "] integration [" + _integrationValue + "] failing? [" + _isFailing + "]");
     }
     
+    void coalesceOnly() {
+    	_coalescing = true;
+    	
+    	//_receiveSize.coalesceStats();
+    	//_sendSuccessSize.coalesceStats();
+    	_tunnelCreateResponseTime.coalesceStats();
+    	_tunnelTestResponseTime.coalesceStats();
+    	_tunnelHistory.coalesceStats();
+    	if (_expandedDB) {
+    		_dbIntroduction.coalesceStats();
+    		_dbResponseTime.coalesceStats();
+    		_dbHistory.coalesceStats();
+    	}
+    	
+    	coalesceThroughput();
+    	
+    	_speedValueNew = calculateSpeed();
+    	_capacityValueNew = calculateCapacity();
+    	_integrationValueNew = calculateIntegration();
+    	_isFailingNew = calculateIsFailing();
+    }
+    
+    void updateValues() {
+    	if (!_coalescing) // can happen
+    		coalesceOnly();
+    	_coalescing = false;
+    	
+    	_speedValue = _speedValueNew;
+    	_capacityValue = _capacityValueNew;
+    	_integrationValue = _integrationValueNew;
+    	_isFailing = _isFailingNew;
+    }
+    
     private double calculateSpeed() { return SpeedCalculator.calc(this); }
     private double calculateCapacity() { return CapacityCalculator.calc(this); }
     private double calculateIntegration() { return IntegrationCalculator.calc(this); }
diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
index bd834c7b92..c7e45262b2 100644
--- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
+++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
@@ -780,7 +780,24 @@ public class ProfileOrganizer {
             // drop profiles that we haven't spoken to in a while
             expireOlderThan = _context.clock().now() - _currentExpireTime;
         }
-            
+        
+        if (shouldCoalesce) {
+        	getReadLock();
+        	try {
+        		for (Iterator<PeerProfile> iter = _strictCapacityOrder.iterator(); iter.hasNext(); ) {
+                    PeerProfile prof = iter.next();
+                    if ( (expireOlderThan > 0) && (prof.getLastSendSuccessful() <= expireOlderThan) ) {
+                        continue;
+                    }
+                    long coalesceStart = System.currentTimeMillis();
+                    prof.coalesceOnly();
+                    coalesceTime += (int)(System.currentTimeMillis()-coalesceStart);
+        		}
+        	} finally {
+        		releaseReadLock();
+        	}
+        }
+        
         if (!getWriteLock())
             return;
         long start = System.currentTimeMillis();
@@ -800,12 +817,7 @@ public class ProfileOrganizer {
                     continue; // drop, but no need to delete, since we don't periodically reread
                     // TODO maybe we should delete files, otherwise they are only deleted at restart
                 }
-                
-                if (shouldCoalesce) {
-                    long coalesceStart = System.currentTimeMillis();
-                    prof.coalesceStats();
-                    coalesceTime += (int)(System.currentTimeMillis()-coalesceStart);
-                }
+                prof.updateValues();
                 reordered.add(prof);
                 profileCount++;
             }
-- 
GitLab