mirror of
https://github.com/transmission/transmission.git
synced 2025-12-24 04:18:39 +00:00
refactor: add tr_announcer.startShutdown() (#4280)
This commit is contained in:
@@ -32,7 +32,6 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "peer-io.h"
|
#include "peer-io.h"
|
||||||
#include "peer-mgr.h" // for tr_pex::fromCompact4()
|
#include "peer-mgr.h" // for tr_pex::fromCompact4()
|
||||||
#include "session.h"
|
|
||||||
#include "tr-assert.h"
|
#include "tr-assert.h"
|
||||||
#include "tr-buffer.h"
|
#include "tr-buffer.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
@@ -300,11 +299,6 @@ struct tau_tracker
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto isIdle() const noexcept
|
|
||||||
{
|
|
||||||
return std::empty(announces) && std::empty(scrapes) && !addr_pending_dns_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendto(std::byte const* buf, size_t buflen)
|
void sendto(std::byte const* buf, size_t buflen)
|
||||||
{
|
{
|
||||||
TR_ASSERT(addr_);
|
TR_ASSERT(addr_);
|
||||||
@@ -341,7 +335,6 @@ struct tau_tracker
|
|||||||
void upkeep(bool timeout_reqs = true)
|
void upkeep(bool timeout_reqs = true)
|
||||||
{
|
{
|
||||||
time_t const now = tr_time();
|
time_t const now = tr_time();
|
||||||
bool const closing = this->close_at != 0;
|
|
||||||
|
|
||||||
// do we have a DNS request that's ready?
|
// do we have a DNS request that's ready?
|
||||||
if (addr_pending_dns_ && addr_pending_dns_->wait_for(0ms) == std::future_status::ready)
|
if (addr_pending_dns_ && addr_pending_dns_->wait_for(0ms) == std::future_status::ready)
|
||||||
@@ -358,7 +351,7 @@ struct tau_tracker
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update the addr if our lookup is past its shelf date
|
// update the addr if our lookup is past its shelf date
|
||||||
if (!closing && !addr_pending_dns_ && addr_expires_at_ <= now)
|
if (!addr_pending_dns_ && addr_expires_at_ <= now)
|
||||||
{
|
{
|
||||||
addr_.reset();
|
addr_.reset();
|
||||||
addr_pending_dns_ = std::async(std::launch::async, lookup, this->host, this->port, this->key);
|
addr_pending_dns_ = std::async(std::launch::async, lookup, this->host, this->port, this->key);
|
||||||
@@ -443,6 +436,11 @@ private:
|
|||||||
return std::make_pair(ss, len);
|
return std::make_pair(ss, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool isIdle() const noexcept
|
||||||
|
{
|
||||||
|
return std::empty(announces) && std::empty(scrapes) && !addr_pending_dns_;
|
||||||
|
}
|
||||||
|
|
||||||
void failAll(bool did_connect, bool did_timeout, std::string_view errmsg)
|
void failAll(bool did_connect, bool did_timeout, std::string_view errmsg)
|
||||||
{
|
{
|
||||||
for (auto& req : this->scrapes)
|
for (auto& req : this->scrapes)
|
||||||
@@ -463,24 +461,22 @@ private:
|
|||||||
|
|
||||||
void timeout_requests(time_t now)
|
void timeout_requests(time_t now)
|
||||||
{
|
{
|
||||||
bool const cancel_all = this->close_at != 0 && (this->close_at <= now);
|
|
||||||
|
|
||||||
if (this->connecting_at != 0 && this->connecting_at + ConnectionRequestTtl < now)
|
if (this->connecting_at != 0 && this->connecting_at + ConnectionRequestTtl < now)
|
||||||
{
|
{
|
||||||
auto empty_buf = libtransmission::Buffer{};
|
auto empty_buf = libtransmission::Buffer{};
|
||||||
on_connection_response(TAU_ACTION_ERROR, empty_buf);
|
on_connection_response(TAU_ACTION_ERROR, empty_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout_requests(this->announces, now, cancel_all, "announce");
|
timeout_requests(this->announces, now, "announce");
|
||||||
timeout_requests(this->scrapes, now, cancel_all, "scrape");
|
timeout_requests(this->scrapes, now, "scrape");
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void timeout_requests(std::list<T>& requests, time_t now, bool cancel_all, std::string_view name)
|
void timeout_requests(std::list<T>& requests, time_t now, std::string_view name)
|
||||||
{
|
{
|
||||||
for (auto it = std::begin(requests); it != std::end(requests);)
|
for (auto it = std::begin(requests); it != std::end(requests);)
|
||||||
{
|
{
|
||||||
if (auto& req = *it; cancel_all || req.expiresAt() <= now)
|
if (auto& req = *it; req.expiresAt() <= now)
|
||||||
{
|
{
|
||||||
logtrace(this->key, fmt::format("timeout {} req {}", name, fmt::ptr(&req)));
|
logtrace(this->key, fmt::format("timeout {} req {}", name, fmt::ptr(&req)));
|
||||||
req.fail(false, true, "");
|
req.fail(false, true, "");
|
||||||
@@ -558,8 +554,6 @@ public:
|
|||||||
tau_connection_t connection_id = {};
|
tau_connection_t connection_id = {};
|
||||||
tau_transaction_t connection_transaction_id = {};
|
tau_transaction_t connection_transaction_id = {};
|
||||||
|
|
||||||
time_t close_at = 0;
|
|
||||||
|
|
||||||
std::list<tau_announce_request> announces;
|
std::list<tau_announce_request> announces;
|
||||||
std::list<tau_scrape_request> scrapes;
|
std::list<tau_scrape_request> scrapes;
|
||||||
|
|
||||||
@@ -622,25 +616,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool isIdle() const noexcept override
|
|
||||||
{
|
|
||||||
return std::all_of(std::begin(trackers_), std::end(trackers_), [](auto const& tracker) { return tracker.isIdle(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start shutting down.
|
|
||||||
// This doesn't destroy everything if there are requests,
|
|
||||||
// but sets a deadline on how much longer to wait for the remaining ones.
|
|
||||||
void startShutdown() override
|
|
||||||
{
|
|
||||||
auto const now = time(nullptr);
|
|
||||||
|
|
||||||
for (auto& tracker : trackers_)
|
|
||||||
{
|
|
||||||
tracker.close_at = now + 3;
|
|
||||||
tracker.upkeep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @brief process an incoming udp message if it's a tracker response.
|
// @brief process an incoming udp message if it's a tracker response.
|
||||||
// @return true if msg was a tracker response; false otherwise
|
// @return true if msg was a tracker response; false otherwise
|
||||||
bool handleMessage(uint8_t const* msg, size_t msglen) override
|
bool handleMessage(uint8_t const* msg, size_t msglen) override
|
||||||
|
|||||||
@@ -160,6 +160,17 @@ public:
|
|||||||
void resetTorrent(tr_torrent* tor) override;
|
void resetTorrent(tr_torrent* tor) override;
|
||||||
void removeTorrent(tr_torrent* tor) override;
|
void removeTorrent(tr_torrent* tor) override;
|
||||||
|
|
||||||
|
void startShutdown() override
|
||||||
|
{
|
||||||
|
is_shutting_down_ = true;
|
||||||
|
flushCloseMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t pendingAnnounces() const override
|
||||||
|
{
|
||||||
|
return pending_announces_;
|
||||||
|
}
|
||||||
|
|
||||||
void upkeep();
|
void upkeep();
|
||||||
|
|
||||||
void onAnnounceDone(int tier_id, tr_announce_event event, bool is_running_on_success, tr_announce_response const& response);
|
void onAnnounceDone(int tier_id, tr_announce_event event, bool is_running_on_success, tr_announce_response const& response);
|
||||||
@@ -178,6 +189,8 @@ public:
|
|||||||
|
|
||||||
void scrape(tr_scrape_request const& request, tr_scrape_response_func on_response)
|
void scrape(tr_scrape_request const& request, tr_scrape_response_func on_response)
|
||||||
{
|
{
|
||||||
|
TR_ASSERT(!is_shutting_down_);
|
||||||
|
|
||||||
if (auto const scrape_sv = request.scrape_url.sv();
|
if (auto const scrape_sv = request.scrape_url.sv();
|
||||||
tr_strvStartsWith(scrape_sv, "http://"sv) || tr_strvStartsWith(scrape_sv, "https://"sv))
|
tr_strvStartsWith(scrape_sv, "http://"sv) || tr_strvStartsWith(scrape_sv, "https://"sv))
|
||||||
{
|
{
|
||||||
@@ -195,14 +208,25 @@ public:
|
|||||||
|
|
||||||
void announce(tr_announce_request const& request, tr_announce_response_func on_response)
|
void announce(tr_announce_request const& request, tr_announce_response_func on_response)
|
||||||
{
|
{
|
||||||
|
TR_ASSERT(!is_shutting_down_ || request.event == TR_ANNOUNCE_EVENT_STOPPED);
|
||||||
|
|
||||||
|
auto callback = [this, on_response = std::move(on_response)](tr_announce_response const& response)
|
||||||
|
{
|
||||||
|
TR_ASSERT(pending_announces_ > 0U);
|
||||||
|
--pending_announces_;
|
||||||
|
on_response(response);
|
||||||
|
};
|
||||||
|
|
||||||
if (auto const announce_sv = request.announce_url.sv();
|
if (auto const announce_sv = request.announce_url.sv();
|
||||||
tr_strvStartsWith(announce_sv, "http://"sv) || tr_strvStartsWith(announce_sv, "https://"sv))
|
tr_strvStartsWith(announce_sv, "http://"sv) || tr_strvStartsWith(announce_sv, "https://"sv))
|
||||||
{
|
{
|
||||||
tr_tracker_http_announce(session, request, std::move(on_response));
|
++pending_announces_;
|
||||||
|
tr_tracker_http_announce(session, request, std::move(callback));
|
||||||
}
|
}
|
||||||
else if (tr_strvStartsWith(announce_sv, "udp://"sv))
|
else if (tr_strvStartsWith(announce_sv, "udp://"sv))
|
||||||
{
|
{
|
||||||
announcer_udp_.announce(request, std::move(on_response));
|
++pending_announces_;
|
||||||
|
announcer_udp_.announce(request, std::move(callback));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -225,6 +249,8 @@ private:
|
|||||||
stops_.clear();
|
stops_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static auto constexpr UpkeepInterval = 500ms;
|
||||||
|
|
||||||
tr_announcer_udp& announcer_udp_;
|
tr_announcer_udp& announcer_udp_;
|
||||||
|
|
||||||
std::map<tr_interned_string, tr_scrape_info> scrape_info_;
|
std::map<tr_interned_string, tr_scrape_info> scrape_info_;
|
||||||
@@ -233,7 +259,9 @@ private:
|
|||||||
|
|
||||||
std::set<tr_announce_request, StopsCompare> stops_;
|
std::set<tr_announce_request, StopsCompare> stops_;
|
||||||
|
|
||||||
static auto constexpr UpkeepInterval = 500ms;
|
size_t pending_announces_ = {};
|
||||||
|
|
||||||
|
bool is_shutting_down_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<tr_announcer> tr_announcer::create(tr_session* session, tr_announcer_udp& announcer_udp)
|
std::unique_ptr<tr_announcer> tr_announcer::create(tr_session* session, tr_announcer_udp& announcer_udp)
|
||||||
@@ -1546,7 +1574,7 @@ void tr_announcer_impl::upkeep()
|
|||||||
flushCloseMessages();
|
flushCloseMessages();
|
||||||
|
|
||||||
// maybe kick off some scrapes / announces whose time has come
|
// maybe kick off some scrapes / announces whose time has come
|
||||||
if (!session->isClosing())
|
if (!is_shutting_down_)
|
||||||
{
|
{
|
||||||
scrapeAndAnnounceMore(this);
|
scrapeAndAnnounceMore(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ public:
|
|||||||
virtual void stopTorrent(tr_torrent* tor) = 0;
|
virtual void stopTorrent(tr_torrent* tor) = 0;
|
||||||
virtual void resetTorrent(tr_torrent* tor) = 0;
|
virtual void resetTorrent(tr_torrent* tor) = 0;
|
||||||
virtual void removeTorrent(tr_torrent* tor) = 0;
|
virtual void removeTorrent(tr_torrent* tor) = 0;
|
||||||
|
|
||||||
|
virtual void startShutdown() = 0;
|
||||||
|
[[nodiscard]] virtual size_t pendingAnnounces() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<tr_announcer> tr_announcerCreate(tr_session* session);
|
std::unique_ptr<tr_announcer> tr_announcerCreate(tr_session* session);
|
||||||
@@ -141,16 +144,12 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] static std::unique_ptr<tr_announcer_udp> create(Mediator&);
|
[[nodiscard]] static std::unique_ptr<tr_announcer_udp> create(Mediator&);
|
||||||
|
|
||||||
[[nodiscard]] virtual bool isIdle() const noexcept = 0;
|
|
||||||
|
|
||||||
virtual void announce(tr_announce_request const& request, tr_announce_response_func on_response) = 0;
|
virtual void announce(tr_announce_request const& request, tr_announce_response_func on_response) = 0;
|
||||||
|
|
||||||
virtual void scrape(tr_scrape_request const& request, tr_scrape_response_func on_response) = 0;
|
virtual void scrape(tr_scrape_request const& request, tr_scrape_response_func on_response) = 0;
|
||||||
|
|
||||||
virtual void upkeep() = 0;
|
virtual void upkeep() = 0;
|
||||||
|
|
||||||
virtual void startShutdown() = 0;
|
|
||||||
|
|
||||||
// @brief process an incoming udp message if it's a tracker response.
|
// @brief process an incoming udp message if it's a tracker response.
|
||||||
// @return true if msg was a tracker response; false otherwise
|
// @return true if msg was a tracker response; false otherwise
|
||||||
virtual bool handleMessage(uint8_t const* msg, size_t msglen) = 0;
|
virtual bool handleMessage(uint8_t const* msg, size_t msglen) = 0;
|
||||||
|
|||||||
@@ -1224,9 +1224,6 @@ void tr_session::closeImplPart1(std::promise<void>* closed_promise)
|
|||||||
bound_ipv6_.reset();
|
bound_ipv6_.reset();
|
||||||
bound_ipv4_.reset();
|
bound_ipv4_.reset();
|
||||||
|
|
||||||
// tell other items to start shutting down
|
|
||||||
announcer_udp_->startShutdown();
|
|
||||||
|
|
||||||
// Close the torrents in order of most active to least active
|
// Close the torrents in order of most active to least active
|
||||||
// so that the most important announce=stopped events are
|
// so that the most important announce=stopped events are
|
||||||
// fired out first...
|
// fired out first...
|
||||||
@@ -1245,14 +1242,14 @@ void tr_session::closeImplPart1(std::promise<void>* closed_promise)
|
|||||||
tr_torrentFreeInSessionThread(tor);
|
tr_torrentFreeInSessionThread(tor);
|
||||||
}
|
}
|
||||||
torrents.clear();
|
torrents.clear();
|
||||||
// ...and now that all the torrents have been closed, any
|
// ...now that all the torrents have been closed, any remaining
|
||||||
// remaining `event=stopped` announce messages are queued in
|
// `&event=stopped` announce messages are queued in the announcer.
|
||||||
// the announcer. The announcer's destructor sends all those
|
// Tell the announcer to start shutdown, which sends out the stop
|
||||||
// out via `web_`...
|
// events and stops scraping.
|
||||||
this->announcer_.reset();
|
this->announcer_->startShutdown();
|
||||||
// ...and now that those are queued, tell web_ that we're
|
// ...and now that those are queued, tell web_ that we're shutting
|
||||||
// shutting down soon. This leaves the `event=stopped` messages
|
// down soon. This leaves the `event=stopped` going but refuses any
|
||||||
// in the queue but refuses to take any _new_ tasks
|
// new tasks.
|
||||||
this->web_->startShutdown();
|
this->web_->startShutdown();
|
||||||
this->cache.reset();
|
this->cache.reset();
|
||||||
|
|
||||||
@@ -1266,7 +1263,7 @@ void tr_session::closeImplPart2(std::promise<void>* closed_promise)
|
|||||||
{
|
{
|
||||||
// try to keep the UDP announcer alive long enough to send out
|
// try to keep the UDP announcer alive long enough to send out
|
||||||
// all the &event=stopped tracker announces
|
// all the &event=stopped tracker announces
|
||||||
if (announcer_udp_ && !announcer_udp_->isIdle())
|
if (announcer_->pendingAnnounces() != 0U)
|
||||||
{
|
{
|
||||||
announcer_udp_->upkeep();
|
announcer_udp_->upkeep();
|
||||||
return;
|
return;
|
||||||
@@ -1274,6 +1271,7 @@ void tr_session::closeImplPart2(std::promise<void>* closed_promise)
|
|||||||
|
|
||||||
save_timer_.reset();
|
save_timer_.reset();
|
||||||
|
|
||||||
|
this->announcer_.reset();
|
||||||
this->announcer_udp_.reset();
|
this->announcer_udp_.reset();
|
||||||
this->udp_core_.reset();
|
this->udp_core_.reset();
|
||||||
|
|
||||||
|
|||||||
@@ -310,22 +310,18 @@ TEST_F(AnnouncerUdpTest, canScrape)
|
|||||||
auto [request, expected_response] = buildSimpleScrapeRequestAndResponse();
|
auto [request, expected_response] = buildSimpleScrapeRequestAndResponse();
|
||||||
auto response = std::optional<tr_scrape_response>{};
|
auto response = std::optional<tr_scrape_response>{};
|
||||||
announcer->scrape(request, [&response](tr_scrape_response const& resp) { response = resp; });
|
announcer->scrape(request, [&response](tr_scrape_response const& resp) { response = resp; });
|
||||||
EXPECT_FALSE(announcer->isIdle());
|
|
||||||
|
|
||||||
// The announcer should have sent a UDP connection request.
|
// The announcer should have sent a UDP connection request.
|
||||||
// Inspect that request for validity.
|
// Inspect that request for validity.
|
||||||
auto sent = waitForAnnouncerToSendMessage(mediator);
|
auto sent = waitForAnnouncerToSendMessage(mediator);
|
||||||
EXPECT_FALSE(announcer->isIdle());
|
|
||||||
auto connect_transaction_id = parseConnectionRequest(sent);
|
auto connect_transaction_id = parseConnectionRequest(sent);
|
||||||
|
|
||||||
// Have the tracker respond to the request
|
// Have the tracker respond to the request
|
||||||
auto const connection_id = sendConnectionResponse(*announcer, connect_transaction_id);
|
auto const connection_id = sendConnectionResponse(*announcer, connect_transaction_id);
|
||||||
EXPECT_FALSE(announcer->isIdle());
|
|
||||||
|
|
||||||
// The announcer should have sent a UDP scrape request.
|
// The announcer should have sent a UDP scrape request.
|
||||||
// Inspect that request for validity.
|
// Inspect that request for validity.
|
||||||
sent = waitForAnnouncerToSendMessage(mediator);
|
sent = waitForAnnouncerToSendMessage(mediator);
|
||||||
EXPECT_FALSE(announcer->isIdle());
|
|
||||||
auto [scrape_transaction_id, info_hashes] = parseScrapeRequest(sent, connection_id);
|
auto [scrape_transaction_id, info_hashes] = parseScrapeRequest(sent, connection_id);
|
||||||
expectEqual(request, info_hashes);
|
expectEqual(request, info_hashes);
|
||||||
|
|
||||||
@@ -340,7 +336,6 @@ TEST_F(AnnouncerUdpTest, canScrape)
|
|||||||
auto arr = std::array<uint8_t, 256>{};
|
auto arr = std::array<uint8_t, 256>{};
|
||||||
buf.toBuf(std::data(arr), response_size);
|
buf.toBuf(std::data(arr), response_size);
|
||||||
EXPECT_TRUE(announcer->handleMessage(std::data(arr), response_size));
|
EXPECT_TRUE(announcer->handleMessage(std::data(arr), response_size));
|
||||||
EXPECT_TRUE(announcer->isIdle());
|
|
||||||
|
|
||||||
// confirm that announcer processed the response
|
// confirm that announcer processed the response
|
||||||
EXPECT_TRUE(response);
|
EXPECT_TRUE(response);
|
||||||
|
|||||||
Reference in New Issue
Block a user