File indexing completed on 2025-01-05 04:37:22

0001 /*
0002     SPDX-FileCopyrightText: 2005 Joris Guisson <joris.guisson@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 #include "peerdownloader.h"
0007 
0008 #include "peer.h"
0009 #include <download/piece.h>
0010 #include <math.h>
0011 #include <util/functions.h>
0012 #include <util/log.h>
0013 
0014 namespace bt
0015 {
0016 TimeStampedRequest::TimeStampedRequest()
0017 {
0018     time_stamp = bt::CurrentTime();
0019 }
0020 
0021 TimeStampedRequest::TimeStampedRequest(const Request &r)
0022     : req(r)
0023 {
0024     time_stamp = bt::CurrentTime();
0025 }
0026 
0027 TimeStampedRequest::TimeStampedRequest(const TimeStampedRequest &t)
0028     : req(t.req)
0029     , time_stamp(t.time_stamp)
0030 {
0031 }
0032 
0033 TimeStampedRequest::~TimeStampedRequest()
0034 {
0035 }
0036 
0037 bool TimeStampedRequest::operator==(const Request &r) const
0038 {
0039     return r == req;
0040 }
0041 
0042 bool TimeStampedRequest::operator==(const TimeStampedRequest &r) const
0043 {
0044     return r.req == req;
0045 }
0046 
0047 TimeStampedRequest &TimeStampedRequest::operator=(const Request &r)
0048 {
0049     time_stamp = bt::CurrentTime();
0050     req = r;
0051     return *this;
0052 }
0053 
0054 TimeStampedRequest &TimeStampedRequest::operator=(const TimeStampedRequest &r)
0055 {
0056     time_stamp = r.time_stamp;
0057     req = r.req;
0058     return *this;
0059 }
0060 
0061 PeerDownloader::PeerDownloader(Peer *peer, Uint32 chunk_size)
0062     : peer(peer)
0063     , chunk_size(chunk_size / MAX_PIECE_LEN)
0064 {
0065     connect(peer, &Peer::destroyed, this, &PeerDownloader::peerDestroyed);
0066     max_wait_queue_size = 25;
0067 }
0068 
0069 PeerDownloader::~PeerDownloader()
0070 {
0071 }
0072 
0073 QString PeerDownloader::getName() const
0074 {
0075     return peer->getPeerID().identifyClient();
0076 }
0077 
0078 bool PeerDownloader::canAddRequest() const
0079 {
0080     return (Uint32)wait_queue.count() < max_wait_queue_size;
0081 }
0082 
0083 bool PeerDownloader::canDownloadChunk() const
0084 {
0085     return !isNull() && (getNumGrabbed() < (int)getMaxChunkDownloads() || isNearlyDone()) && canAddRequest();
0086 }
0087 
0088 Uint32 PeerDownloader::getNumRequests() const
0089 {
0090     return reqs.count() /*+ wait_queue.count() */;
0091 }
0092 
0093 void PeerDownloader::download(const Request &req)
0094 {
0095     if (!peer)
0096         return;
0097 
0098     wait_queue.append(req);
0099     update();
0100 }
0101 
0102 void PeerDownloader::cancel(const Request &req)
0103 {
0104     if (!peer)
0105         return;
0106 
0107     if (!wait_queue.removeAll(req)) {
0108         reqs.removeAll(req);
0109         peer->sendCancel(req);
0110     }
0111 }
0112 
0113 void PeerDownloader::onRejected(const Request &req)
0114 {
0115     if (!peer)
0116         return;
0117 
0118     if (reqs.removeAll(req))
0119         rejected(req);
0120 }
0121 
0122 void PeerDownloader::cancelAll()
0123 {
0124     if (peer) {
0125         QList<TimeStampedRequest>::iterator i = reqs.begin();
0126         while (i != reqs.end()) {
0127             TimeStampedRequest &tr = *i;
0128             peer->sendCancel(tr.req);
0129             ++i;
0130         }
0131     }
0132 
0133     wait_queue.clear();
0134     reqs.clear();
0135 }
0136 
0137 void PeerDownloader::piece(const Piece &p)
0138 {
0139     Request r(p);
0140     if (!reqs.removeOne(r))
0141         wait_queue.removeAll(r);
0142 }
0143 
0144 void PeerDownloader::peerDestroyed()
0145 {
0146     peer = nullptr;
0147 }
0148 
0149 bool PeerDownloader::isChoked() const
0150 {
0151     if (peer)
0152         return peer->isChoked();
0153     else
0154         return true;
0155 }
0156 
0157 bool PeerDownloader::hasChunk(Uint32 idx) const
0158 {
0159     if (peer)
0160         return peer->getBitSet().get(idx);
0161     else
0162         return false;
0163 }
0164 
0165 Uint32 PeerDownloader::getDownloadRate() const
0166 {
0167     if (peer)
0168         return peer->getDownloadRate();
0169     else
0170         return 0;
0171 }
0172 
0173 void PeerDownloader::checkTimeouts()
0174 {
0175     TimeStamp now = bt::CurrentTime();
0176     // we use a 60 second interval
0177     const Uint32 MAX_INTERVAL = 60 * 1000;
0178 
0179     // expire any timed-out requests: the list is sorted with the
0180     // oldest requests at the front, so we simply pop off requests
0181     // until we find one that shouldn't be expired
0182     while (!reqs.isEmpty() && (now - reqs.first().time_stamp > MAX_INTERVAL))
0183         timedout(reqs.takeFirst().req);
0184 }
0185 
0186 Uint32 PeerDownloader::getMaxChunkDownloads() const
0187 {
0188     // get the download rate in KB/sec
0189     Uint32 rate_kbs = peer->getDownloadRate();
0190     rate_kbs = rate_kbs / 1024;
0191     Uint32 num_extra = rate_kbs / 25;
0192 
0193     if (chunk_size >= 16) {
0194         return 1 + 16 * num_extra / chunk_size;
0195     } else {
0196         return 1 + (16 / chunk_size) * num_extra;
0197     }
0198 }
0199 
0200 void PeerDownloader::choked()
0201 {
0202     // when the peers supports the fast extensions, choke does not mean that all
0203     // requests are rejected, so lets do nothing
0204     if (peer->getStats().fast_extensions)
0205         return;
0206 
0207     QList<TimeStampedRequest>::iterator i = reqs.begin();
0208     while (i != reqs.end()) {
0209         TimeStampedRequest &tr = *i;
0210         rejected(tr.req);
0211         ++i;
0212     }
0213     reqs.clear();
0214 
0215     QList<Request>::iterator j = wait_queue.begin();
0216     while (j != wait_queue.end()) {
0217         Request &req = *j;
0218         rejected(req);
0219         ++j;
0220     }
0221     wait_queue.clear();
0222 }
0223 
0224 void PeerDownloader::update()
0225 {
0226     // modify the interval if necessary
0227     double pieces_per_sec = (double)peer->getDownloadRate() / MAX_PIECE_LEN;
0228     int max_reqs = 1 + (int)ceil(10 * pieces_per_sec);
0229     // cap if client has supplied a reqq in extended protocol handshake
0230     if (max_reqs > (int)peer->getStats().max_request_queue && peer->getStats().max_request_queue != 0)
0231         max_reqs = peer->getStats().max_request_queue;
0232 
0233     while (wait_queue.count() > 0 && reqs.count() < max_reqs) {
0234         // get a request from the wait queue and send that
0235         Request req = wait_queue.front();
0236         wait_queue.pop_front();
0237         TimeStampedRequest r = TimeStampedRequest(req);
0238         reqs.append(r);
0239         peer->sendRequest(req);
0240     }
0241 
0242     max_wait_queue_size = 2 * max_reqs;
0243     if (max_wait_queue_size < 10)
0244         max_wait_queue_size = 10;
0245 }
0246 }
0247 
0248 #include "moc_peerdownloader.cpp"