/* * Created on Jul 15, 2006 * Created by Alon Rohter * Copyright (C) 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, SARL au capital de 30,000 euros * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. * */ package com.aelitis.azureus.core.peermanager.uploadslots; import java.util.*; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.core3.util.SystemTime; import com.aelitis.azureus.core.peermanager.control.*; /** * */ public class UploadSlotManager { private static final int EXPIRE_NORMAL = 1; //1 round = 10sec upload private static final int EXPIRE_OPTIMISTIC = 3; //3 rounds = 30sec upload private static final int EXPIRE_SEED = 6; //6 rounds = 60sec upload // change this to true and you'll have to fix the scheduleing code below public static final boolean AUTO_SLOT_ENABLE = false; //TODO private long last_process_time; private static final UploadSlotManager instance = new UploadSlotManager(); public static UploadSlotManager getSingleton() { return instance; } private final UploadSessionPicker picker = new UploadSessionPicker(); //init with empty slots, optimistic first in line private final UploadSlot[] slots = new UploadSlot[] { new UploadSlot( UploadSlot.TYPE_OPTIMISTIC ), //TODO dynamic # of slots new UploadSlot( UploadSlot.TYPE_NORMAL ), new UploadSlot( UploadSlot.TYPE_NORMAL ), new UploadSlot( UploadSlot.TYPE_NORMAL ) }; private long current_round = 0; private UploadSlotManager() { if( AUTO_SLOT_ENABLE ) { System.out.println( "UPLOAD_SLOT_MANAGER SCHEDULAR STARTED" ); /* removed pending rework PeerControlSchedulerFactory.getSingleton().register( new PeerControlInstance() { public void schedule() { long now = SystemTime.getCurrentTime(); if( now - last_process_time >= 10000 ) { //10sec process loop process(); last_process_time = now; } else if( last_process_time > now ) { Debug.out( "OOPS, time went backwards!" ); last_process_time = now; } } public int getSchedulePriority() { return 0; } }); */ } else { //System.out.println( "AUTO UPLOAD SLOT *DISABLED*" ); } } public void registerHelper( UploadHelper helper ) { if( AUTO_SLOT_ENABLE ) { picker.registerHelper( helper ); } } public void deregisterHelper( UploadHelper helper ) { if( AUTO_SLOT_ENABLE ) { picker.deregisterHelper( helper ); } } /** * Notify of helper state change (i.e. priority changed) * @param helper */ public void updateHelper( UploadHelper helper ) { if( AUTO_SLOT_ENABLE ) { picker.updateHelper( helper ); } } private void process() { if( !AUTO_SLOT_ENABLE ) return; current_round++; ArrayList to_stop = new ArrayList(); //get a list of the best sessions, peers who are uploading to us in download mode LinkedList best_sessions = picker.pickBestDownloadSessions( slots.length ); int best_size = best_sessions.size(); //go through all currently expired slots and pick sessions for next round for( int i=0; i < slots.length; i++ ) { UploadSlot slot = slots[i]; if( slot.getExpireRound() <= current_round ) { //expired UploadSession session = slot.getSession(); if( session != null ) { to_stop.add( session ); //make sure it gets stopped slot.setSession( null ); //clear slot } if( slot.getSlotType() == UploadSlot.TYPE_OPTIMISTIC ) { //optimistic //pick new session for optimistic upload session = pickOptSession(); if( session == null ) { continue; } if( session.getSessionType() == UploadSession.TYPE_SEED ) { //place first seed session in a normal slot best_sessions.addFirst( session ); //put at front of good list to ensure it gets picked //pick a new optimistic session, whatever type session = pickOptSession(); if( session == null ) continue; } slot.setSession( session ); //place the new session in the slot slot.setExpireRound( current_round + EXPIRE_OPTIMISTIC ); //set the new expire time } else { //normal session = getNextBestSession( best_sessions ); //get the next "best" session if( session == null && best_size == slots.length ) { Debug.out( "session == null && best_size == slots.length" ); } if( session == null ) { //no download mode peers, must be only seeding; or all best are already slotted session = pickOptSession(); //just pick the next optimistic if( session == null ) continue; //no optimistic either } slot.setSession( session ); //place the session in the slot slot.setExpireRound( current_round + ( session.getSessionType() == UploadSession.TYPE_SEED ? EXPIRE_SEED : EXPIRE_NORMAL ) ); //set the new expire time } } } //start and stop sessions for the round //filter out sessions allowed to continue another round, so we don't stop-start them for( Iterator it = to_stop.iterator(); it.hasNext(); ) { UploadSession stop_s = (UploadSession)it.next(); for( int i=0; i < slots.length; i++ ) { if( stop_s.isSameSession( slots[i].getSession() ) ) { //need to do this because two session objects can represent the same peer it.remove(); break; } } } //stop discontinued sessions for( Iterator it = to_stop.iterator(); it.hasNext(); ) { UploadSession session = (UploadSession)it.next(); session.stop(); } //ensure sessions are started for( int i=0; i < slots.length; i++ ) { UploadSession s = slots[i].getSession(); if( s != null ) s.start(); } printSlotStats(); } int count = 0; private UploadSession getNextBestSession( LinkedList best ) { count++; System.out.print( "getNextBestSession [" +count+"] best.size=" +best.size()+ " " ); if( !best.isEmpty() ) { UploadSession session = (UploadSession)best.removeFirst(); //get next if( !isAlreadySlotted( session ) ) { //found an unslotted session System.out.println( "OK found session [" +session.getStatsTrace()+ "]" ); return session; } System.out.println( "FAIL already-slotted session [" +session.getStatsTrace()+ "]" ); return getNextBestSession( best ); //oops, already been slotted, try again } return null; } private UploadSession pickOptSession() { int max = picker.getHelperCount(); //max number of sessions the picker will return before it loops back 'round for( int i=0; i < max; i++ ) { //make sure we don't loop UploadSession session = picker.pickNextOptimisticSession(); if( session != null && !isAlreadySlotted( session ) ) { return session; //found! } } return null; //no optimistic sessions } private boolean isAlreadySlotted( UploadSession session ) { for( int i=0; i < slots.length; i++ ) { UploadSession s = slots[i].getSession(); if( s != null && s.isSameSession( session ) ) return true; } return false; } private void printSlotStats() { System.out.println( "\nUPLOAD SLOTS [" +current_round+ "x]:" ); for( int i=0; i < slots.length; i++ ) { UploadSlot slot = slots[i]; System.out.print( "[" +i+ "]: " ); String slot_type = slot.getSlotType() == UploadSlot.TYPE_NORMAL ? "NORM" : "OPTI"; long rem = slot.getExpireRound() - current_round; String remaining = rem < 0 ? "" : " [" +rem+ "]rr"; String ses_trace = slot.getSession() == null ? "EMPTY" : slot.getSession().getStatsTrace(); System.out.println( slot_type + remaining+ " : " +ses_trace ); } } }