FileDocCategorySizeDatePackage
SpeedManagerImpl.javaAPI DocAzureus 3.0.3.424370Wed Aug 08 09:41:18 BST 2007com.aelitis.azureus.core.speedmanager.impl

SpeedManagerImpl

public class SpeedManagerImpl extends Object implements com.aelitis.azureus.core.speedmanager.SpeedManager, SpeedManagerAlgorithmProviderAdapter, org.gudy.azureus2.core3.util.AEDiagnosticsEvidenceGenerator

Fields Summary
protected static final int
UPDATE_PERIOD_MILLIS
private static final int
CONTACT_NUMBER
private static final int
CONTACT_PING_SECS
private static final int
LONG_PERIOD_SECS
private static final int
LONG_PERIOD_TICKS
private static final int
SHORT_ESTIMATE_SECS
private static final int
MEDIUM_ESTIMATE_SECS
static final int
SHORT_ESTIMATE_SAMPLES
static final int
MEDIUM_ESTIMATE_SAMPLES
private static final int
SAVE_PERIOD_SECS
private static final int
SAVE_PERIOD_TICKS
private static final int
SPEED_AVERAGE_PERIOD
private static boolean
DEBUG
public static final String
CONFIG_VERSION_STR
public static final String
CONFIG_VERSION
private static final String
CONFIG_AVAIL
private static final String
CONFIG_DEBUG
private static final String[]
CONFIG_PARAMS
private static boolean
emulated_ping_source
private com.aelitis.azureus.core.AzureusCore
core
private com.aelitis.azureus.core.dht.speed.DHTSpeedTester
speed_tester
private com.aelitis.azureus.core.speedmanager.SpeedManagerAdapter
adapter
private SpeedManagerAlgorithmProvider
provider
private int
provider_version
private boolean
enabled
private Map
contacts
private volatile int
total_contacts
private pingContact[]
contacts_array
private Object
original_limits
private org.gudy.azureus2.core3.util.AsyncDispatcher
dispatcher
private SpeedManagerPingMapperImpl
ping_mapper
private SpeedManagerPingMapperImpl[]
ping_mappers
private com.aelitis.azureus.core.util.CopyOnWriteList
transient_mappers
private org.gudy.azureus2.core3.util.AEDiagnosticsLogger
logger
private String
asn
private com.aelitis.azureus.core.util.CopyOnWriteList
listeners
Constructors Summary
public SpeedManagerImpl(com.aelitis.azureus.core.AzureusCore _core, com.aelitis.azureus.core.speedmanager.SpeedManagerAdapter _adapter)

	
	
	
					
			 
	
		core			= _core;
		adapter			= _adapter;

		AEDiagnostics.addEvidenceGenerator( this );
		
		logger = AEDiagnostics.getLogger( "SpeedMan" );
		
		ping_mapper	= new SpeedManagerPingMapperImpl( this, "Var", LONG_PERIOD_TICKS, true, false );

		if ( Constants.isCVSVersion()){
			
			SpeedManagerPingMapperImpl		pm2 	= new SpeedManagerPingMapperImpl( this, "Abs", LONG_PERIOD_TICKS, false, false );

			ping_mappers = new SpeedManagerPingMapperImpl[]{ pm2, ping_mapper };
			
		}else{
			
			ping_mappers = new SpeedManagerPingMapperImpl[]{ ping_mapper }; 
		}

		final File	config_dir = new File( SystemProperties.getUserPath(), "net" );
		
		if ( !config_dir.exists()){
			
			config_dir.mkdirs();
		}
		
		NetworkAdmin.getSingleton().addAndFirePropertyChangeListener(
			new NetworkAdminPropertyChangeListener()
			{
				public void
				propertyChanged(
					String		property )
				{
					if ( property == NetworkAdmin.PR_AS ){
						
					    NetworkAdminASN net_asn = NetworkAdmin.getSingleton().getCurrentASN();

						String	as = net_asn.getAS();
						
						if ( as.length() == 0 ){
							
							as = "default";
						}
						
						File history = new File( config_dir, "pm_" + FileUtil.convertOSSpecificChars( as ) + ".dat" );
						
						ping_mapper.loadHistory( history );
						
						asn = net_asn.getASName();
						
						if ( asn.length() == 0 ){
							
							asn = "Unknown";
						}
						
						informListeners( SpeedManagerListener.PR_ASN );
					}
				}
			});
		
		core.addLifecycleListener(
			new AzureusCoreLifecycleAdapter()
			{
				public void
				stopping(
					AzureusCore		core )
				{
					ping_mapper.saveHistory();
				}	
			});

		COConfigurationManager.addAndFireParameterListener( 
			CONFIG_VERSION,
			new ParameterListener()
			{
				public void
				parameterChanged(
					final String name )
				{
					dispatcher.dispatch(
						new AERunnable()
						{
							public void
							runSupport()
							{
								boolean	do_reset = provider_version == -1;
								
								int version = COConfigurationManager.getIntParameter( name );
								
								if ( version != provider_version ){
									
									provider_version = version;
									
									if ( isEnabled()){
										
										setEnabledSupport( false );
										
										setEnabledSupport( true );
									}
								}
								
								if ( do_reset ){
									
									enableOrAlgChanged();
								}
							}
						});
				}
			});
		
		COConfigurationManager.setParameter( CONFIG_AVAIL, false );
		
		SimpleTimer.addPeriodicEvent(
			"SpeedManager:timer",
			UPDATE_PERIOD_MILLIS,
			new TimerEventPerformer()
			{
				private int	tick_count;
				
				public void 
				perform(
					TimerEvent event ) 
				{
						// if enabled the ping stream drives the stats update for the ping mappers
						// When not enabled we do it here instead
					
					if ( !enabled || contacts_array.length == 0 ){
											
						int	x	= (adapter.getCurrentDataUploadSpeed(SPEED_AVERAGE_PERIOD) + adapter.getCurrentProtocolUploadSpeed(SPEED_AVERAGE_PERIOD));
						int	y 	= (adapter.getCurrentDataDownloadSpeed(SPEED_AVERAGE_PERIOD) + adapter.getCurrentProtocolDownloadSpeed(SPEED_AVERAGE_PERIOD));
						
						for (int i=0;i<ping_mappers.length;i++){
							
							ping_mappers[i].addSpeed( x, y );
						}
					}
					
					tick_count++;
					
					if ( tick_count % SAVE_PERIOD_TICKS == 0 ){
						
						ping_mapper.saveHistory();
					}
				}
			});
		
		emulated_ping_source	= false;
		
		if ( emulated_ping_source ){
			
			Debug.out( "Emulated ping source!!!!" );
		
			setSpeedTester( new TestPingSourceRandom( this ));
		}
	
Methods Summary
public voidaddListener(com.aelitis.azureus.core.speedmanager.SpeedManagerListener l)

		listeners.add( l );
	
protected voidaddPingHistory(int rtt, boolean re_base)

		int	x	= (adapter.getCurrentDataUploadSpeed(SPEED_AVERAGE_PERIOD) + adapter.getCurrentProtocolUploadSpeed(SPEED_AVERAGE_PERIOD));
		int	y 	= (adapter.getCurrentDataDownloadSpeed(SPEED_AVERAGE_PERIOD) + adapter.getCurrentProtocolDownloadSpeed(SPEED_AVERAGE_PERIOD));
		
		for (int i=0;i<ping_mappers.length;i++){
			
			ping_mappers[i].addPing( x, y, rtt, re_base );
		}
		
		Iterator it = transient_mappers.iterator();
		
		while( it.hasNext()){
			
			((SpeedManagerPingMapperImpl)it.next()).addPing( x, y, rtt, re_base );
		}
	
public com.aelitis.azureus.core.speedmanager.SpeedManagerPingMappercreateTransientPingMapper()

		SpeedManagerPingMapper res = new SpeedManagerPingMapperImpl( this, "Transient", LONG_PERIOD_TICKS, true, true );
		
		transient_mappers.add( res );
		
		if ( transient_mappers.size() > 32 ){
			
			Debug.out( "Transient mappers are growing too large" );
		}
		
		return( res );
	
protected voiddestroy(com.aelitis.azureus.core.speedmanager.SpeedManagerPingMapper mapper)

		transient_mappers.remove( mapper );
	
protected voidenableOrAlgChanged()

		total_contacts		= 0;
		
		SpeedManagerAlgorithmProvider	old_provider = provider;
		
		if ( provider_version == 1 ){
			
			if ( !( provider instanceof SpeedManagerAlgorithmProviderV1 )){
				
				provider = new SpeedManagerAlgorithmProviderV1( this );
			}
		}else if ( provider_version == 2 ){
			
			if ( !( provider instanceof SpeedManagerAlgorithmProviderV2 )){
				
				provider = new SpeedManagerAlgorithmProviderV2( this );
			}
            
        }else if ( provider_version == 3 ){

            provider = new SpeedManagerAlgorithmProviderV2( this );

        }else{
						
			Debug.out( "Unknown provider version " + provider_version );
			
			if ( !( provider instanceof nullProvider )){
				
				provider = new nullProvider();
			}
		}
		
		if ( old_provider != provider ){
		
			log( "Algorithm set to " + provider.getClass().getName());
		}
		
		if ( old_provider != null ){
			
			old_provider.destroy();
		}
		
		provider.reset();
	
public voidgenerate(org.gudy.azureus2.core3.util.IndentWriter writer)

		writer.println( "SpeedManager: enabled=" + enabled + ",provider=" + provider );
		
		try{
			writer.indent();
			
			ping_mapper.generateEvidence( writer );
			
		}finally{
			
			writer.exdent();
		}
	
public java.lang.StringgetASN()

		return( asn );
	
public com.aelitis.azureus.core.speedmanager.SpeedManagerPingMappergetActiveMapper()

		return( ping_mapper );
	
public intgetCurrentChokeSpeed()
Returns the current view of when choking occurs

return
speed in bytes/sec

		return( provider.getCurrentChokeSpeed());
	
public intgetCurrentDataDownloadSpeed()

        return( adapter.getCurrentDataDownloadSpeed(-1) );
    
public intgetCurrentDataUploadSpeed()

		return( adapter.getCurrentDataUploadSpeed(-1));
	
public intgetCurrentDownloadLimit()

		return( adapter.getCurrentDownloadLimit());
	
public intgetCurrentPingMillis()

		return( provider.getCurrentPingMillis());
	
public intgetCurrentProtocolDownloadSpeed()

        return( adapter.getCurrentProtocolDownloadSpeed(-1) );
    
public intgetCurrentProtocolUploadSpeed()

		return( adapter.getCurrentProtocolUploadSpeed(-1));
	
public intgetCurrentUploadLimit()

		return( adapter.getCurrentUploadLimit());
	
public com.aelitis.azureus.core.speedmanager.SpeedManagerLimitEstimategetEstimatedDownloadCapacityBytesPerSec()

		return( ping_mapper.getEstimatedDownloadCapacityBytesPerSec());	
	
public com.aelitis.azureus.core.speedmanager.SpeedManagerLimitEstimategetEstimatedUploadCapacityBytesPerSec()

		return( ping_mapper.getEstimatedUploadCapacityBytesPerSec());	
	
public intgetIdlePingMillis()

		return( provider.getIdlePingMillis());
	
public com.aelitis.azureus.core.speedmanager.SpeedManagerPingMapper[]getMappers()

		return( ping_mappers );
	
public intgetMaxPingMillis()

		return( provider.getMaxPingMillis());
	
public intgetMaxUploadSpeed()

		return( provider.getMaxUploadSpeed());
	
public com.aelitis.azureus.core.speedmanager.SpeedManagerPingMappergetPingMapper()

		return( getActiveMapper());
	
public com.aelitis.azureus.core.speedmanager.SpeedManagerPingSource[]getPingSources()

		return( contacts_array );
	
public com.aelitis.azureus.core.speedmanager.SpeedManagergetSpeedManager()

		return( this );
	
public com.aelitis.azureus.core.dht.speed.DHTSpeedTestergetSpeedTester()

		return( speed_tester );
	
protected voidinformDownCapChanged()

		informListeners( SpeedManagerListener.PR_DOWN_CAPACITY );
	
protected voidinformListeners(int type)

		Iterator	it = listeners.iterator();
		
		while( it.hasNext()){
			
			try{
				((SpeedManagerListener)it.next()).propertyChanged( type );
				
			}catch( Throwable e ){
				
				Debug.printStackTrace(e);
			}
		}
	
protected voidinformUpCapChanged()

		informListeners( SpeedManagerListener.PR_UP_CAPACITY );
	
public booleanisAvailable()

		return( speed_tester != null );
	
public booleanisEnabled()

		return( enabled );
	
public voidlog(java.lang.String str)

		logger.log( str );
	
public voidremoveListener(com.aelitis.azureus.core.speedmanager.SpeedManagerListener l)

		listeners.remove( l );
	
public voidreset()

		ping_mapper.reset();
	
public voidsetCurrentDownloadLimit(int bytes_per_second)

		if ( enabled ){

			adapter.setCurrentDownloadLimit( bytes_per_second );
		}
	
public voidsetCurrentUploadLimit(int bytes_per_second)

		if ( enabled ){
			
			adapter.setCurrentUploadLimit( bytes_per_second );
		}
	
public voidsetEnabled(boolean _enabled)

			// unfortunately we need this to run synchronously as the caller may be disabling it
			// and then setting speed limits in which case we can't go async and restore the
			// original values below and overwrite the new limit...
		
		final AESemaphore	sem = new AESemaphore( "SpeedManagerImpl.setEnabled" );
		
			// single thread enable/disable (and derivative reset) ops
		
		dispatcher.dispatch(
			new AERunnable()
			{
				public void
				runSupport()
				{
					try{
						setEnabledSupport( _enabled );
						
					}finally{
						
						sem.release();
					}
				}
			});
		
		if ( !sem.reserve( 10000 )){
			
			Debug.out( "operation didn't complete in time" );
		}
	
protected voidsetEnabledSupport(boolean _enabled)

		if ( enabled != _enabled ){
			
			log( "Enabled set to " + _enabled );
			
			if ( _enabled ){
				
				original_limits	= adapter.getLimits();
				
			}else{
				
				ping_mapper.saveHistory();
			}
			
			enableOrAlgChanged();
			
			enabled	= _enabled;
			
			if ( speed_tester != null ){
				
				speed_tester.setContactNumber( enabled?CONTACT_NUMBER:0);
			}
			
			if ( !enabled ){
									
				adapter.setLimits( original_limits, true, provider.getAdjustsDownloadLimits());
			}
		}
	
public voidsetEstimatedDownloadCapacityBytesPerSec(int bytes_per_sec, float metric)

		ping_mapper.setEstimatedDownloadCapacityBytesPerSec( bytes_per_sec, metric );	
	
public voidsetEstimatedUploadCapacityBytesPerSec(int bytes_per_sec, float metric)

		ping_mapper.setEstimatedUploadCapacityBytesPerSec( bytes_per_sec, metric );	
	
public voidsetLoggingEnabled(boolean enabled)

		COConfigurationManager.setParameter( CONFIG_DEBUG, enabled );
	
public voidsetSpeedTester(com.aelitis.azureus.core.dht.speed.DHTSpeedTester _tester)

		if ( speed_tester != null ){
			
			if ( !emulated_ping_source ){
			
				Debug.out( "speed tester already set!" );
			}
			
			return;
		}
		
		COConfigurationManager.setParameter( CONFIG_AVAIL, true );

		speed_tester	= _tester; 
				
		speed_tester.addListener(
				new DHTSpeedTesterListener()
				{
					private DHTSpeedTesterContact[]	last_contact_group = new DHTSpeedTesterContact[0];
										
					public void 
					contactAdded(
						DHTSpeedTesterContact contact )
					{
						if ( core.getInstanceManager().isLANAddress(contact.getAddress().getAddress())){
							
							contact.destroy();
							
						}else{
							log( "activePing: " + contact.getString());
							
							contact.setPingPeriod( CONTACT_PING_SECS );
							
							synchronized( contacts ){
								
								pingContact	source = new pingContact( contact );
								
								contacts.put( contact, source );
								
								contacts_array = new pingContact[ contacts.size() ];
								
								contacts.values().toArray( contacts_array );
							
								total_contacts++;
							
								provider.pingSourceFound( source, total_contacts > CONTACT_NUMBER );
							}
							
							contact.addListener(
								new DHTSpeedTesterContactListener()
								{
									public void
									ping(
										DHTSpeedTesterContact	contact,
										int						round_trip_time )
									{
									}
									
									public void
									pingFailed(
										DHTSpeedTesterContact	contact )
									{
									}
									
									public void
									contactDied(
										DHTSpeedTesterContact	contact )
									{
										log( "deadPing: " + contact.getString());
										
										synchronized( contacts ){
											
											pingContact source = (pingContact)contacts.remove( contact );
											
											if ( source != null ){
												
												contacts_array = new pingContact[ contacts.size() ];
												
												contacts.values().toArray( contacts_array );
												
												provider.pingSourceFailed( source );
											}
										}
									}
								});
						}
					}
					
					public void
					resultGroup(
						DHTSpeedTesterContact[]	st_contacts,
						int[]					round_trip_times )
					{
						if ( !enabled ){
							
							for (int i=0;i<st_contacts.length;i++){
								
								st_contacts[i].destroy();
							}
							
							return;
						}
						
						boolean	sources_changed = false;
						
						for (int i=0;i<st_contacts.length;i++){
							
							boolean	found = false;
							
							for (int j=0;j<last_contact_group.length;j++){
							
								if ( st_contacts[i] == last_contact_group[j] ){
									
									found = true;
									
									break;
								}
							}
							
							if ( !found ){
									
								sources_changed = true;
								
								break;
							}
						}
						
						last_contact_group = st_contacts;
						
						pingContact[]	sources = new pingContact[st_contacts.length];
					
						boolean	miss = false;
						
						int	worst_value	= -1;
						
						int	num_values	= 0;
						int	total		= 0;
						
						synchronized( contacts ){

							for (int i=0;i<st_contacts.length;i++){
								
								pingContact source = sources[i] = (pingContact)contacts.get( st_contacts[i] );
								
								if ( source != null ){
									
									int	rtt = round_trip_times[i];
									
									if ( rtt > 0 ){
										
										if ( rtt > worst_value ){
											
											worst_value = rtt;
										}
										
										num_values++;
										
										total += rtt;
									}
									
									source.setPingTime( rtt );
									
								}else{
									
									miss = true;
								}
							}
						}
						
						if ( miss ){
							
							Debug.out( "Auto-speed: source missing" );
							
						}else{
						
							provider.calculate( sources );
							
								// remove worst value if we have > 1
							
							if ( num_values > 1 ){
								
								total -= worst_value;
								num_values--;
							}

							if ( num_values> 0 ){
								
								addPingHistory( total/num_values, sources_changed );
							}
						}
					}
				});
		
		SimpleTimer.addPeriodicEvent(
			"SpeedManager:stats",
			SpeedManagerAlgorithmProvider.UPDATE_PERIOD_MILLIS,
			new TimerEventPerformer()
			{
				public void
				perform(
					TimerEvent	event )
				{
					if ( enabled ){
					
						provider.updateStats();
					}
				}
			});