private void ExecuteReview()
{
//Review current choke/unchoke position and adjust as necessary
//Start by populating the lists of peers, then allocate available slots oberving the unchoke limit
//Clear the lists to start with
_nascentPeers.Clear();
_candidatePeers.Clear();
_optimisticUnchokeCandidates.Clear();
//No review needed or disabled by the torrent settings
/////???Remove when working
////Log peer status - temporary
//if (isLogging)
//{
// StringBuilder logEntry = new StringBuilder(1000);
// logEntry.Append(B2YN(owningTorrent.State == TorrentState.Seeding) + timeOfLastReview.ToString() + "," + DateTime.Now.ToString() + ";");
// foreach (PeerIdInternal connectedPeer in owningTorrent.Peers.ConnectedPeers)
// {
// if (connectedPeer.Connection != null)
// if (!connectedPeer.Peer.IsSeeder)
// {
// {
// logEntry.Append(
// B2YN(connectedPeer.Peer.IsSeeder) +
// B2YN(connectedPeer.AmChoking) +
// B2YN(connectedPeer.AmInterested) +
// B2YN(connectedPeer.IsInterested) +
// B2YN(connectedPeer.Peer.FirstReviewPeriod) +
// connectedPeer.Connection.Monitor.DataBytesDownloaded.ToString() + "," +
// connectedPeer.Peer.BytesDownloadedAtLastReview.ToString() + "," +
// connectedPeer.Connection.Monitor.DataBytesUploaded.ToString() + "," +
// connectedPeer.Peer.BytesUploadedAtLastReview.ToString() + "," +
// connectedPeer.Peer.Location);
// DateTime? lastUnchoked = connectedPeer.Peer.LastUnchoked;
// if (lastUnchoked.HasValue)
// logEntry.Append(
// "," +
// lastUnchoked.ToString() + "," +
// SecondsBetween(lastUnchoked.Value, DateTime.Now).ToString());
// logEntry.Append(";");
// }
// }
// }
// Send2Log(logEntry.ToString());
//}
//Scan the peers building the lists as we go and count number of unchoked peers
var unchokedPeers = 0;
foreach (var connectedPeer in _owningTorrent.Peers.ConnectedPeers)
{
if (connectedPeer.Connection != null)
{
if (!connectedPeer.Peer.IsSeeder)
{
//Determine common values for use in this routine
var timeSinceLastReview = SecondsBetween(_timeOfLastReview, DateTime.Now);
double timeUnchoked = 0;
if (!connectedPeer.AmChoking)
{
timeUnchoked = SecondsBetween(connectedPeer.LastUnchoked.Value, DateTime.Now);
unchokedPeers++;
}
long bytesTransferred = 0;
if (!_isDownloading)
//We are seeding the torrent; determine bytesTransferred as bytes uploaded
bytesTransferred = connectedPeer.Monitor.DataBytesUploaded -
connectedPeer.BytesUploadedAtLastReview;
else
//The peer is unchoked and we are downloading the torrent; determine bytesTransferred as bytes downloaded
bytesTransferred = connectedPeer.Monitor.DataBytesDownloaded -
connectedPeer.BytesDownloadedAtLastReview;
//Reset review up and download rates to zero; peers are therefore non-responders unless we determine otherwise
connectedPeer.LastReviewDownloadRate = 0;
connectedPeer.LastReviewUploadRate = 0;
if (!connectedPeer.AmChoking &&
(timeUnchoked < _minimumTimeBetweenReviews ||
(connectedPeer.FirstReviewPeriod && bytesTransferred > 0)))
//The peer is unchoked but either it has not been unchoked for the warm up interval,
// or it is the first full period and only just started transferring data
_nascentPeers.Add(connectedPeer);
else if ((timeUnchoked >= _minimumTimeBetweenReviews) && bytesTransferred > 0)
//The peer is unchoked, has been for the warm up period and has transferred data in the period
{
//Add to peers that are candidates for unchoking based on their performance
_candidatePeers.Add(connectedPeer);
//Calculate the latest up/downloadrate
connectedPeer.LastReviewUploadRate = (connectedPeer.Monitor.DataBytesUploaded -
connectedPeer.BytesUploadedAtLastReview)/
timeSinceLastReview;
connectedPeer.LastReviewDownloadRate = (connectedPeer.Monitor.DataBytesDownloaded -
connectedPeer.BytesDownloadedAtLastReview)/
timeSinceLastReview;
}
else if (_isDownloading && connectedPeer.IsInterested && connectedPeer.AmChoking &&
bytesTransferred > 0)
//A peer is optimistically unchoking us. Take the maximum of their current download rate and their download rate over the
// review period since they might have only just unchoked us and we don't want to miss out on a good opportunity. Upload
// rate is less important, so just take an average over the period.
{
//Add to peers that are candidates for unchoking based on their performance
_candidatePeers.Add(connectedPeer);
//Calculate the latest up/downloadrate
connectedPeer.LastReviewUploadRate = (connectedPeer.Monitor.DataBytesUploaded -
connectedPeer.BytesUploadedAtLastReview)/
timeSinceLastReview;
connectedPeer.LastReviewDownloadRate =
Math.Max(
(connectedPeer.Monitor.DataBytesDownloaded -
connectedPeer.BytesDownloadedAtLastReview)/timeSinceLastReview,
connectedPeer.Monitor.DownloadSpeed);
}
else if (connectedPeer.IsInterested)
//All other interested peers are candidates for optimistic unchoking
_optimisticUnchokeCandidates.Add(connectedPeer);
//Remember the number of bytes up and downloaded for the next review
connectedPeer.BytesUploadedAtLastReview = connectedPeer.Monitor.DataBytesUploaded;
connectedPeer.BytesDownloadedAtLastReview = connectedPeer.Monitor.DataBytesDownloaded;
//If the peer has been unchoked for longer than one review period, unset FirstReviewPeriod
if (timeUnchoked >= _minimumTimeBetweenReviews)
connectedPeer.FirstReviewPeriod = false;
}
}
}
// Send2Log(nascentPeers.Count.ToString() + "," + candidatePeers.Count.ToString() + "," + optimisticUnchokeCandidates.Count.ToString());
//Now sort the lists of peers so we are ready to reallocate them
_nascentPeers.Sort(_owningTorrent.State == TorrentState.Seeding);
_candidatePeers.Sort(_owningTorrent.State == TorrentState.Seeding);
_optimisticUnchokeCandidates.Sort(_owningTorrent.State == TorrentState.Seeding);
// if (isLogging)
// {
// string x = "";
// while (optimisticUnchokeCandidates.MorePeers)
// x += optimisticUnchokeCandidates.GetNextPeer().Location + ";";
// Send2Log(x);
// optimisticUnchokeCandidates.StartScan();
// }
//If there is an optimistic unchoke peer and it is nascent, we should reallocate all the available slots
//Otherwise, if all the slots are allocated to nascent peers, don't try an optimistic unchoke this time
if (_nascentPeers.Count >= _owningTorrent.Settings.UploadSlots || _nascentPeers.Includes(_optimisticUnchokePeer))
ReallocateSlots(_owningTorrent.Settings.UploadSlots, unchokedPeers);
else
{
//We should reallocate all the slots but one and allocate the last slot to the next optimistic unchoke peer
ReallocateSlots(_owningTorrent.Settings.UploadSlots - 1, unchokedPeers);
//In case we don't find a suitable peer, make the optimistic unchoke peer null
var oup = _optimisticUnchokeCandidates.GetOUPeer();
if (oup != null)
{
// Send2Log("OUP: " + oup.Location);
Unchoke(oup);
_optimisticUnchokePeer = oup;
}
}
//Finally, deallocate (any) remaining peers from the three lists
while (_nascentPeers.MorePeers)
{
var nextPeer = _nascentPeers.GetNextPeer();
if (!nextPeer.AmChoking)
Choke(nextPeer);
}
while (_candidatePeers.MorePeers)
{
var nextPeer = _candidatePeers.GetNextPeer();
if (!nextPeer.AmChoking)
Choke(nextPeer);
}
while (_optimisticUnchokeCandidates.MorePeers)
{
var nextPeer = _optimisticUnchokeCandidates.GetNextPeer();
if (!nextPeer.AmChoking)
//This peer is currently unchoked, choke it unless it is the optimistic unchoke peer
if (_optimisticUnchokePeer == null)
//There isn't an optimistic unchoke peer
Choke(nextPeer);
else if (!nextPeer.Equals(_optimisticUnchokePeer))
//This isn't the optimistic unchoke peer
Choke(nextPeer);
}
_timeOfLastReview = DateTime.Now;
ReviewsExecuted++;
}