diff --git a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/DownloadMode.cs b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/DownloadMode.cs index e0929cc85..821be42d3 100644 --- a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/DownloadMode.cs +++ b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/DownloadMode.cs @@ -59,6 +59,9 @@ public DownloadMode (TorrentManager manager, DiskManager diskManager, Connection } } + public override void HandleFilePriorityChanged (ITorrentManagerFile file, Priority oldPriority) + => RefreshAmInterestedStatusForAllPeers (); + public override bool ShouldConnect (Peer peer) { return !(peer.IsSeeder && Manager.HasMetadata && Manager.Complete); diff --git a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/ErrorMode.cs b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/ErrorMode.cs index d0f1c3338..ac961d16a 100644 --- a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/ErrorMode.cs +++ b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/ErrorMode.cs @@ -54,6 +54,11 @@ public ErrorMode (TorrentManager manager, ConnectionManager connectionManager) public void Dispose () => Cancellation.Cancel (); + public void HandleFilePriorityChanged (ITorrentManagerFile file, Priority oldPriority) + { + // Nothing + } + public void HandleMessage (PeerId id, PeerMessage message, PeerMessage.Releaser releaser) => throw new NotSupportedException (); diff --git a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/HashingMode.cs b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/HashingMode.cs index cc9a85fc9..b3d577b0e 100644 --- a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/HashingMode.cs +++ b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/HashingMode.cs @@ -58,6 +58,11 @@ public HashingMode (TorrentManager manager, DiskManager diskManager) PausedCompletionSource.SetResult (null); } + public void HandleFilePriorityChanged (ITorrentManagerFile file, Priority oldPriority) + { + // Nothing + } + public void Pause () { if (State == TorrentState.HashingPaused) diff --git a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/IMode.cs b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/IMode.cs index b635eb04a..6735a29b9 100644 --- a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/IMode.cs +++ b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/IMode.cs @@ -42,6 +42,7 @@ interface IMode : IDisposable TorrentState State { get; } CancellationToken Token { get; } + void HandleFilePriorityChanged (ITorrentManagerFile file, Priority oldPriority); void HandleMessage (PeerId id, PeerMessage message, PeerMessage.Releaser releaser); void HandlePeerConnected (PeerId id); void HandlePeerDisconnected (PeerId id); diff --git a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/Mode.cs b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/Mode.cs index 42b809129..f5a4a5f10 100644 --- a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/Mode.cs +++ b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/Mode.cs @@ -54,7 +54,8 @@ abstract class Mode : IMode static readonly SHA1 AllowedFastHasher = SHA1.Create (); bool hashingPendingFiles; - + ValueStopwatch lastSendHaveMessage; + ValueStopwatch lastRefreshAllPeers; protected CancellationTokenSource Cancellation { get; } protected ConnectionManager ConnectionManager { get; } @@ -80,6 +81,11 @@ protected Mode (TorrentManager manager, DiskManager diskManager, ConnectionManag Unchoker = unchoker ?? new ChokeUnchokeManager (new TorrentManagerUnchokeable (manager)); } + public virtual void HandleFilePriorityChanged (ITorrentManagerFile file, Priority oldPriority) + { + + } + public void HandleMessage (PeerId id, PeerMessage message, PeerMessage.Releaser releaser) { if (!CanHandleMessages) @@ -577,7 +583,16 @@ protected virtual void AppendBitfieldMessage (PeerId id, MessageBundle bundle) protected void PreLogicTick (int counter) { SendAnnounces (); - CloseConnectionsForStalePeers (); + + // The 'AmInterested' status is dependent on whether or not the set of IPiecePicker's + // associated with the TorrentManager determine if any pieces are ready to be requested. + // There's no event which will be raised each time this occurs, so just periodically + // refresh peers. + if (!lastRefreshAllPeers.IsRunning || lastRefreshAllPeers.Elapsed > TimeSpan.FromSeconds (5)) { + lastRefreshAllPeers = ValueStopwatch.StartNew (); + RefreshAmInterestedStatusForAllPeers (); + CloseConnectionsForStalePeers (); + } Manager.Peers.UpdatePeerCounts (); //Execute initial logic for individual peers @@ -611,8 +626,6 @@ void PostLogicTick (int counter) for (int i = 0; i < Manager.Peers.ConnectedPeers.Count; i++) { var id = Manager.Peers.ConnectedPeers[i]; - if (id.Connection == null) - continue; if (!id.LastPeerExchangeReview.IsRunning || id.LastPeerExchangeReview.Elapsed > TimeSpan.FromMinutes (1)) { id.PeerExchangeManager?.OnTick (); @@ -629,8 +642,6 @@ void PostLogicTick (int counter) for (int i = 0; i < Manager.Peers.ConnectedPeers.Count; i++) { var id = Manager.Peers.ConnectedPeers[i]; - if (id.Connection == null) - continue; ConnectionManager.TryProcessQueue (Manager, id); @@ -778,8 +789,6 @@ internal async ReusableTask TryHashPendingFilesAsync () } } - ValueStopwatch lastSendHaveMessage; - void SendHaveMessagesToAll () { if (Manager.finishedPieces.Count == 0 || (lastSendHaveMessage.IsRunning && lastSendHaveMessage.ElapsedMilliseconds < 5000)) @@ -799,11 +808,15 @@ void SendHaveMessagesToAll () peer.MessageQueue.Enqueue (bundle, releaser); } + Manager.finishedPieces.Clear (); + } + + protected void RefreshAmInterestedStatusForAllPeers () + { foreach (PeerId peer in Manager.Peers.ConnectedPeers) { bool isInteresting = Manager.PieceManager.IsInteresting (peer); SetAmInterestedStatus (peer, isInteresting); } - Manager.finishedPieces.Clear (); } public void Dispose () diff --git a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/PausedMode.cs b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/PausedMode.cs index 4d31ee503..04ae64cb1 100644 --- a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/PausedMode.cs +++ b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/PausedMode.cs @@ -41,6 +41,9 @@ public PausedMode (TorrentManager manager, DiskManager diskManager, ConnectionMa // does not need to do anything special. } + public override void HandleFilePriorityChanged (ITorrentManagerFile file, Priority oldPriority) + => RefreshAmInterestedStatusForAllPeers (); + public override void Tick (int counter) { // TODO: In future maybe this can be made smarter by refactoring diff --git a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/StartingMode.cs b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/StartingMode.cs index 1eb7e35d5..770c9c9b3 100644 --- a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/StartingMode.cs +++ b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/StartingMode.cs @@ -63,6 +63,11 @@ public StartingMode (TorrentManager manager, DiskManager diskManager, Connection public void Dispose () => Cancellation.Cancel (); + public void HandleFilePriorityChanged (ITorrentManagerFile file, Priority oldPriority) + { + // Nothing + } + public void HandleMessage (PeerId id, PeerMessage message, PeerMessage.Releaser releaser) => throw new NotSupportedException (); diff --git a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/StoppedMode.cs b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/StoppedMode.cs index 22dacdefd..779520e51 100644 --- a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/StoppedMode.cs +++ b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/StoppedMode.cs @@ -50,6 +50,11 @@ public StoppedMode () public void Dispose () => Cancellation.Cancel (); + public void HandleFilePriorityChanged (ITorrentManagerFile file, Priority oldPriority) + { + // Nothing + } + public void HandleMessage (PeerId id, PeerMessage message, PeerMessage.Releaser releaser) => throw new NotSupportedException (); diff --git a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/StoppingMode.cs b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/StoppingMode.cs index 7c279f17e..5af6df512 100644 --- a/src/MonoTorrent.Client/MonoTorrent.Client.Modes/StoppingMode.cs +++ b/src/MonoTorrent.Client/MonoTorrent.Client.Modes/StoppingMode.cs @@ -58,6 +58,11 @@ public StoppingMode (TorrentManager manager, DiskManager diskManager, Connection public void Dispose () => Cancellation.Dispose (); + public void HandleFilePriorityChanged (ITorrentManagerFile file, Priority oldPriority) + { + // Nothing + } + public void HandleMessage (PeerId id, PeerMessage message, PeerMessage.Releaser releaser) => throw new NotSupportedException (); diff --git a/src/MonoTorrent.Client/MonoTorrent.Client/Managers/TorrentManager.cs b/src/MonoTorrent.Client/MonoTorrent.Client/Managers/TorrentManager.cs index e2a9e4819..be9b21aea 100644 --- a/src/MonoTorrent.Client/MonoTorrent.Client/Managers/TorrentManager.cs +++ b/src/MonoTorrent.Client/MonoTorrent.Client/Managers/TorrentManager.cs @@ -95,6 +95,7 @@ public async Task SetFilePriorityAsync (ITorrentManagerFile file, Priority prior // If the old priority, or new priority, is 'DoNotDownload' then the selector needs to be refreshed bool needsToUpdateSelector = file.Priority == Priority.DoNotDownload || priority == Priority.DoNotDownload; + var oldPriority = file.Priority; ((TorrentFileInfo) file).Priority = priority; if (needsToUpdateSelector) { @@ -109,6 +110,8 @@ public async Task SetFilePriorityAsync (ITorrentManagerFile file, Priority prior PartialProgressSelector.SetTrue ((f.StartPieceIndex, f.EndPieceIndex)); } } + + Mode.HandleFilePriorityChanged (file, oldPriority); } /// diff --git a/src/Tests/Tests.MonoTorrent.Client/MonoTorrent.Client.Modes/DownloadModeTests.cs b/src/Tests/Tests.MonoTorrent.Client/MonoTorrent.Client.Modes/DownloadModeTests.cs index ddb4d7c95..8826d5e5a 100644 --- a/src/Tests/Tests.MonoTorrent.Client/MonoTorrent.Client.Modes/DownloadModeTests.cs +++ b/src/Tests/Tests.MonoTorrent.Client/MonoTorrent.Client.Modes/DownloadModeTests.cs @@ -492,6 +492,21 @@ public async Task PartialProgress_RelatedDownloaded_FileAdded () Assert.AreEqual (TorrentState.Downloading, newState, "#7"); } + [Test] + public async Task SettingPriorityRefreshesAmInterestedStatus () + { + Manager.OnPieceHashed (0, true); + + var mode = new DownloadMode (Manager, DiskManager, ConnectionManager, Settings); + Manager.Mode = mode; + + var peer = PeerId.CreateNull (Manager.Bitfield.Length, true, true, true, Manager.InfoHashes.V1OrV2); + Manager.Peers.ConnectedPeers.Add (peer); + foreach (var file in Manager.Files) + await Manager.SetFilePriorityAsync (file, Priority.DoNotDownload); + Assert.IsFalse (peer.AmInterested); + } + [Test] public async Task PartialProgress_UnrelatedDownloaded_AllDoNotDownload () {