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) { if (connectedPeer.LastUnchoked != null) { timeUnchoked = SecondsBetween(connectedPeer.LastUnchoked.Value, DateTime.Now); } unchokedPeers++; } long bytesTransferred; 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++; }