refactor: use C++ inheritance for tr_peer, tr_peerMsgs, and tr_webseed (#1877)

* refactor: use C++ inheritance for tr_peer, tr_peerMsgs, and tr_webseed
This commit is contained in:
Charles Kerr 2021-10-07 08:33:55 -05:00 committed by GitHub
parent 83f21b8e0e
commit 3b72a1feea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 850 additions and 1084 deletions

View File

@ -86,7 +86,6 @@
A209EBF91142FEEE002B02D1 /* InfoOptionsViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A209EBF81142FEEE002B02D1 /* InfoOptionsViewController.mm */; };
A209EC12114301C6002B02D1 /* InfoOptionsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = A209EC11114301C6002B02D1 /* InfoOptionsView.xib */; };
A209ECA2114319C3002B02D1 /* InfoWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = A209ECA1114319C3002B02D1 /* InfoWindow.xib */; };
A209EE5C1144B51E002B02D1 /* history.cc in Sources */ = {isa = PBXBuildFile; fileRef = A209EE5A1144B51E002B02D1 /* history.cc */; };
A209EE5D1144B51E002B02D1 /* history.h in Headers */ = {isa = PBXBuildFile; fileRef = A209EE5B1144B51E002B02D1 /* history.h */; };
A20BFFB70D091CC700CE5D2B /* ToolbarSegmentedCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = A20BFFB60D091CC700CE5D2B /* ToolbarSegmentedCell.mm */; };
A21282A80CA6C66800EAEE0F /* StatusBarView.mm in Sources */ = {isa = PBXBuildFile; fileRef = A21282A60CA6C66800EAEE0F /* StatusBarView.mm */; };
@ -599,7 +598,6 @@
A209EBF81142FEEE002B02D1 /* InfoOptionsViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = InfoOptionsViewController.mm; sourceTree = "<group>"; };
A209EC13114301C6002B02D1 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/InfoOptionsView.xib; sourceTree = "<group>"; };
A209ECA1114319C3002B02D1 /* InfoWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InfoWindow.xib; sourceTree = "<group>"; };
A209EE5A1144B51E002B02D1 /* history.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = history.cc; sourceTree = "<group>"; };
A209EE5B1144B51E002B02D1 /* history.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = history.h; sourceTree = "<group>"; };
A20BFFB50D091CC700CE5D2B /* ToolbarSegmentedCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ToolbarSegmentedCell.h; sourceTree = "<group>"; };
A20BFFB60D091CC700CE5D2B /* ToolbarSegmentedCell.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ToolbarSegmentedCell.mm; sourceTree = "<group>"; };
@ -1466,7 +1464,6 @@
A21FBBA90EDA78C300BC3C51 /* bandwidth.h */,
A21FBBAA0EDA78C300BC3C51 /* bandwidth.cc */,
A209EE5B1144B51E002B02D1 /* history.h */,
A209EE5A1144B51E002B02D1 /* history.cc */,
A23547E011CD0B090046EAE6 /* cache.cc */,
A23547E111CD0B090046EAE6 /* cache.h */,
BEFC1E020C07861A00B0BB3C /* platform.h */,
@ -2483,7 +2480,6 @@
A25964A6106D73A800453B31 /* announcer.cc in Sources */,
4D8017EA10BBC073008A4AF2 /* torrent-magnet.cc in Sources */,
4D80185910BBC0B0008A4AF2 /* magnet.cc in Sources */,
A209EE5C1144B51E002B02D1 /* history.cc in Sources */,
A220EC5B118C8A060022B4BE /* tr-lpd.cc in Sources */,
C1FEE57A1C3223CC00D62832 /* watchdir.cc in Sources */,
A23547E211CD0B090046EAE6 /* cache.cc in Sources */,

View File

@ -27,7 +27,6 @@ set(PROJECT_FILES
file-posix.cc
file-win32.cc
handshake.cc
history.cc
inout.cc
log.cc
magnet.cc

View File

@ -1,60 +0,0 @@
/*
* This file Copyright (C) 2010-2014 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
*/
#include <string.h> /* memset() */
#include "transmission.h"
#include "history.h"
#include "utils.h"
void tr_historyAdd(tr_recentHistory* h, time_t now, unsigned int n)
{
if (h->slices[h->newest].date == now)
{
h->slices[h->newest].n += n;
}
else
{
if (++h->newest == TR_RECENT_HISTORY_PERIOD_SEC)
{
h->newest = 0;
}
h->slices[h->newest].date = now;
h->slices[h->newest].n = n;
}
}
unsigned int tr_historyGet(tr_recentHistory const* h, time_t now, unsigned int sec)
{
unsigned int n = 0;
time_t const cutoff = (now != 0 ? now : tr_time()) - sec;
int i = h->newest;
for (;;)
{
if (h->slices[i].date <= cutoff)
{
break;
}
n += h->slices[i].n;
if (--i == -1)
{
i = TR_RECENT_HISTORY_PERIOD_SEC - 1; /* circular history */
}
if (i == h->newest)
{
break; /* we've come all the way around */
}
}
return n;
}

View File

@ -12,47 +12,61 @@
#error only libtransmission should #include this header.
#endif
#include <time.h> /* time_t */
#include "tr-macros.h"
#include <array>
#include <cstddef> // size_t
#include <ctime> // time_t
#include <numeric> // std::accumulate
/**
* A generic short-term memory object that remembers how many times
* something happened over the last N seconds.
*
* For example, it could count how many are bytes transferred
* to estimate the speed over the last N seconds.
* A short-term memory object that remembers how many times something
* happened over the last N seconds. tr_peer uses it to count how many
* bytes transferred to estimate the speed over the last N seconds.
*/
enum
class tr_recentHistory
{
TR_RECENT_HISTORY_PERIOD_SEC = 60
};
struct tr_recentHistory
{
/* these are PRIVATE IMPLEMENTATION details included for composition only.
* Don't access these directly! */
int newest;
struct
public:
/**
* @brief add a counter to the recent history object.
* @param when the current time in sec, such as from tr_time()
* @param n how many items to add to the history's counter
*/
void add(time_t now, size_t n)
{
unsigned int n;
time_t date;
} slices[TR_RECENT_HISTORY_PERIOD_SEC];
if (slices[newest].time != now)
{
newest = (newest + 1) % TR_RECENT_HISTORY_PERIOD_SEC;
slices[newest].time = now;
}
slices[newest].n += n;
}
/**
* @brief count how many events have occurred in the last N seconds.
* @param when the current time in sec, such as from tr_time()
* @param seconds how many seconds to count back through.
*/
size_t count(time_t now, unsigned int age_sec) const
{
time_t const oldest = now - age_sec;
return std::accumulate(
std::begin(slices),
std::end(slices),
size_t{ 0 },
[&oldest](size_t sum, auto const& slice) { return slice.time >= oldest ? sum + slice.n : sum; });
}
private:
inline auto static constexpr TR_RECENT_HISTORY_PERIOD_SEC = size_t{ 60 };
int newest = 0;
struct slice_t
{
size_t n = 0;
time_t time = 0;
};
std::array<slice_t, TR_RECENT_HISTORY_PERIOD_SEC> slices = {};
};
/**
* @brief add a counter to the recent history object.
* @param when the current time in sec, such as from tr_time()
* @param n how many items to add to the history's counter
*/
void tr_historyAdd(tr_recentHistory*, time_t when, unsigned int n);
/**
* @brief count how many events have occurred in the last N seconds.
* @param when the current time in sec, such as from tr_time()
* @param seconds how many seconds to count back through.
*/
unsigned int tr_historyGet(tr_recentHistory const*, time_t when, unsigned int seconds);

View File

@ -22,16 +22,14 @@
* @{
*/
struct tr_peer;
class tr_peer;
struct tr_swarm;
struct peer_atom;
enum
{
/* this is the maximum size of a block request.
most bittorrent clients will reject requests
larger than this size. */
MAX_BLOCK_SIZE = (1024 * 16)
};
/* This is the maximum size of a block request.
most bittorrent clients will reject requests
larger than this size. */
auto inline constexpr MAX_BLOCK_SIZE = 1024 * 16;
/**
*** Peer Publish / Subscribe
@ -66,22 +64,7 @@ struct tr_peer_event
tr_port port; /* for GOT_PORT */
};
using tr_peer_callback = void (*)(struct tr_peer* peer, tr_peer_event const* event, void* client_data);
/***
****
***/
using tr_peer_destruct_func = void (*)(struct tr_peer* peer);
using tr_peer_is_transferring_pieces_func =
bool (*)(tr_peer const* peer, uint64_t now, tr_direction direction, unsigned int* Bps);
struct tr_peer_virtual_funcs
{
tr_peer_destruct_func destruct;
tr_peer_is_transferring_pieces_func is_transferring_pieces;
};
using tr_peer_callback = void (*)(tr_peer* peer, tr_peer_event const* event, void* client_data);
/**
* State information about a connected peer.
@ -89,53 +72,55 @@ struct tr_peer_virtual_funcs
* @see struct peer_atom
* @see tr_peerMsgs
*/
struct tr_peer
class tr_peer
{
public:
tr_peer(tr_torrent const* tor, peer_atom* atom = nullptr);
virtual ~tr_peer();
virtual bool is_transferring_pieces(uint64_t now, tr_direction direction, unsigned int* setme_Bps) const = 0;
/* whether or not we should free this peer soon.
NOTE: private to peer-mgr.c */
bool doPurge;
bool doPurge = false;
/* number of bad pieces they've contributed to */
uint8_t strikes;
uint8_t strikes = 0;
/* how many requests the peer has made that we haven't responded to yet */
int pendingReqsToClient;
int pendingReqsToClient = 0;
/* how many requests we've made and are currently awaiting a response for */
int pendingReqsToPeer;
int pendingReqsToPeer = 0;
tr_session* const session;
/* Hook to private peer-mgr information */
struct peer_atom* atom;
peer_atom* const atom;
struct tr_swarm* swarm;
tr_swarm* const swarm;
/** how complete the peer's copy of the torrent is. [0.0...1.0] */
float progress;
float progress = 0.0f;
struct tr_bitfield blame;
struct tr_bitfield have;
struct tr_bitfield blame = {};
struct tr_bitfield have = {};
/* the client name.
For BitTorrent peers, this is the app name derived from the `v' string in LTEP's handshake dictionary */
tr_quark client;
tr_quark client = TR_KEY_NONE;
tr_recentHistory blocksSentToClient;
tr_recentHistory blocksSentToPeer;
tr_recentHistory cancelsSentToClient;
tr_recentHistory cancelsSentToPeer;
struct tr_peer_virtual_funcs const* funcs;
};
void tr_peerConstruct(struct tr_peer* peer, tr_torrent const* tor);
void tr_peerDestruct(struct tr_peer* peer);
/** Update the tr_peer.progress field based on the 'have' bitset. */
void tr_peerUpdateProgress(tr_torrent* tor, struct tr_peer*);
void tr_peerUpdateProgress(tr_torrent* tor, tr_peer*);
bool tr_peerIsSeed(struct tr_peer const* peer);
bool tr_peerIsSeed(tr_peer const* peer);
/***
****

View File

@ -227,60 +227,37 @@ struct tr_peerMgr
*** tr_peer virtual functions
**/
static bool tr_peerIsTransferringPieces(tr_peer const* peer, uint64_t now, tr_direction direction, unsigned int* Bps)
{
TR_ASSERT(peer != nullptr);
TR_ASSERT(peer->funcs != nullptr);
return (*peer->funcs->is_transferring_pieces)(peer, now, direction, Bps);
}
unsigned int tr_peerGetPieceSpeed_Bps(tr_peer const* peer, uint64_t now, tr_direction direction)
{
unsigned int Bps = 0;
tr_peerIsTransferringPieces(peer, now, direction, &Bps);
peer->is_transferring_pieces(now, direction, &Bps);
return Bps;
}
static void tr_peerFree(tr_peer* peer)
tr_peer::tr_peer(tr_torrent const* tor, peer_atom* atom_in)
: session{ tor->session }
, atom{ atom_in }
, swarm{ tor->swarm }
{
TR_ASSERT(peer != nullptr);
TR_ASSERT(peer->funcs != nullptr);
(*peer->funcs->destruct)(peer);
tr_free(peer);
}
void tr_peerConstruct(tr_peer* peer, tr_torrent const* tor)
{
TR_ASSERT(peer != nullptr);
TR_ASSERT(tr_isTorrent(tor));
*peer = {};
peer->client = TR_KEY_NONE;
peer->swarm = tor->swarm;
tr_bitfieldConstruct(&peer->have, tor->info.pieceCount);
tr_bitfieldConstruct(&peer->blame, tor->blockCount);
tr_bitfieldConstruct(&have, tor->info.pieceCount);
tr_bitfieldConstruct(&blame, tor->blockCount);
}
static void peerDeclinedAllRequests(tr_swarm*, tr_peer const*);
void tr_peerDestruct(tr_peer* peer)
tr_peer::~tr_peer()
{
TR_ASSERT(peer != nullptr);
if (peer->swarm != nullptr)
if (swarm != nullptr)
{
peerDeclinedAllRequests(peer->swarm, peer);
peerDeclinedAllRequests(swarm, this);
}
tr_bitfieldDestruct(&peer->have);
tr_bitfieldDestruct(&peer->blame);
tr_bitfieldDestruct(&have);
tr_bitfieldDestruct(&blame);
if (peer->atom != nullptr)
if (atom != nullptr)
{
peer->atom->peer = nullptr;
atom->peer = nullptr;
}
}
@ -451,7 +428,7 @@ static void swarmFree(void* vs)
TR_ASSERT(tr_ptrArrayEmpty(&s->outgoingHandshakes));
TR_ASSERT(tr_ptrArrayEmpty(&s->peers));
tr_ptrArrayDestruct(&s->webseeds, (PtrArrayForeachFunc)tr_peerFree);
tr_ptrArrayDestruct(&s->webseeds, [](void* peer) { delete static_cast<tr_peer*>(peer); });
tr_ptrArrayDestruct(&s->pool, (PtrArrayForeachFunc)tr_free);
tr_ptrArrayDestruct(&s->outgoingHandshakes, nullptr);
tr_ptrArrayDestruct(&s->peers, nullptr);
@ -471,14 +448,14 @@ static void rebuildWebseedArray(tr_swarm* s, tr_torrent* tor)
tr_info const* inf = &tor->info;
/* clear the array */
tr_ptrArrayDestruct(&s->webseeds, (PtrArrayForeachFunc)tr_peerFree);
tr_ptrArrayDestruct(&s->webseeds, [](void* peer) { delete static_cast<tr_peer*>(peer); });
s->webseeds = {};
s->stats.activeWebseedCount = 0;
/* repopulate it */
for (unsigned int i = 0; i < inf->webseedCount; ++i)
{
tr_webseed* w = tr_webseedNew(tor, inf->webseeds[i], peerCallbackFunc, s);
tr_peer* w = tr_webseedNew(tor, inf->webseeds[i], peerCallbackFunc, s);
tr_ptrArrayAppend(&s->webseeds, w);
}
}
@ -781,11 +758,7 @@ static int countActiveWebseeds(tr_swarm* s)
for (int i = 0, n = tr_ptrArraySize(&s->webseeds); i < n; ++i)
{
if (tr_peerIsTransferringPieces(
static_cast<tr_peer const*>(tr_ptrArrayNth(&s->webseeds, i)),
now,
TR_DOWN,
nullptr))
if (static_cast<tr_peer const*>(tr_ptrArrayNth(&s->webseeds, i))->is_transferring_pieces(now, TR_DOWN, nullptr))
{
++activeCount;
}
@ -1512,9 +1485,9 @@ static void refillUpkeep(evutil_socket_t fd, short what, void* vmgr)
for (int i = 0; i < n; ++i)
{
struct block_request const* const request = &s->requests[i];
tr_peerMsgs const* const msgs = PEER_MSGS(request->peer);
auto const* const msgs = dynamic_cast<tr_peerMsgs const*>(request->peer);
if (msgs != nullptr && request->sentAt <= too_old && !tr_peerMsgsIsReadingBlock(msgs, request->block))
if (msgs != nullptr && request->sentAt <= too_old && !msgs->is_reading_block(request->block))
{
TR_ASSERT(cancel != nullptr);
TR_ASSERT(cancelCount < cancel_buflen);
@ -1528,7 +1501,7 @@ static void refillUpkeep(evutil_socket_t fd, short what, void* vmgr)
s->requests[keepCount] = *request;
}
keepCount++;
++keepCount;
}
}
@ -1539,12 +1512,11 @@ static void refillUpkeep(evutil_socket_t fd, short what, void* vmgr)
for (int i = 0; i < cancelCount; ++i)
{
struct block_request const* const request = &cancel[i];
tr_peerMsgs* msgs = PEER_MSGS(request->peer);
auto* msgs = dynamic_cast<tr_peerMsgs*>(request->peer);
if (msgs != nullptr)
{
tr_historyAdd(&request->peer->cancelsSentToPeer, now, 1);
tr_peerMsgsCancel(msgs, request->block);
request->peer->cancelsSentToPeer.add(now, 1);
msgs->cancel_block_request(request->block);
decrementPendingReqCount(request);
}
}
@ -1671,12 +1643,15 @@ static void peerDeclinedAllRequests(tr_swarm* s, tr_peer const* peer)
static void cancelAllRequestsForBlock(tr_swarm* s, tr_block_index_t block, tr_peer* no_notify)
{
auto const now = tr_time();
for (auto* p : getBlockRequestPeers(s, block))
{
if (p != no_notify && tr_isPeerMsgs(p))
auto* msgs = dynamic_cast<tr_peerMsgs*>(p);
if ((msgs != nullptr) && (msgs != no_notify))
{
tr_historyAdd(&p->cancelsSentToPeer, tr_time(), 1);
tr_peerMsgsCancel(PEER_MSGS(p), block);
msgs->cancelsSentToPeer.add(now, 1);
msgs->cancel_block_request(block);
}
removeRequestFromTables(s, block, p);
@ -1691,10 +1666,10 @@ void tr_peerMgrPieceCompleted(tr_torrent* tor, tr_piece_index_t p)
/* walk through our peers */
for (int i = 0, n = tr_ptrArraySize(&s->peers); i < n; ++i)
{
auto* peer = static_cast<tr_peer*>(tr_ptrArrayNth(&s->peers, i));
auto* peer = static_cast<tr_peerMsgs*>(tr_ptrArrayNth(&s->peers, i));
/* notify the peer that we now have this piece */
tr_peerMsgsHave(PEER_MSGS(peer), p);
// notify the peer that we now have this piece
peer->on_piece_completed(p);
if (!pieceCameFromPeers)
{
@ -1835,7 +1810,7 @@ static void peerCallbackFunc(tr_peer* peer, tr_peer_event const* e, void* vs)
tr_piece_index_t const p = e->pieceIndex;
tr_block_index_t const block = _tr_block(tor, p, e->offset);
cancelAllRequestsForBlock(s, block, peer);
tr_historyAdd(&peer->blocksSentToClient, tr_time(), 1);
peer->blocksSentToClient.add(tr_time(), 1);
pieceListResortPiece(s, pieceListLookup(s, p));
tr_torrentGotBlock(tor, block);
break;
@ -1957,8 +1932,7 @@ static void createBitTorrentPeer(tr_torrent* tor, struct tr_peerIo* io, struct p
tr_swarm* swarm = tor->swarm;
tr_peer* peer = (tr_peer*)tr_peerMsgsNew(tor, io, peerCallbackFunc, swarm);
peer->atom = atom;
auto* peer = tr_peerMsgsNew(tor, atom, io, peerCallbackFunc, swarm);
peer->client = client;
atom->peer = peer;
@ -1969,9 +1943,10 @@ static void createBitTorrentPeer(tr_torrent* tor, struct tr_peerIo* io, struct p
TR_ASSERT(swarm->stats.peerCount == tr_ptrArraySize(&swarm->peers));
TR_ASSERT(swarm->stats.peerFromCount[atom->fromFirst] <= swarm->stats.peerCount);
tr_peerMsgs* msgs = PEER_MSGS(peer);
tr_peerMsgsUpdateActive(msgs, TR_UP);
tr_peerMsgsUpdateActive(msgs, TR_DOWN);
// TODO is this needed?
// isn't it already initialized in tr_peerMsgsImpl's ctor?
peer->update_active(TR_UP);
peer->update_active(TR_DOWN);
}
/* FIXME: this is kind of a mess. */
@ -2597,8 +2572,9 @@ void tr_peerMgrOnTorrentGotMetainfo(tr_torrent* tor)
/* update the bittorrent peers' willingnes... */
for (int i = 0; i < peerCount; ++i)
{
tr_peerMsgsUpdateActive(tr_peerMsgsCast(peers[i]), TR_UP);
tr_peerMsgsUpdateActive(tr_peerMsgsCast(peers[i]), TR_DOWN);
auto* msgs = static_cast<tr_peerMsgs*>(peers[i]);
msgs->update_active(TR_UP);
msgs->update_active(TR_DOWN);
}
}
@ -2759,7 +2735,7 @@ double* tr_peerMgrWebSpeeds_KBps(tr_torrent const* tor)
unsigned int Bps = 0;
auto const* const peer = static_cast<tr_peer*>(tr_ptrArrayNth(&s->webseeds, i));
if (tr_peerIsTransferringPieces(peer, now, TR_DOWN, &Bps))
if (peer->is_transferring_pieces(now, TR_DOWN, &Bps))
{
ret[i] = Bps / (double)tr_speed_K;
}
@ -2789,7 +2765,7 @@ struct tr_peer_stat* tr_peerMgrPeerStats(tr_torrent const* tor, int* setmeCount)
{
char* pch;
tr_peer* peer = peers[i];
tr_peerMsgs const* const msgs = PEER_MSGS(peer);
auto const* const msgs = dynamic_cast<tr_peerMsgs const*>(peer);
struct peer_atom const* atom = peer->atom;
tr_peer_stat* stat = ret + i;
@ -2798,23 +2774,23 @@ struct tr_peer_stat* tr_peerMgrPeerStats(tr_torrent const* tor, int* setmeCount)
stat->port = ntohs(peer->atom->port);
stat->from = atom->fromFirst;
stat->progress = peer->progress;
stat->isUTP = tr_peerMsgsIsUtpConnection(msgs);
stat->isEncrypted = tr_peerMsgsIsEncrypted(msgs);
stat->isUTP = msgs->is_utp_connection();
stat->isEncrypted = msgs->is_encrypted();
stat->rateToPeer_KBps = toSpeedKBps(tr_peerGetPieceSpeed_Bps(peer, now_msec, TR_CLIENT_TO_PEER));
stat->rateToClient_KBps = toSpeedKBps(tr_peerGetPieceSpeed_Bps(peer, now_msec, TR_PEER_TO_CLIENT));
stat->peerIsChoked = tr_peerMsgsIsPeerChoked(msgs);
stat->peerIsInterested = tr_peerMsgsIsPeerInterested(msgs);
stat->clientIsChoked = tr_peerMsgsIsClientChoked(msgs);
stat->clientIsInterested = tr_peerMsgsIsClientInterested(msgs);
stat->isIncoming = tr_peerMsgsIsIncomingConnection(msgs);
stat->isDownloadingFrom = tr_peerMsgsIsActive(msgs, TR_PEER_TO_CLIENT);
stat->isUploadingTo = tr_peerMsgsIsActive(msgs, TR_CLIENT_TO_PEER);
stat->peerIsChoked = msgs->is_peer_choked();
stat->peerIsInterested = msgs->is_peer_interested();
stat->clientIsChoked = msgs->is_client_choked();
stat->clientIsInterested = msgs->is_client_interested();
stat->isIncoming = msgs->is_incoming_connection();
stat->isDownloadingFrom = msgs->is_active(TR_PEER_TO_CLIENT);
stat->isUploadingTo = msgs->is_active(TR_CLIENT_TO_PEER);
stat->isSeed = tr_peerIsSeed(peer);
stat->blocksToPeer = tr_historyGet(&peer->blocksSentToPeer, now, CANCEL_HISTORY_SEC);
stat->blocksToClient = tr_historyGet(&peer->blocksSentToClient, now, CANCEL_HISTORY_SEC);
stat->cancelsToPeer = tr_historyGet(&peer->cancelsSentToPeer, now, CANCEL_HISTORY_SEC);
stat->cancelsToClient = tr_historyGet(&peer->cancelsSentToClient, now, CANCEL_HISTORY_SEC);
stat->blocksToPeer = peer->blocksSentToPeer.count(now, CANCEL_HISTORY_SEC);
stat->blocksToClient = peer->blocksSentToClient.count(now, CANCEL_HISTORY_SEC);
stat->cancelsToPeer = peer->cancelsSentToPeer.count(now, CANCEL_HISTORY_SEC);
stat->cancelsToClient = peer->cancelsSentToClient.count(now, CANCEL_HISTORY_SEC);
stat->pendingReqsToPeer = peer->pendingReqsToPeer;
stat->pendingReqsToClient = peer->pendingReqsToClient;
@ -2900,7 +2876,7 @@ void tr_peerMgrClearInterest(tr_torrent* tor)
for (int i = 0; i < peerCount; ++i)
{
tr_peerMsgsSetInterested(static_cast<tr_peerMsgs*>(tr_ptrArrayNth(&s->peers, i)), false);
static_cast<tr_peerMsgs*>(tr_ptrArrayNth(&s->peers, i))->set_interested(false);
}
}
@ -2936,7 +2912,7 @@ enum tr_rechoke_state
struct tr_rechoke_info
{
tr_peer* peer;
tr_peerMsgs* peer;
int salt;
int rechoke_state;
};
@ -2998,8 +2974,8 @@ static void rechokeDownloads(tr_swarm* s)
for (int i = 0; i < peerCount; ++i)
{
auto const* const peer = static_cast<tr_peer const*>(tr_ptrArrayNth(&s->peers, i));
int const b = tr_historyGet(&peer->blocksSentToClient, now, CANCEL_HISTORY_SEC);
int const c = tr_historyGet(&peer->cancelsSentToPeer, now, CANCEL_HISTORY_SEC);
auto const b = peer->blocksSentToClient.count(now, CANCEL_HISTORY_SEC);
auto const c = peer->cancelsSentToPeer.count(now, CANCEL_HISTORY_SEC);
if (b == 0) /* ignore unresponsive peers, as described above */
{
@ -3072,17 +3048,17 @@ static void rechokeDownloads(tr_swarm* s)
/* decide WHICH peers to be interested in (based on their cancel-to-block ratio) */
for (int i = 0; i < peerCount; ++i)
{
auto* const peer = static_cast<tr_peer*>(tr_ptrArrayNth(&s->peers, i));
auto* const peer = static_cast<tr_peerMsgs*>(tr_ptrArrayNth(&s->peers, i));
if (!isPeerInteresting(s->tor, piece_is_interesting, peer))
{
tr_peerMsgsSetInterested(PEER_MSGS(peer), false);
peer->set_interested(false);
}
else
{
tr_rechoke_state rechoke_state;
int const blocks = tr_historyGet(&peer->blocksSentToClient, now, CANCEL_HISTORY_SEC);
int const cancels = tr_historyGet(&peer->cancelsSentToPeer, now, CANCEL_HISTORY_SEC);
auto const blocks = peer->blocksSentToClient.count(now, CANCEL_HISTORY_SEC);
auto const cancels = peer->cancelsSentToPeer.count(now, CANCEL_HISTORY_SEC);
if (blocks == 0 && cancels == 0)
{
@ -3131,7 +3107,7 @@ static void rechokeDownloads(tr_swarm* s)
for (int i = 0; i < rechoke_count; ++i)
{
tr_peerMsgsSetInterested(PEER_MSGS(rechoke[i].peer), i < s->interestedCount);
rechoke[i].peer->set_interested(i < s->interestedCount);
}
/* cleanup */
@ -3178,7 +3154,7 @@ static int compareChoke(void const* va, void const* vb)
/* is this a new connection? */
static bool isNew(tr_peerMsgs const* msgs)
{
return msgs != nullptr && tr_peerMsgsGetConnectionAge(msgs) < 45;
return msgs != nullptr && msgs->get_connection_age() < 45;
}
/* get a rate for deciding which peers to choke and unchoke. */
@ -3248,27 +3224,25 @@ static void rechokeUploads(tr_swarm* s, uint64_t const now)
/* sort the peers by preference and rate */
for (int i = 0; i < peerCount; ++i)
{
tr_peer* peer = peers[i];
tr_peerMsgs* msgs = PEER_MSGS(peer);
struct peer_atom* atom = peer->atom;
auto* const peer = dynamic_cast<tr_peerMsgs*>(peers[i]);
struct peer_atom* const atom = peer->atom;
if (tr_peerIsSeed(peer))
{
/* choke seeds and partial seeds */
tr_peerMsgsSetChoke(PEER_MSGS(peer), true);
peer->set_choke(true);
}
else if (chokeAll)
{
/* choke everyone if we're not uploading */
tr_peerMsgsSetChoke(PEER_MSGS(peer), true);
peer->set_choke(true);
}
else if (msgs != s->optimistic)
else if (peer != s->optimistic)
{
struct ChokeData* n = &choke[size++];
n->msgs = msgs;
n->isInterested = tr_peerMsgsIsPeerInterested(msgs);
n->wasChoked = tr_peerMsgsIsPeerChoked(msgs);
n->msgs = peer;
n->isInterested = peer->is_peer_interested();
n->wasChoked = peer->is_peer_choked();
n->rate = getRate(s->tor, atom, now);
n->salt = tr_rand_int_weak(INT_MAX);
n->isChoked = true;
@ -3338,7 +3312,7 @@ static void rechokeUploads(tr_swarm* s, uint64_t const now)
for (int i = 0; i < size; ++i)
{
tr_peerMsgsSetChoke(choke[i].msgs, choke[i].isChoked);
choke[i].msgs->set_choke(choke[i].isChoked);
}
/* cleanup */
@ -3426,7 +3400,7 @@ static tr_peer** getPeersToClose(tr_swarm* s, time_t const now_sec, int* setmeSi
int peerCount;
int outsize = 0;
struct tr_peer** ret = nullptr;
tr_peer** ret = nullptr;
tr_peer** peers = (tr_peer**)tr_ptrArrayPeek(&s->peers, &peerCount);
for (int i = 0; i < peerCount; ++i)
@ -3527,7 +3501,7 @@ static void removePeer(tr_swarm* s, tr_peer* peer)
TR_ASSERT(s->stats.peerCount == tr_ptrArraySize(&s->peers));
TR_ASSERT(s->stats.peerFromCount[atom->fromFirst] >= 0);
tr_peerFree(peer);
delete peer;
}
static void closePeer(tr_swarm* s, tr_peer* peer)
@ -3570,7 +3544,7 @@ static void closeBadPeers(tr_swarm* s, time_t const now_sec)
if (!tr_ptrArrayEmpty(&s->peers))
{
int peerCount;
struct tr_peer** peers;
tr_peer** peers;
peers = getPeersToClose(s, now_sec, &peerCount);
@ -3793,7 +3767,7 @@ static void pumpAllPeers(tr_peerMgr* mgr)
for (int j = 0, n = tr_ptrArraySize(&s->peers); j < n; ++j)
{
tr_peerMsgsPulse(static_cast<tr_peerMsgs*>(tr_ptrArrayNth(&s->peers, j)));
static_cast<tr_peerMsgs*>(tr_ptrArrayNth(&s->peers, j))->pulse();
}
}
}

View File

@ -28,6 +28,7 @@
* @{
*/
class tr_peerMsgs;
struct UTPSocket;
struct peer_atom;
struct tr_peerIo;

File diff suppressed because it is too large Load Diff

View File

@ -15,9 +15,9 @@
#include <inttypes.h>
#include "peer-common.h"
class tr_peer;
struct tr_address;
struct tr_bitfield;
struct tr_peer;
struct tr_peerIo;
struct tr_torrent;
@ -26,47 +26,47 @@ struct tr_torrent;
* @{
*/
struct tr_peerMsgs;
class tr_peerMsgs : public tr_peer
{
public:
tr_peerMsgs(tr_torrent* torrent, peer_atom* atom_in)
: tr_peer{ torrent, atom_in }
{
}
#define PEER_MSGS(o) (tr_peerMsgsCast(o))
virtual ~tr_peerMsgs() override = default;
bool tr_isPeerMsgs(void const* msgs);
virtual bool is_peer_choked() const = 0;
virtual bool is_peer_interested() const = 0;
virtual bool is_client_choked() const = 0;
virtual bool is_client_interested() const = 0;
tr_peerMsgs* tr_peerMsgsCast(void* msgs);
virtual bool is_utp_connection() const = 0;
virtual bool is_encrypted() const = 0;
virtual bool is_incoming_connection() const = 0;
tr_peerMsgs* tr_peerMsgsNew(struct tr_torrent* torrent, struct tr_peerIo* io, tr_peer_callback callback, void* callback_data);
virtual bool is_active(tr_direction direction) const = 0;
virtual void update_active(tr_direction direction) = 0;
bool tr_peerMsgsIsPeerChoked(tr_peerMsgs const* msgs);
virtual time_t get_connection_age() const = 0;
virtual bool is_reading_block(tr_block_index_t block) const = 0;
bool tr_peerMsgsIsPeerInterested(tr_peerMsgs const* msgs);
virtual void cancel_block_request(tr_block_index_t block) = 0;
bool tr_peerMsgsIsClientChoked(tr_peerMsgs const* msgs);
virtual void set_choke(bool peer_is_choked) = 0;
virtual void set_interested(bool client_is_interested) = 0;
bool tr_peerMsgsIsClientInterested(tr_peerMsgs const* msgs);
virtual void pulse() = 0;
bool tr_peerMsgsIsActive(tr_peerMsgs const* msgs, tr_direction direction);
virtual void on_piece_completed(tr_piece_index_t) = 0;
};
void tr_peerMsgsUpdateActive(tr_peerMsgs* msgs, tr_direction direction);
time_t tr_peerMsgsGetConnectionAge(tr_peerMsgs const* msgs);
bool tr_peerMsgsIsUtpConnection(tr_peerMsgs const* msgs);
bool tr_peerMsgsIsEncrypted(tr_peerMsgs const* msgs);
bool tr_peerMsgsIsIncomingConnection(tr_peerMsgs const* msgs);
void tr_peerMsgsSetChoke(tr_peerMsgs* msgs, bool peerIsChoked);
bool tr_peerMsgsIsReadingBlock(tr_peerMsgs const* msgs, tr_block_index_t block);
void tr_peerMsgsSetInterested(tr_peerMsgs* msgs, bool clientIsInterested);
void tr_peerMsgsHave(tr_peerMsgs* msgs, uint32_t pieceIndex);
void tr_peerMsgsPulse(tr_peerMsgs* msgs);
void tr_peerMsgsCancel(tr_peerMsgs* msgs, tr_block_index_t block);
tr_peerMsgs* tr_peerMsgsNew(
tr_torrent* torrent,
peer_atom* atom,
tr_peerIo* io,
tr_peer_callback callback,
void* callback_data);
size_t tr_generateAllowedSet(
tr_piece_index_t* setmePieces,

View File

@ -24,6 +24,11 @@
#include "web.h"
#include "webseed.h"
namespace
{
struct tr_webseed;
struct tr_webseed_task
{
bool dead;
@ -40,36 +45,83 @@ struct tr_webseed_task
long response_code;
};
struct tr_webseed
auto constexpr TR_IDLE_TIMER_MSEC = 2000;
auto constexpr FAILURE_RETRY_INTERVAL = 150;
auto constexpr MAX_CONSECUTIVE_FAILURES = 5;
auto constexpr MAX_WEBSEED_CONNECTIONS = 4;
void webseed_timer_func(evutil_socket_t fd, short what, void* vw);
struct tr_webseed : public tr_peer
{
tr_peer parent;
tr_bandwidth bandwidth;
tr_session* session;
tr_peer_callback callback;
void* callback_data;
public:
tr_webseed(struct tr_torrent* tor, std::string_view url, tr_peer_callback callback_in, void* callback_data_in)
: tr_peer{ tor }
, torrent_id{ tr_torrentId(tor) }
, base_url{ url }
, callback{ callback_in }
, callback_data{ callback_data_in }
{
// init parent bits
tr_bitfieldSetHasAll(&have);
tr_peerUpdateProgress(tor, this);
file_urls.resize(tr_torrentInfo(tor)->fileCount);
tr_bandwidthConstruct(&bandwidth, &tor->bandwidth);
timer = evtimer_new(session->event_base, webseed_timer_func, this);
tr_timerAddMsec(timer, TR_IDLE_TIMER_MSEC);
}
~tr_webseed() override
{
// flag all the pending tasks as dead
std::for_each(std::begin(tasks), std::end(tasks), [](auto* task) { task->dead = true; });
tasks.clear();
event_free(timer);
tr_bandwidthDestruct(&bandwidth);
}
bool is_transferring_pieces(uint64_t now, tr_direction direction, unsigned int* setme_Bps) const override
{
unsigned int Bps = 0;
bool is_active = false;
if (direction == TR_DOWN)
{
is_active = !std::empty(tasks);
Bps = tr_bandwidthGetPieceSpeed_Bps(&bandwidth, now, direction);
}
if (setme_Bps != nullptr)
{
*setme_Bps = Bps;
}
return is_active;
}
int const torrent_id;
std::string const base_url;
tr_peer_callback const callback;
void* const callback_data;
tr_bandwidth bandwidth = {};
std::set<tr_webseed_task*> tasks;
struct event* timer;
char* base_url;
size_t base_url_len;
int torrent_id;
int consecutive_failures;
int retry_tickcount;
int retry_challenge;
int idle_connections;
int active_transfers;
char** file_urls;
struct event* timer = nullptr;
int consecutive_failures = 0;
int retry_tickcount = 0;
int retry_challenge = 0;
int idle_connections = 0;
int active_transfers = 0;
std::vector<std::string> file_urls;
};
enum
{
TR_IDLE_TIMER_MSEC = 2000,
/* */
FAILURE_RETRY_INTERVAL = 150,
/* */
MAX_CONSECUTIVE_FAILURES = 5,
/* */
MAX_WEBSEED_CONNECTIONS = 4
};
} // namespace
/***
****
@ -79,7 +131,7 @@ static void publish(tr_webseed* w, tr_peer_event* e)
{
if (w->callback != nullptr)
{
(*w->callback)(&w->parent, e, w->callback_data);
(*w->callback)(w, e, w->callback_data);
}
}
@ -210,8 +262,7 @@ static void connection_succeeded(void* vdata)
tr_file_index_t file_index;
tr_ioFindFileLocation(tor, data->piece_index, data->piece_offset, &file_index, &file_offset);
tr_free(w->file_urls[file_index]);
w->file_urls[file_index] = data->real_url;
w->file_urls[file_index].assign(data->real_url);
data->real_url = nullptr;
}
}
@ -228,7 +279,7 @@ static void on_content_changed(struct evbuffer* buf, struct evbuffer_cb_info con
{
size_t const n_added = info->n_added;
auto* task = static_cast<struct tr_webseed_task*>(vtask);
tr_session* session = task->session;
auto* session = task->session;
tr_sessionLock(session);
@ -256,7 +307,7 @@ static void on_content_changed(struct evbuffer* buf, struct evbuffer_cb_info con
/* processing this uses a tr_torrent pointer,
so push the work to the libevent thread... */
tr_runInEventThread(w->session, connection_succeeded, data);
tr_runInEventThread(session, connection_succeeded, data);
}
}
@ -264,10 +315,10 @@ static void on_content_changed(struct evbuffer* buf, struct evbuffer_cb_info con
{
/* once we've got at least one full block, save it */
struct write_block_data* data;
uint32_t const block_size = task->block_size;
tr_block_index_t const completed = len / block_size;
struct write_block_data* data;
data = tr_new(struct write_block_data, 1);
data->webseed = task->webseed;
data->piece_index = task->piece_index;
@ -322,7 +373,7 @@ static void on_idle(tr_webseed* w)
tr_block_index_t* blocks = nullptr;
blocks = tr_new(tr_block_index_t, want * 2);
tr_peerMgrGetNextRequests(tor, &w->parent, want, blocks, &got, true);
tr_peerMgrGetNextRequests(tor, w, want, blocks, &got, true);
w->idle_connections -= std::min(w->idle_connections, got);
@ -450,19 +501,21 @@ static void web_response_func(
}
}
static struct evbuffer* make_url(tr_webseed* w, tr_file const* file)
static std::string make_url(tr_webseed* w, tr_file const* file)
{
struct evbuffer* buf = evbuffer_new();
evbuffer_add(buf, w->base_url, w->base_url_len);
evbuffer_add(buf, std::data(w->base_url), std::size(w->base_url));
/* if url ends with a '/', add the torrent name */
if (w->base_url[w->base_url_len - 1] == '/' && file->name != nullptr)
if (*std::rbegin(w->base_url) == '/' && file->name != nullptr)
{
tr_http_escape(buf, file->name, strlen(file->name), false);
}
return buf;
auto const url = std::string{ (char const*)evbuffer_pullup(buf, -1), evbuffer_get_length(buf) };
evbuffer_free(buf);
return url;
}
static void task_request_next_chunk(struct tr_webseed_task* t)
@ -472,8 +525,7 @@ static void task_request_next_chunk(struct tr_webseed_task* t)
if (tor != nullptr)
{
char range[64];
char** urls = t->webseed->file_urls;
auto& urls = t->webseed->file_urls;
tr_info const* inf = tr_torrentInfo(tor);
uint64_t const remain = t->length - t->blocks_done * tor->blockSize - evbuffer_get_length(t->content);
@ -491,14 +543,15 @@ static void task_request_next_chunk(struct tr_webseed_task* t)
file = &inf->files[file_index];
this_pass = std::min(remain, file->length - file_offset);
if (urls[file_index] == nullptr)
if (std::empty(urls[file_index]))
{
urls[file_index] = evbuffer_free_to_str(make_url(t->webseed, file), nullptr);
urls[file_index] = make_url(t->webseed, file);
}
char range[64];
tr_snprintf(range, sizeof(range), "%" PRIu64 "-%" PRIu64, file_offset, file_offset + this_pass - 1);
t->web_task = tr_webRunWebseed(tor, urls[file_index], range, web_response_func, t, t->content);
t->web_task = tr_webRunWebseed(tor, urls[file_index].c_str(), range, web_response_func, t, t->content);
}
}
@ -506,7 +559,10 @@ static void task_request_next_chunk(struct tr_webseed_task* t)
****
***/
static void webseed_timer_func(evutil_socket_t fd, short what, void* vw)
namespace
{
void webseed_timer_func(evutil_socket_t fd, short what, void* vw)
{
TR_UNUSED(fd);
TR_UNUSED(what);
@ -523,99 +579,9 @@ static void webseed_timer_func(evutil_socket_t fd, short what, void* vw)
tr_timerAddMsec(w->timer, TR_IDLE_TIMER_MSEC);
}
/***
**** tr_peer virtual functions
***/
} // unnamed namespace
static bool webseed_is_transferring_pieces(tr_peer const* peer, uint64_t now, tr_direction direction, unsigned int* setme_Bps)
tr_peer* tr_webseedNew(struct tr_torrent* torrent, std::string_view url, tr_peer_callback callback, void* callback_data)
{
unsigned int Bps = 0;
bool is_active = false;
if (direction == TR_DOWN)
{
tr_webseed const* w = (tr_webseed const*)peer;
is_active = !std::empty(w->tasks);
Bps = tr_bandwidthGetPieceSpeed_Bps(&w->bandwidth, now, direction);
}
if (setme_Bps != nullptr)
{
*setme_Bps = Bps;
}
return is_active;
}
static void webseed_destruct(tr_peer* peer)
{
tr_webseed* w = (tr_webseed*)peer;
/* flag all the pending tasks as dead */
auto& src = w->tasks;
std::for_each(std::begin(src), std::end(src), [](auto* task) { task->dead = true; });
// Manually destructing is unfortunately necessary until we C++ify
// the tr_peer / tr_peerMsgs / tr_webseed inheritance. Peers are
// curently tr_free()d in tr_peerFree() so we can't new/delete them.
using type = decltype(w->tasks);
w->tasks.~type();
/* if we have an array of file URLs, free it */
if (w->file_urls != nullptr)
{
tr_torrent const* const tor = tr_torrentFindFromId(w->session, w->torrent_id);
tr_info const* const inf = tr_torrentInfo(tor);
for (tr_file_index_t i = 0; i < inf->fileCount; ++i)
{
tr_free(w->file_urls[i]);
}
tr_free(w->file_urls);
}
/* webseed destruct */
event_free(w->timer);
tr_bandwidthDestruct(&w->bandwidth);
tr_free(w->base_url);
/* parent class destruct */
tr_peerDestruct(&w->parent);
}
static struct tr_peer_virtual_funcs const my_funcs = {
webseed_destruct,
webseed_is_transferring_pieces,
};
/***
****
***/
tr_webseed* tr_webseedNew(struct tr_torrent* tor, char const* url, tr_peer_callback callback, void* callback_data)
{
tr_webseed* w = tr_new0(tr_webseed, 1);
tr_peer* peer = &w->parent;
tr_info const* inf = tr_torrentInfo(tor);
/* construct parent class */
tr_peerConstruct(peer, tor);
peer->client = TR_KEY_webseeds;
peer->funcs = &my_funcs;
tr_bitfieldSetHasAll(&peer->have);
tr_peerUpdateProgress(tor, peer);
using type = decltype(w->tasks);
new (&w->tasks) type;
w->torrent_id = tr_torrentId(tor);
w->session = tor->session;
w->base_url_len = strlen(url);
w->base_url = tr_strndup(url, w->base_url_len);
w->callback = callback;
w->callback_data = callback_data;
w->file_urls = tr_new0(char*, inf->fileCount);
tr_bandwidthConstruct(&w->bandwidth, &tor->bandwidth);
w->timer = evtimer_new(w->session->event_base, webseed_timer_func, w);
tr_timerAddMsec(w->timer, TR_IDLE_TIMER_MSEC);
return w;
return new tr_webseed(torrent, url, callback, callback_data);
}

View File

@ -12,8 +12,8 @@
#error only libtransmission should #include this header.
#endif
struct tr_webseed;
#include <string_view>
#include "peer-common.h"
tr_webseed* tr_webseedNew(struct tr_torrent* torrent, char const* url, tr_peer_callback callback, void* callback_data);
tr_peer* tr_webseedNew(struct tr_torrent* torrent, std::string_view, tr_peer_callback callback, void* callback_data);

View File

@ -15,13 +15,13 @@ TEST(History, recentHistory)
{
auto h = tr_recentHistory{};
tr_historyAdd(&h, 10000, 1);
EXPECT_EQ(0, tr_historyGet(&h, 12000, 1000));
EXPECT_EQ(1, tr_historyGet(&h, 12000, 3000));
EXPECT_EQ(1, tr_historyGet(&h, 12000, 5000));
tr_historyAdd(&h, 20000, 1);
EXPECT_EQ(0, tr_historyGet(&h, 22000, 1000));
EXPECT_EQ(1, tr_historyGet(&h, 22000, 3000));
EXPECT_EQ(2, tr_historyGet(&h, 22000, 15000));
EXPECT_EQ(2, tr_historyGet(&h, 22000, 20000));
h.add(10000, 1);
EXPECT_EQ(0, h.count(12000, 1000));
EXPECT_EQ(1, h.count(12000, 3000));
EXPECT_EQ(1, h.count(12000, 5000));
h.add(20000, 1);
EXPECT_EQ(0, h.count(22000, 1000));
EXPECT_EQ(1, h.count(22000, 3000));
EXPECT_EQ(2, h.count(22000, 15000));
EXPECT_EQ(2, h.count(22000, 20000));
}