/*
* Created on Oct 7, 2004
* Created by Alon Rohter
* Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/
package com.aelitis.azureus.core.networkmanager.impl;
import java.util.*;
import org.gudy.azureus2.core3.util.AEMonitor;
import org.gudy.azureus2.core3.util.Debug;
import com.aelitis.azureus.core.networkmanager.LimitedRateGroup;
import com.aelitis.azureus.core.networkmanager.NetworkConnectionBase;
import com.aelitis.azureus.core.networkmanager.NetworkManager;
import com.aelitis.azureus.core.networkmanager.RateHandler;
/**
*
*/
public class TransferProcessor {
private static final boolean RATE_LIMIT_LAN_TOO = false;
static{
if ( RATE_LIMIT_LAN_TOO ){
System.err.println( "**** TransferProcessor: RATE_LIMIT_LAN_TOO enabled ****" );
}
}
public static final int TYPE_UPLOAD = 0;
public static final int TYPE_DOWNLOAD = 1;
private final LimitedRateGroup max_rate;
private final RateHandler main_rate_handler;
private final ByteBucket main_bucket;
private final EntityHandler main_controller;
private final HashMap<LimitedRateGroup,GroupData> group_buckets = new HashMap<LimitedRateGroup,GroupData>();
private final HashMap<NetworkConnectionBase,ConnectionData> connections = new HashMap<NetworkConnectionBase,ConnectionData>();
private final AEMonitor connections_mon;
private final boolean multi_threaded;
/**
* Create new transfer processor for the given read/write type, limited to the given max rate.
* @param processor_type read or write processor
* @param max_rate_limit to use
*/
public TransferProcessor( int processor_type, LimitedRateGroup max_rate_limit, boolean multi_threaded ) {
this.max_rate = max_rate_limit;
this.multi_threaded = multi_threaded;
connections_mon = new AEMonitor( "TransferProcessor:" +processor_type );
main_bucket = createBucket( max_rate.getRateLimitBytesPerSecond() );
main_rate_handler =
new RateHandler() {
public int getCurrentNumBytesAllowed() {
if( main_bucket.getRate() != max_rate.getRateLimitBytesPerSecond() ) { //sync rate
main_bucket.setRate( max_rate.getRateLimitBytesPerSecond() );
}
return main_bucket.getAvailableByteCount();
}
public void bytesProcessed( int num_bytes_written ) {
main_bucket.setBytesUsed( num_bytes_written );
max_rate.updateBytesUsed( num_bytes_written );
}
};
main_controller = new EntityHandler( processor_type, main_rate_handler );
}
/**
* Register peer connection for upload handling.
* NOTE: The given max rate limit is ignored until the connection is upgraded.
* @param connection to register
* @param group rate limit group
*/
public void registerPeerConnection( NetworkConnectionBase connection, boolean upload ) {
final ConnectionData conn_data = new ConnectionData();
try { connections_mon.enter();
LimitedRateGroup[] groups = connection.getRateLimiters( upload );
//do group registration
GroupData[] group_datas = new GroupData[groups.length];
for (int i=0;i<groups.length;i++){
LimitedRateGroup group = groups[i];
// boolean log = group.getName().contains("parg");
GroupData group_data = (GroupData)group_buckets.get( group );
if( group_data == null ) {
int limit = NetworkManagerUtilities.getGroupRateLimit( group );
group_data = new GroupData( createBucket( limit ) );
group_buckets.put( group, group_data );
/*
if ( log ){
System.out.println( "Creating RL1: " + group.getName() + " -> " + group_data );
}
*/
}
group_data.group_size++;
group_datas[i] = group_data;
/*
if ( log ){
System.out.println( "Applying RL1: " + group.getName() + " -> " + connection );
}
*/
}
conn_data.groups = groups;
conn_data.group_datas = group_datas;
conn_data.state = ConnectionData.STATE_NORMAL;
connections.put( connection, conn_data );
}
finally { connections_mon.exit(); }
main_controller.registerPeerConnection( connection );
}
public List<NetworkConnectionBase>
getConnections()
{
try{
connections_mon.enter();
return( new ArrayList<NetworkConnectionBase>( connections.keySet()));
}finally{
connections_mon.exit();
}
}
public boolean isRegistered( NetworkConnectionBase connection ){
try{ connections_mon.enter();
return( connections.containsKey( connection ));
}
finally{ connections_mon.exit(); }
}
/**
* Cancel upload handling for the given peer connection.
* @param connection to cancel
*/
public void deregisterPeerConnection( NetworkConnectionBase connection ) {
try{ connections_mon.enter();
ConnectionData conn_data = (ConnectionData)connections.remove( connection );
if( conn_data != null ) {
GroupData[] group_datas = conn_data.group_datas;
//do groups de-registration
for (int i=0;i<group_datas.length;i++){
GroupData group_data = group_datas[i];
if( group_data.group_size == 1 ) { //last of the group
group_buckets.remove( conn_data.groups[i] ); //so remove
}else {
group_data.group_size--;
}
}
}
}
finally{ connections_mon.exit(); }
main_controller.cancelPeerConnection( connection );
}
public void
setRateLimiterFreezeState(
boolean frozen )
{
main_bucket.setFrozen( frozen );
}
public void
addRateLimiter(
NetworkConnectionBase connection,
LimitedRateGroup group )
{
try{
connections_mon.enter();
ConnectionData conn_data = (ConnectionData)connections.get( connection );
if ( conn_data != null ){
LimitedRateGroup[] groups = conn_data.groups;
for (int i=0;i<groups.length;i++){
if ( groups[i] == group ){
return;
}
}
// boolean log = group.getName().contains("parg");
GroupData group_data = (GroupData)group_buckets.get( group );
if ( group_data == null ){
int limit = NetworkManagerUtilities.getGroupRateLimit( group );
group_data = new GroupData( createBucket( limit ) );
/*
if ( log ){
System.out.println( "Creating RL2: " + group.getName() + " -> " + group_data );
}
*/
group_buckets.put( group, group_data );
}
/*
if ( log ){
System.out.println( "Applying RL2: " + group.getName() + " -> " + connection );
}
*/
group_data.group_size++;
GroupData[] group_datas = conn_data.group_datas;
int len = groups.length;
LimitedRateGroup[] new_groups = new LimitedRateGroup[ len + 1 ];
System.arraycopy( groups, 0, new_groups, 0, len );
new_groups[len] = group;
conn_data.groups = new_groups;
GroupData[] new_group_datas = new GroupData[ len + 1 ];
System.arraycopy( group_datas, 0, new_group_datas, 0, len );
new_group_datas[len] = group_data;
conn_data.group_datas = new_group_datas;
}
}finally{
connections_mon.exit();
}
}
public void
removeRateLimiter(
NetworkConnectionBase connection,
LimitedRateGroup group )
{
try{
connections_mon.enter();
ConnectionData conn_data = (ConnectionData)connections.get( connection );
if ( conn_data != null ){
LimitedRateGroup[] groups = conn_data.groups;
GroupData[] group_datas = conn_data.group_datas;
int len = groups.length;
if ( len == 0 ){
return;
}
LimitedRateGroup[] new_groups = new LimitedRateGroup[ len - 1 ];
GroupData[] new_group_datas = new GroupData[ len - 1 ];
int pos = 0;
for (int i=0;i<groups.length;i++){
if ( groups[i] == group ){
GroupData group_data = conn_data.group_datas[i];
if ( group_data.group_size == 1 ){ //last of the group
group_buckets.remove( conn_data.groups[i] ); //so remove
}else {
group_data.group_size--;
}
}else{
if ( pos == new_groups.length ){
return;
}
new_groups[pos] = groups[i];
new_group_datas[pos] = group_datas[i];
pos++;
}
}
conn_data.groups = new_groups;
conn_data.group_datas = new_group_datas;
}
}finally{
connections_mon.exit();
}
}
// private static long last_log = 0;
/**
* Upgrade the given connection to a high-speed transfer handler.
* @param connection to upgrade
*/
public void upgradePeerConnection( final NetworkConnectionBase connection, int partition_id ) {
ConnectionData connection_data = null;
try{ connections_mon.enter();
connection_data = (ConnectionData)connections.get( connection );
}
finally{ connections_mon.exit(); }
if( connection_data != null && connection_data.state == ConnectionData.STATE_NORMAL ) {
final ConnectionData conn_data = connection_data;
main_controller.upgradePeerConnection( connection, new RateHandler() {
public int getCurrentNumBytesAllowed() {
// sync global rate
if( main_bucket.getRate() != max_rate.getRateLimitBytesPerSecond() ) {
main_bucket.setRate( max_rate.getRateLimitBytesPerSecond() );
}
int allowed = main_bucket.getAvailableByteCount();
// reserve bandwidth for the general pool
allowed -= connection.getMssSize();
if ( allowed < 0 )allowed = 0;
// only apply group rates to non-lan local connections
if ( RATE_LIMIT_LAN_TOO || !( connection.isLANLocal() && NetworkManager.isLANRateEnabled())){
// sync group rates
try{
for (int i=0;i<conn_data.group_datas.length;i++){
LimitedRateGroup group = conn_data.groups[i];
//boolean log = group.getName().contains("parg");
int group_rate = NetworkManagerUtilities.getGroupRateLimit( conn_data.groups[i] );
ByteBucket group_bucket = conn_data.group_datas[i].bucket;
/*
if ( log ){
long now = SystemTime.getCurrentTime();
if ( now - last_log > 500 ){
last_log = now;
System.out.println( " " + group.getName() + " -> " + group_rate + "/" + group_bucket.getAvailableByteCount());
}
}
*/
if ( group_bucket.getRate() != group_rate ){
group_bucket.setRate( group_rate );
}
int group_allowed = group_bucket.getAvailableByteCount();
if ( group_allowed < allowed ){
allowed = group_allowed;
}
}
}catch( Throwable e ){
// conn_data.group stuff is not synchronized for speed but can cause borkage if new
// limiters added so trap here
if (!( e instanceof IndexOutOfBoundsException )){
Debug.printStackTrace(e);
}
}
}
return allowed;
}
public void bytesProcessed( int num_bytes_written ) {
if ( RATE_LIMIT_LAN_TOO || !( connection.isLANLocal() && NetworkManager.isLANRateEnabled())){
for (int i=0;i<conn_data.group_datas.length;i++){
conn_data.group_datas[i].bucket.setBytesUsed( num_bytes_written );
conn_data.groups[i].updateBytesUsed( num_bytes_written );
}
}
main_bucket.setBytesUsed( num_bytes_written );
}
}, partition_id );
conn_data.state = ConnectionData.STATE_UPGRADED;
}
}
/**
* Downgrade the given connection back to a normal-speed transfer handler.
* @param connection to downgrade
*/
public void downgradePeerConnection( NetworkConnectionBase connection ) {
ConnectionData conn_data = null;
try{ connections_mon.enter();
conn_data = (ConnectionData)connections.get( connection );
}
finally{ connections_mon.exit(); }
if( conn_data != null && conn_data.state == ConnectionData.STATE_UPGRADED ) {
main_controller.downgradePeerConnection( connection );
conn_data.state = ConnectionData.STATE_NORMAL;
}
}
public RateHandler
getRateHandler()
{
return( main_rate_handler );
}
public RateHandler
getRateHandler(
NetworkConnectionBase connection )
{
return( main_controller.getRateHandler( connection ));
}
private ByteBucket
createBucket(
int bytes_per_sec )
{
if ( multi_threaded ){
return( new ByteBucketMT( bytes_per_sec ));
}else{
return( new ByteBucketST( bytes_per_sec ));
}
}
private static class ConnectionData {
private static final int STATE_NORMAL = 0;
private static final int STATE_UPGRADED = 1;
private int state;
private LimitedRateGroup[] groups;
private GroupData[] group_datas;
}
private static class GroupData {
private final ByteBucket bucket;
private int group_size = 0;
private GroupData( ByteBucket bucket ) {
this.bucket = bucket;
}
}
}