*/
private final int getRequestCandidate(final PEPeerTransport pt )
{
if (pt ==null ||pt.getPeerState() !=PEPeer.TRANSFERING)
return -1;
final BitFlags peerHavePieces =pt.getAvailable();
if (peerHavePieces ==null ||peerHavePieces.nbSet <=0)
return -1;
// piece number and its block number that we'll try to DL
int[] reservedPieceNumbers = pt.getReservedPieceNumbers();
// If there's a piece seserved to this peer resume it and only it (if possible)
if ( reservedPieceNumbers != null ){
for ( int reservedPieceNumber: reservedPieceNumbers ){
PEPiece pePiece = pePieces[reservedPieceNumber];
if ( pePiece != null ){
String peerReserved = pePiece.getReservedBy();
if ( peerReserved != null && peerReserved.equals( pt.getIp())){
if ( peerHavePieces.flags[reservedPieceNumber] &&pePiece.isRequestable()){
return reservedPieceNumber;
}else{
pePiece.setReservedBy( null );
}
}
}
// reserved piece is no longer valid, dump it
pt.removeReservedPieceNumber( reservedPieceNumber );
}
// note, pieces reserved to peers that get disconnected are released in pepeercontrol
}
int reservedPieceNumber = -1;
final int peerSpeed =(int) pt.getStats().getDataReceiveRate() /1024; // how many KB/s has the peer has been sending
final int lastPiece =pt.getLastPiece();
//final boolean rarestOverride = calcRarestAllowed() > 0;
final int nbSnubbed =peerControl.getNbPeersSnubbed();
long resumeMinAvail =Long.MAX_VALUE;
int resumeMaxPriority =Integer.MIN_VALUE;
boolean resumeIsRarest = false; // can the peer continuea piece with lowest avail of all pieces we want
int secondChoiceResume = -1;
BitFlags startCandidates =null;
int startMaxPriority =Integer.MIN_VALUE;
int startMinAvail =Integer.MAX_VALUE;
boolean startIsRarest =false;
boolean forceStart=false;
int priority; // aggregate priority of piece under inspection (start priority or resume priority for pieces to be resumed)
int avail =0; // the swarm-wide availability level of the piece under inspection
long pieceAge; // how long since the PEPiece first started downloading (requesting, actually)
final boolean rarestAllowed = calcRarestAllowed() > 0;
final int startI =peerHavePieces.start;
final int endI =peerHavePieces.end;
int i;
final int[] peerPriorities = pt.getPriorityOffsets();
final long now =SystemTime.getCurrentTime();
int[] request_hint = pt.getRequestHint();
int request_hint_piece_number;
if ( request_hint != null ){
request_hint_piece_number = request_hint[0];
if ( dmPieces[request_hint_piece_number].isDone()){
pt.clearRequestHint();
request_hint_piece_number = -1;
}
}else{
request_hint_piece_number = -1;
}
// Try to continue a piece already loaded, according to priority
for (i =startI; i <=endI; i++){
// is the piece available from this peer?
if ( peerHavePieces.flags[i]){
priority = startPriorities[i];
final DiskManagerPiece dmPiece = dmPieces[i];
if ( priority >=0 && dmPiece.isDownloadable()){
if ( peerPriorities != null ){
int peer_priority = peerPriorities[i];
if ( peer_priority < 0 ){
continue;
}
priority += peer_priority;
}
if ( enable_request_hints && i == request_hint_piece_number ){
priority += PRIORITY_REQUEST_HINT;
PEPiece pePiece = pePieces[i];
if ( pePiece == null ){
forceStart = true;
}else{
pePiece.setReservedBy( pt.getIp());
pt.addReservedPieceNumber( i );
}
}
final PEPiece pePiece = pePieces[i];
// if we are considering realtime pieces then don't bother with non-realtime ones
if ( pePiece == null || pePiece.isRequestable())
{
// if this priority exceeds the priority-override threshold then we override rarity
boolean pieceRarestOverride = priority>=PRIORITY_OVERRIDES_RAREST?true:rarestAllowed;
// piece is: Needed, not fully: Requested, Downloaded, Written, hash-Checking or Done
avail =availability[i];
if (avail ==0)
{ // maybe we didn't know we could get it before
availability[i] =1; // but the peer says s/he has it
avail =1;
}
// is the piece active
if (pePiece !=null)
{
if ( priority != startPriorities[i])
pePiece.setResumePriority( priority ); // maintained for display purposes only
boolean startedRarest = rarestStartedPieces.contains(pePiece);
boolean rarestPrio = avail <=globalMinOthers && ( startedRarest || rarestAllowed);
// How many requests can still be made on this piece?
final int freeReqs =pePiece.getNbUnrequested();
if (freeReqs <=0)
{
pePiece.setRequested();
continue;
}
// Don't touch pieces reserved for others
final String peerReserved =pePiece.getReservedBy();
if (peerReserved !=null)
{
if (!peerReserved.equals(pt.getIp()))
continue; //reserved to somebody else
// the peer forgot this is reserved to him; re-associate it
pt.addReservedPieceNumber(i);
return i;
}
int pieceSpeed =pePiece.getSpeed();
// ### Piece/Peer speed checks
boolean mayResume = true;
if(pt.isSnubbed())
{
// snubbed peers shouldn't stall fast pieces under ANY condition
// may lead to trouble when the snubbed peer is the only seed, needs further testing
mayResume &= pieceSpeed < 1;
mayResume &= freeReqs > 2 || avail <= nbSnubbed;
} else
{
// slower peers are allowed as long as there is enough free room
mayResume &= freeReqs*peerSpeed >= pieceSpeed/2; //|| rarestPrio;
// prevent non-subbed peers from resuming on snubbed-peer-pieces but still allow them to resume stalled pieces
mayResume &= peerSpeed < 2 || pieceSpeed > 0 || pePiece.getNbRequests() == 0;
mayResume |= i == pt.getLastPiece();
}
// find a fallback piece in case the peer could theoretically contribute
// to an existing piece but is prevented by the snubbing rules etc.
// this will prevent unecessary piece starting
if(secondChoiceResume == -1 || avail > availability[secondChoiceResume])
secondChoiceResume = i;
if(!mayResume)
continue;
if (avail > resumeMinAvail)
continue;
priority +=pieceSpeed;
priority +=(i ==lastPiece) ?PRIORITY_W_SAME_PIECE :0;
// Adjust priority for purpose of continuing pieces
// how long since last written to (if written to)
priority +=pePiece.getTimeSinceLastActivity() /PRIORITY_DW_STALE;
// how long since piece was started
pieceAge =now -pePiece.getCreationTime();
if (pieceAge >0)
priority +=PRIORITY_W_AGE *pieceAge /(PRIORITY_DW_AGE *dmPiece.getNbBlocks());
// how much is already written to disk
priority +=(PRIORITY_W_PIECE_DONE *dmPiece.getNbWritten()) /dmPiece.getNbBlocks();
pePiece.setResumePriority(priority); // this is only for display
if (avail < resumeMinAvail || (avail == resumeMinAvail && priority > resumeMaxPriority))
{ // this piece seems like best choice for resuming
// Verify it's still possible to get a block to request from this piece
if (pePiece.hasUnrequestedBlock())
{ // change the different variables to reflect interest in this block
reservedPieceNumber = i;
resumeMinAvail =avail;
resumeMaxPriority = priority;
resumeMinAvail = avail;
resumeIsRarest = rarestPrio;
}
}
} else if (avail <=globalMinOthers && rarestAllowed)
{ // rarest pieces only from now on
if (!startIsRarest)
{ // 1st rarest piece
if (startCandidates ==null)
startCandidates =new BitFlags(nbPieces);
startMaxPriority =priority;
startMinAvail =avail;
startIsRarest =avail <=globalMinOthers;
startCandidates.setOnly(i); // clear the non-rarest bits in favor of only rarest
} else if (priority >startMaxPriority)
{ // continuing rarest, higher priority level
if (startCandidates ==null)
startCandidates =new BitFlags(nbPieces);
startMaxPriority =priority;
startCandidates.setOnly(i);
} else if (priority ==startMaxPriority)
{ // continuing rares, same priority level
startCandidates.setEnd(i);
}
} else if (!startIsRarest ||!rarestAllowed)
{ // not doing rarest pieces
if (priority >startMaxPriority)
{ // new priority level
if (startCandidates ==null)
startCandidates =new BitFlags(nbPieces);
startMaxPriority =priority;
startMinAvail =avail;
startIsRarest =avail <=globalMinOthers;
startCandidates.setOnly(i);
} else if (priority ==startMaxPriority)
{ // continuing same priority level
if (startCandidates ==null)
startCandidates =new BitFlags(nbPieces);
if (avail <startMinAvail)
{ // same priority, new availability level
startMinAvail =avail;
startIsRarest =avail <=globalMinOthers;
startCandidates.setOnly(i);
} else if (avail ==startMinAvail)
{ // same priority level, same availability level
startCandidates.setEnd(i);
}
}
}
}
}