Skip to content

Commit c9e31cf

Browse files
committed
[core] Allow the StreamingPicker to pick any piece
A prior change to ensure pieces from within the high, or low priority range were always prioritised ahead of pieces from the 'not important at all' range accidentally made the picker not chooose pieces from the 'not important at all' set. This would have little significance for real world streaming scenarios as the 'not important at all' set are pieces which come are before the current file position, i.e. pieces the media player would not try to use. Fixes #425
1 parent 4961054 commit c9e31cf

File tree

2 files changed

+119
-22
lines changed

2 files changed

+119
-22
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
//
2+
// StreamingPieceRequesterTests.cs
3+
//
4+
// Authors:
5+
// Alan McGovern [email protected]
6+
//
7+
// Copyright (C) 2021 Alan McGovern
8+
//
9+
// Permission is hereby granted, free of charge, to any person obtaining
10+
// a copy of this software and associated documentation files (the
11+
// "Software"), to deal in the Software without restriction, including
12+
// without limitation the rights to use, copy, modify, merge, publish,
13+
// distribute, sublicense, and/or sell copies of the Software, and to
14+
// permit persons to whom the Software is furnished to do so, subject to
15+
// the following conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be
18+
// included in all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24+
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25+
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26+
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27+
//
28+
29+
30+
using System;
31+
using System.Collections.Generic;
32+
using System.Linq;
33+
34+
using MonoTorrent.Client.Messages;
35+
using MonoTorrent.Client.Messages.Standard;
36+
37+
using NUnit.Framework;
38+
39+
namespace MonoTorrent.Client.PiecePicking
40+
{
41+
[TestFixture]
42+
public class StreamingPieceRequesterTests
43+
{
44+
class TorrentData : ITorrentData
45+
{
46+
public IList<ITorrentFileInfo> Files { get; } = TorrentFileInfo.Create (Piece.BlockSize * 8, 1024 * 1024 * 8);
47+
public int PieceLength => Piece.BlockSize * 8;
48+
public long Size => Files[0].Length;
49+
}
50+
51+
[Test]
52+
public void PickFromBeforeHighPrioritySet ()
53+
{
54+
var data = new TorrentData ();
55+
var ignoringBitfield = new MutableBitField (data.PieceCount ())
56+
.SetAll (true)
57+
.Set (0, false);
58+
59+
var requester = new StreamingPieceRequester ();
60+
requester.Initialise (data, new[] { ignoringBitfield });
61+
requester.SeekToPosition (data.Files[0], data.PieceLength * 3);
62+
63+
var peer = PeerId.CreateNull (ignoringBitfield.Length, true, false, true);
64+
requester.AddRequests (peer, Array.Empty<IPeerWithMessaging> ());
65+
Assert.AreEqual (2, peer.AmRequestingPiecesCount);
66+
67+
var requests = GetRequests (peer);
68+
Assert.AreEqual (2, requests.Count);
69+
Assert.IsTrue (requests.All (r => r.PieceIndex == 0));
70+
}
71+
72+
[Test]
73+
public void PickHighestPriority ()
74+
{
75+
var data = new TorrentData ();
76+
var ignoringBitfield = new MutableBitField (data.PieceCount ())
77+
.SetAll (false);
78+
79+
var requester = new StreamingPieceRequester ();
80+
requester.Initialise (data, new[] { ignoringBitfield });
81+
requester.SeekToPosition (data.Files[0], data.PieceLength * 3);
82+
83+
var peer = PeerId.CreateNull (ignoringBitfield.Length, true, false, true);
84+
requester.AddRequests (peer, Array.Empty<IPeerWithMessaging> ());
85+
Assert.AreEqual (4, peer.AmRequestingPiecesCount);
86+
87+
var requests = GetRequests (peer);
88+
Assert.AreEqual (4, requests.Count);
89+
Assert.IsTrue (requests.All (r => r.PieceIndex == 3));
90+
}
91+
92+
static List<RequestMessage> GetRequests (PeerId peer)
93+
{
94+
List<RequestMessage> results = new List<RequestMessage> ();
95+
while (peer.MessageQueue.QueueLength > 0) {
96+
var message = peer.MessageQueue.TryDequeue ();
97+
if (message is RequestMessage r) {
98+
results.Add (r);
99+
} else if (message is RequestBundle bundle) {
100+
foreach (var inner in bundle.ToRequestMessages ())
101+
if (inner is RequestMessage req)
102+
results.Add (req);
103+
}
104+
}
105+
return results;
106+
}
107+
108+
}
109+
}

src/MonoTorrent/MonoTorrent.Client.PiecePicking/StreamingPieceRequester.cs

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public void AddRequests (IReadOnlyList<IPeerWithMessaging> peers)
121121
foreach (var supportsFastPeer in new[] { true, false }) {
122122
for (int i = 0; i < 4; i++) {
123123
foreach (var peer in fastestPeers.Where (p => p.SupportsFastPeer == supportsFastPeer)) {
124-
AddRequests (peer, peers, HighPriorityPieceIndex, Math.Min (HighPriorityPieceIndex + 1, bitfield.Length - 1), 2, (i + 1) * 2);
124+
AddRequests (peer, peers, HighPriorityPieceIndex, Math.Min (HighPriorityPieceIndex + 1, bitfield.Length - 1), 2, preferredMaxRequests : (i + 1) * 2);
125125
}
126126
}
127127
}
@@ -136,19 +136,20 @@ public void AddRequests (IPeerWithMessaging peer, IReadOnlyList<IPeerWithMessagi
136136
// The first two pieces in the high priority set should be requested multiple times to ensure fast delivery
137137
var pieceCount = TorrentData.PieceCount ();
138138
for (int i = HighPriorityPieceIndex; i < pieceCount && i <= HighPriorityPieceIndex + 1; i++)
139-
AddRequests (peer, allPeers, HighPriorityPieceIndex, HighPriorityPieceIndex, 2);
139+
AddRequests (peer, allPeers, HighPriorityPieceIndex, HighPriorityPieceIndex, 2, preferredMaxRequests: 4);
140140

141141
var lowPriorityEnd = Math.Min (pieceCount - 1, HighPriorityPieceIndex + LowPriorityCount - 1);
142-
AddRequests (peer, allPeers, HighPriorityPieceIndex, lowPriorityEnd, 1);
142+
AddRequests (peer, allPeers, HighPriorityPieceIndex, lowPriorityEnd, 1, preferredMaxRequests: 3);
143+
AddRequests (peer, allPeers, 0, pieceCount - 1, 1, preferredMaxRequests: 2);
143144
}
144145

145-
void AddRequests (IPeerWithMessaging peer, IReadOnlyList<IPeerWithMessaging> allPeers, int startPieceIndex, int endPieceIndex, int maxDuplicates, int? preferredMaxRequests = null)
146+
void AddRequests (IPeerWithMessaging peer, IReadOnlyList<IPeerWithMessaging> allPeers, int startPieceIndex, int endPieceIndex, int maxDuplicates, int preferredMaxRequests)
146147
{
147148
if (!peer.CanRequestMorePieces)
148149
return;
149150

150151
int preferredRequestAmount = peer.PreferredRequestAmount (TorrentData.PieceLength);
151-
var maxRequests = Math.Min (preferredMaxRequests ?? 3, peer.MaxPendingRequests);
152+
var maxRequests = Math.Min (preferredMaxRequests, peer.MaxPendingRequests);
152153

153154
if (peer.AmRequestingPiecesCount >= maxRequests)
154155
return;
@@ -185,23 +186,13 @@ void AddRequests (IPeerWithMessaging peer, IReadOnlyList<IPeerWithMessaging> all
185186
MutableBitField filtered = null;
186187
while (peer.AmRequestingPiecesCount < maxRequests) {
187188
filtered ??= GenerateAlreadyHaves ().Not ().And (peer.BitField);
188-
IList<BlockInfo> request = PriorityPick (peer, filtered, allPeers, preferredRequestAmount, 0, TorrentData.PieceCount () - 1);
189+
IList<BlockInfo> request = PriorityPick (peer, filtered, allPeers, preferredRequestAmount, startPieceIndex, endPieceIndex);
189190
if (request != null && request.Count > 0)
190191
peer.EnqueueRequests (request);
191192
else
192193
break;
193194
}
194195
}
195-
196-
if (!peer.IsChoking && peer.AmRequestingPiecesCount == 0) {
197-
while (peer.AmRequestingPiecesCount < maxRequests) {
198-
BlockInfo? request = Picker.ContinueAnyExistingRequest (peer, HighPriorityPieceIndex, TorrentData.PieceCount () - 1, 1);
199-
if (request != null)
200-
peer.EnqueueRequest (request.Value);
201-
else
202-
break;
203-
}
204-
}
205196
}
206197

207198
MutableBitField GenerateAlreadyHaves ()
@@ -234,18 +225,15 @@ IList<BlockInfo> PriorityPick (IPeer peer, BitField available, IReadOnlyList<IPe
234225
return bundle;
235226
}
236227

237-
if (endIndex < HighPriorityPieceIndex)
238-
return null;
239-
240228
var lowPriorityEndIndex = Math.Min (HighPriorityPieceIndex + LowPriorityCount, endIndex);
241229
if ((bundle = LowPriorityPicker.PickPiece (peer, available, otherPeers, count, HighPriorityPieceIndex, lowPriorityEndIndex)) != null)
242230
return bundle;
243231

244232
// If we're downloading from the 'not important at all' section, queue up at most 2.
245-
if (peer.AmRequestingPiecesCount > 2)
246-
return null;
233+
if (peer.AmRequestingPiecesCount < 2)
234+
return LowPriorityPicker.PickPiece (peer, available, otherPeers, count, startIndex, endIndex);
247235

248-
return LowPriorityPicker.PickPiece (peer, available, otherPeers, count, HighPriorityPieceIndex, endIndex);
236+
return null;
249237
}
250238

251239
/// <summary>

0 commit comments

Comments
 (0)