2021-05-13 07:56:58 +08:00
|
|
|
package request_strategy
|
|
|
|
|
|
|
|
import (
|
2021-09-18 18:34:14 +08:00
|
|
|
"bytes"
|
2021-11-30 12:18:38 +08:00
|
|
|
"expvar"
|
2021-05-13 07:56:58 +08:00
|
|
|
|
2023-01-03 22:25:57 +08:00
|
|
|
"github.com/anacrolix/generics"
|
2021-05-13 07:56:58 +08:00
|
|
|
"github.com/anacrolix/multiless"
|
2021-05-21 12:02:45 +08:00
|
|
|
|
2022-11-15 20:22:10 +08:00
|
|
|
"github.com/anacrolix/torrent/metainfo"
|
2021-05-13 07:56:58 +08:00
|
|
|
"github.com/anacrolix/torrent/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
2022-05-09 09:34:08 +08:00
|
|
|
RequestIndex uint32
|
|
|
|
ChunkIndex = RequestIndex
|
2021-05-13 07:56:58 +08:00
|
|
|
Request = types.Request
|
|
|
|
pieceIndex = types.PieceIndex
|
|
|
|
piecePriority = types.PiecePriority
|
2021-05-13 09:26:22 +08:00
|
|
|
// This can be made into a type-param later, will be great for testing.
|
|
|
|
ChunkSpec = types.ChunkSpec
|
2021-05-13 07:56:58 +08:00
|
|
|
)
|
|
|
|
|
2021-12-15 15:07:17 +08:00
|
|
|
func pieceOrderLess(i, j *pieceRequestOrderItem) multiless.Computation {
|
2021-11-30 18:31:32 +08:00
|
|
|
return multiless.New().Int(
|
2021-12-15 15:07:17 +08:00
|
|
|
int(j.state.Priority), int(i.state.Priority),
|
2021-12-17 19:06:21 +08:00
|
|
|
// TODO: Should we match on complete here to prevent churn when availability changes?
|
2021-11-30 18:31:32 +08:00
|
|
|
).Bool(
|
2021-12-15 15:07:17 +08:00
|
|
|
j.state.Partial, i.state.Partial,
|
2021-12-17 19:06:21 +08:00
|
|
|
).Int(
|
|
|
|
// If this is done with relative availability, do we lose some determinism? If completeness
|
|
|
|
// is used, would that push this far enough down?
|
2021-12-15 15:07:17 +08:00
|
|
|
i.state.Availability, j.state.Availability,
|
2021-11-30 18:31:32 +08:00
|
|
|
).Int(
|
2021-12-15 15:07:17 +08:00
|
|
|
i.key.Index, j.key.Index,
|
2021-11-30 18:31:32 +08:00
|
|
|
).Lazy(func() multiless.Computation {
|
|
|
|
return multiless.New().Cmp(bytes.Compare(
|
2021-12-15 15:07:17 +08:00
|
|
|
i.key.InfoHash[:],
|
|
|
|
j.key.InfoHash[:],
|
2021-11-30 18:31:32 +08:00
|
|
|
))
|
2021-12-01 11:38:47 +08:00
|
|
|
})
|
2021-05-13 07:56:58 +08:00
|
|
|
}
|
|
|
|
|
2021-11-30 12:18:38 +08:00
|
|
|
var packageExpvarMap = expvar.NewMap("request-strategy")
|
|
|
|
|
2021-09-18 16:57:50 +08:00
|
|
|
// Calls f with requestable pieces in order.
|
2022-05-09 17:37:35 +08:00
|
|
|
func GetRequestablePieces(
|
|
|
|
input Input, pro *PieceRequestOrder,
|
|
|
|
f func(ih metainfo.Hash, pieceIndex int, orderState PieceRequestOrderState),
|
|
|
|
) {
|
2021-05-13 07:56:58 +08:00
|
|
|
// Storage capacity left for this run, keyed by the storage capacity pointer on the storage
|
2021-09-15 08:30:37 +08:00
|
|
|
// TorrentImpl. A nil value means no capacity limit.
|
2021-11-29 10:07:18 +08:00
|
|
|
var storageLeft *int64
|
2021-12-01 16:21:25 +08:00
|
|
|
if cap, ok := input.Capacity(); ok {
|
|
|
|
storageLeft = &cap
|
2021-11-29 10:07:18 +08:00
|
|
|
}
|
2021-05-14 11:40:09 +08:00
|
|
|
var allTorrentsUnverifiedBytes int64
|
2023-01-03 22:25:57 +08:00
|
|
|
var lastItem generics.Option[pieceRequestOrderItem]
|
2021-12-18 13:07:44 +08:00
|
|
|
pro.tree.Scan(func(_i pieceRequestOrderItem) bool {
|
2023-01-03 22:25:57 +08:00
|
|
|
// Check that scan emits pieces in priority order.
|
|
|
|
if lastItem.Ok {
|
|
|
|
if _i.Less(&lastItem.Value) {
|
|
|
|
panic("scan not in order")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lastItem.Set(_i)
|
|
|
|
|
2021-12-01 16:21:25 +08:00
|
|
|
ih := _i.key.InfoHash
|
2022-05-09 08:51:50 +08:00
|
|
|
var t = input.Torrent(ih)
|
2021-12-01 16:21:25 +08:00
|
|
|
pieceLength := t.PieceLength()
|
|
|
|
if storageLeft != nil {
|
|
|
|
if *storageLeft < pieceLength {
|
2021-12-01 16:31:29 +08:00
|
|
|
return false
|
2021-05-13 07:56:58 +08:00
|
|
|
}
|
2021-12-01 16:21:25 +08:00
|
|
|
*storageLeft -= pieceLength
|
2021-05-14 09:50:41 +08:00
|
|
|
}
|
2022-04-11 12:22:05 +08:00
|
|
|
if t.IgnorePiece(_i.key.Index) {
|
2021-05-14 11:40:09 +08:00
|
|
|
// TODO: Clarify exactly what is verified. Stuff that's being hashed should be
|
|
|
|
// considered unverified and hold up further requests.
|
2021-12-01 11:38:47 +08:00
|
|
|
return true
|
2021-05-13 07:56:58 +08:00
|
|
|
}
|
2021-12-01 16:21:25 +08:00
|
|
|
if input.MaxUnverifiedBytes() != 0 && allTorrentsUnverifiedBytes+pieceLength > input.MaxUnverifiedBytes() {
|
2021-12-01 11:38:47 +08:00
|
|
|
return true
|
2021-05-14 11:40:09 +08:00
|
|
|
}
|
2021-12-01 16:21:25 +08:00
|
|
|
allTorrentsUnverifiedBytes += pieceLength
|
2022-05-09 17:37:35 +08:00
|
|
|
f(ih, _i.key.Index, _i.state)
|
2021-12-01 11:38:47 +08:00
|
|
|
return true
|
|
|
|
})
|
2021-05-14 11:06:12 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-01 16:21:25 +08:00
|
|
|
type Input interface {
|
|
|
|
Torrent(metainfo.Hash) Torrent
|
|
|
|
// Storage capacity, shared among all Torrents with the same storage.TorrentCapacity pointer in
|
|
|
|
// their storage.Torrent references.
|
|
|
|
Capacity() (cap int64, capped bool)
|
2021-11-29 10:07:18 +08:00
|
|
|
// Across all the Torrents. This might be partitioned by storage capacity key now.
|
2021-12-01 16:21:25 +08:00
|
|
|
MaxUnverifiedBytes() int64
|
2021-05-13 16:35:49 +08:00
|
|
|
}
|