2023-11-01 21:11:11 +00:00
|
|
|
// This file Copyright © Mnemosyne LLC.
|
2022-02-07 16:25:02 +00:00
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
|
|
// License text can be found in the licenses/ folder.
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2021-09-19 20:41:35 +00:00
|
|
|
#include <algorithm>
|
2023-11-21 15:02:03 +00:00
|
|
|
#include <array>
|
|
|
|
#include <cstddef>
|
|
|
|
#include <cstdint>
|
2023-07-08 15:24:03 +00:00
|
|
|
#include <initializer_list>
|
2023-12-06 16:10:28 +00:00
|
|
|
#include <limits>
|
2023-11-21 15:02:03 +00:00
|
|
|
#include <memory>
|
2022-12-18 19:24:46 +00:00
|
|
|
#include <utility> // for std::swap()
|
2021-11-09 03:30:03 +00:00
|
|
|
#include <vector>
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2022-03-15 14:52:16 +00:00
|
|
|
#include <fmt/core.h>
|
|
|
|
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/transmission.h"
|
|
|
|
|
|
|
|
#include "libtransmission/bandwidth.h"
|
|
|
|
#include "libtransmission/crypto-utils.h"
|
|
|
|
#include "libtransmission/log.h"
|
|
|
|
#include "libtransmission/peer-io.h"
|
|
|
|
#include "libtransmission/tr-assert.h"
|
|
|
|
#include "libtransmission/utils.h" // tr_time_msec()
|
2023-11-10 23:12:24 +00:00
|
|
|
#include "libtransmission/values.h"
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2023-11-10 23:12:24 +00:00
|
|
|
using namespace libtransmission::Values;
|
|
|
|
|
|
|
|
Speed tr_bandwidth::get_speed(RateControl& r, unsigned int interval_msec, uint64_t now)
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
2023-11-28 16:18:38 +00:00
|
|
|
if (now == 0U)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
now = tr_time_msec();
|
|
|
|
}
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2021-10-12 06:04:22 +00:00
|
|
|
if (now != r.cache_time_)
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
2023-11-28 16:18:38 +00:00
|
|
|
uint64_t bytes = 0U;
|
2017-04-20 16:02:19 +00:00
|
|
|
uint64_t const cutoff = now - interval_msec;
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2022-04-28 13:44:24 +00:00
|
|
|
for (int i = r.newest_; r.date_[i] > cutoff;)
|
2011-03-15 18:11:31 +00:00
|
|
|
{
|
2022-04-28 13:44:24 +00:00
|
|
|
bytes += r.size_[i];
|
2011-03-15 18:11:31 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (--i == -1)
|
|
|
|
{
|
2021-10-12 06:04:22 +00:00
|
|
|
i = HistorySize - 1; /* circular history */
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2013-01-24 23:59:52 +00:00
|
|
|
|
2021-10-12 06:04:22 +00:00
|
|
|
if (i == r.newest_)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
break; /* we've come all the way around */
|
|
|
|
}
|
2011-03-15 18:11:31 +00:00
|
|
|
}
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2023-11-10 23:12:24 +00:00
|
|
|
r.cache_val_ = Speed{ bytes * 1000U / interval_msec, Speed::Units::Byps };
|
2021-10-12 06:04:22 +00:00
|
|
|
r.cache_time_ = now;
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
|
|
|
|
2021-10-12 06:04:22 +00:00
|
|
|
return r.cache_val_;
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
|
|
|
|
2023-10-06 23:30:04 +00:00
|
|
|
void tr_bandwidth::notify_bandwidth_consumed_bytes(uint64_t const now, RateControl& r, size_t size)
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
2023-10-06 23:30:04 +00:00
|
|
|
if (r.date_[r.newest_] + GranularityMSec >= now)
|
2013-01-24 23:59:52 +00:00
|
|
|
{
|
2023-10-06 23:30:04 +00:00
|
|
|
r.size_[r.newest_] += size;
|
2013-01-24 23:59:52 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
2023-10-06 23:30:04 +00:00
|
|
|
if (++r.newest_ == HistorySize)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-10-06 23:30:04 +00:00
|
|
|
r.newest_ = 0;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2023-10-06 23:30:04 +00:00
|
|
|
r.date_[r.newest_] = now;
|
|
|
|
r.size_[r.newest_] = size;
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
2011-03-15 18:11:31 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* invalidate cache_val*/
|
2023-11-28 16:18:38 +00:00
|
|
|
r.cache_time_ = 0U;
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 22:55:00 +00:00
|
|
|
// ---
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2023-11-28 16:18:38 +00:00
|
|
|
tr_bandwidth::tr_bandwidth(tr_bandwidth* parent, bool is_group)
|
2023-12-06 16:10:28 +00:00
|
|
|
: priority_(is_group ? std::numeric_limits<tr_priority_t>::max() : TR_PRI_NORMAL)
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
2023-11-28 16:18:38 +00:00
|
|
|
set_parent(parent);
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 22:55:00 +00:00
|
|
|
// ---
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2023-01-07 22:55:00 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
namespace deparent_helpers
|
|
|
|
{
|
|
|
|
void remove_child(std::vector<tr_bandwidth*>& v, tr_bandwidth* remove_me) noexcept
|
2021-11-20 16:58:47 +00:00
|
|
|
{
|
|
|
|
// the list isn't sorted -- so instead of erase()ing `it`,
|
|
|
|
// do the cheaper option of overwriting it with the final item
|
2022-08-13 17:11:07 +00:00
|
|
|
if (auto it = std::find(std::begin(v), std::end(v), remove_me); it != std::end(v))
|
|
|
|
{
|
2023-10-06 23:30:04 +00:00
|
|
|
std::swap(*it, v.back());
|
|
|
|
v.pop_back();
|
2022-08-13 17:11:07 +00:00
|
|
|
}
|
2021-11-20 16:58:47 +00:00
|
|
|
}
|
2023-01-07 22:55:00 +00:00
|
|
|
} // namespace deparent_helpers
|
|
|
|
} // namespace
|
2021-11-20 16:58:47 +00:00
|
|
|
|
2022-08-16 14:30:05 +00:00
|
|
|
void tr_bandwidth::deparent() noexcept
|
|
|
|
{
|
2023-01-07 22:55:00 +00:00
|
|
|
using namespace deparent_helpers;
|
|
|
|
|
2022-08-16 14:30:05 +00:00
|
|
|
if (parent_ == nullptr)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
remove_child(parent_->children_, this);
|
|
|
|
parent_ = nullptr;
|
|
|
|
}
|
|
|
|
|
2023-04-23 01:25:55 +00:00
|
|
|
void tr_bandwidth::set_parent(tr_bandwidth* new_parent)
|
2008-11-25 21:35:17 +00:00
|
|
|
{
|
2021-10-12 06:04:22 +00:00
|
|
|
TR_ASSERT(this != new_parent);
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2022-08-16 14:30:05 +00:00
|
|
|
deparent();
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2021-10-12 06:04:22 +00:00
|
|
|
if (new_parent != nullptr)
|
2008-11-25 21:35:17 +00:00
|
|
|
{
|
2021-11-28 14:05:44 +00:00
|
|
|
#ifdef TR_ENABLE_ASSERTS
|
2021-10-12 06:04:22 +00:00
|
|
|
TR_ASSERT(new_parent->parent_ != this);
|
2021-11-20 16:58:47 +00:00
|
|
|
auto& children = new_parent->children_;
|
|
|
|
TR_ASSERT(std::find(std::begin(children), std::end(children), this) == std::end(children)); // not already there
|
2021-11-28 14:05:44 +00:00
|
|
|
#endif
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2021-11-20 16:58:47 +00:00
|
|
|
new_parent->children_.push_back(this);
|
2023-11-28 16:18:38 +00:00
|
|
|
parent_ = new_parent;
|
2008-11-25 21:35:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-07 22:55:00 +00:00
|
|
|
// ---
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2023-04-23 01:25:55 +00:00
|
|
|
void tr_bandwidth::allocate_bandwidth(
|
2021-08-15 09:41:48 +00:00
|
|
|
tr_priority_t parent_priority,
|
2023-10-06 23:30:04 +00:00
|
|
|
uint64_t period_msec,
|
2022-08-30 17:38:30 +00:00
|
|
|
std::vector<std::shared_ptr<tr_peerIo>>& peer_pool)
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
2023-11-28 16:18:38 +00:00
|
|
|
auto const priority = std::min(parent_priority, priority_);
|
2017-06-13 02:24:09 +00:00
|
|
|
|
2022-12-20 19:49:37 +00:00
|
|
|
// set the available bandwidth
|
|
|
|
for (auto const dir : { TR_UP, TR_DOWN })
|
2008-11-26 15:58:26 +00:00
|
|
|
{
|
2022-12-20 19:49:37 +00:00
|
|
|
if (auto& bandwidth = band_[dir]; bandwidth.is_limited_)
|
|
|
|
{
|
2023-11-10 23:12:24 +00:00
|
|
|
auto const next_pulse_speed = bandwidth.desired_speed_;
|
|
|
|
bandwidth.bytes_left_ = next_pulse_speed.base_quantity() * period_msec / 1000U;
|
2022-12-20 19:49:37 +00:00
|
|
|
}
|
2008-11-26 15:58:26 +00:00
|
|
|
}
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2022-12-16 07:23:12 +00:00
|
|
|
// add this bandwidth's peer, if any, to the peer pool
|
2023-11-28 16:18:38 +00:00
|
|
|
if (auto shared = peer_.lock(); shared)
|
2013-01-24 23:59:52 +00:00
|
|
|
{
|
2023-11-28 01:59:26 +00:00
|
|
|
TR_ASSERT(tr_isPriority(priority));
|
2022-12-16 07:23:12 +00:00
|
|
|
shared->set_priority(priority);
|
2022-08-30 17:38:30 +00:00
|
|
|
peer_pool.push_back(std::move(shared));
|
2009-04-18 23:17:30 +00:00
|
|
|
}
|
2008-12-09 22:05:45 +00:00
|
|
|
|
2021-10-09 12:52:09 +00:00
|
|
|
// traverse & repeat for the subtree
|
2023-11-28 16:18:38 +00:00
|
|
|
for (auto* child : children_)
|
2013-01-24 23:59:52 +00:00
|
|
|
{
|
2023-04-23 01:25:55 +00:00
|
|
|
child->allocate_bandwidth(priority, period_msec, peer_pool);
|
2008-11-25 21:35:17 +00:00
|
|
|
}
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
|
|
|
|
2023-04-23 01:25:55 +00:00
|
|
|
void tr_bandwidth::phase_one(std::vector<tr_peerIo*>& peers, tr_direction dir)
|
2008-12-09 22:05:45 +00:00
|
|
|
{
|
2022-12-18 19:24:46 +00:00
|
|
|
// First phase of IO. Tries to distribute bandwidth fairly to keep faster
|
|
|
|
// peers from starving the others.
|
2022-12-19 23:31:24 +00:00
|
|
|
tr_logAddTrace(fmt::format("{} peers to go round-robin for {}", peers.size(), dir == TR_UP ? "upload" : "download"));
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-12-18 19:24:46 +00:00
|
|
|
// Shuffle the peers so they all have equal chance to be first in line.
|
2022-12-19 23:31:24 +00:00
|
|
|
thread_local auto urbg = tr_urbg<size_t>{};
|
|
|
|
std::shuffle(std::begin(peers), std::end(peers), urbg);
|
2011-05-04 21:38:01 +00:00
|
|
|
|
2022-12-18 19:24:46 +00:00
|
|
|
// Give each peer `Increment` bandwidth bytes to use. Repeat this
|
|
|
|
// process until we run out of bandwidth and/or peers that can use it.
|
2022-12-19 23:31:24 +00:00
|
|
|
for (size_t n_unfinished = std::size(peers); n_unfinished > 0U;)
|
2022-12-18 19:24:46 +00:00
|
|
|
{
|
2023-11-28 16:18:38 +00:00
|
|
|
for (size_t i = 0U; i < n_unfinished;)
|
2022-12-18 19:24:46 +00:00
|
|
|
{
|
|
|
|
// Value of 3000 bytes chosen so that when using µTP we'll send a full-size
|
|
|
|
// frame right away and leave enough buffered data for the next frame to go
|
|
|
|
// out in a timely manner.
|
2023-10-06 23:30:04 +00:00
|
|
|
static auto constexpr Increment = 3000U;
|
2008-12-20 22:19:34 +00:00
|
|
|
|
2022-12-19 23:31:24 +00:00
|
|
|
auto const bytes_used = peers[i]->flush(dir, Increment);
|
2022-12-18 19:24:46 +00:00
|
|
|
tr_logAddTrace(fmt::format("peer #{} of {} used {} bytes in this pass", i, n_unfinished, bytes_used));
|
2009-01-03 02:43:17 +00:00
|
|
|
|
2022-12-18 19:24:46 +00:00
|
|
|
if (bytes_used != Increment)
|
|
|
|
{
|
|
|
|
// peer is done writing for now; move it to the end of the list
|
2022-12-19 23:31:24 +00:00
|
|
|
std::swap(peers[i], peers[n_unfinished - 1]);
|
2022-12-18 19:24:46 +00:00
|
|
|
--n_unfinished;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++i;
|
|
|
|
}
|
2008-12-15 21:22:08 +00:00
|
|
|
}
|
2008-12-09 22:05:45 +00:00
|
|
|
}
|
2009-04-18 23:17:30 +00:00
|
|
|
}
|
|
|
|
|
2023-10-06 23:30:04 +00:00
|
|
|
void tr_bandwidth::allocate(uint64_t period_msec)
|
2009-04-18 23:17:30 +00:00
|
|
|
{
|
2022-08-30 17:38:30 +00:00
|
|
|
// keep these peers alive for the scope of this function
|
|
|
|
auto refs = std::vector<std::shared_ptr<tr_peerIo>>{};
|
|
|
|
|
2022-12-20 19:49:37 +00:00
|
|
|
auto peer_arrays = std::array<std::vector<tr_peerIo*>, 3>{};
|
|
|
|
auto& high = peer_arrays[0];
|
|
|
|
auto& normal = peer_arrays[1];
|
|
|
|
auto& low = peer_arrays[2];
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-12-20 19:49:37 +00:00
|
|
|
// allocateBandwidth () is a helper function with two purposes:
|
|
|
|
// 1. allocate bandwidth to b and its subtree
|
|
|
|
// 2. accumulate an array of all the peerIos from b and its subtree.
|
2023-12-06 16:10:28 +00:00
|
|
|
allocate_bandwidth(std::numeric_limits<tr_priority_t>::max(), period_msec, refs);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-11-28 15:45:39 +00:00
|
|
|
for (auto const& io : refs)
|
2009-04-21 16:18:51 +00:00
|
|
|
{
|
2022-12-16 07:23:12 +00:00
|
|
|
io->flush_outgoing_protocol_msgs();
|
2009-04-21 16:18:51 +00:00
|
|
|
|
2022-12-16 07:23:12 +00:00
|
|
|
switch (io->priority())
|
2013-01-24 23:59:52 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
case TR_PRI_HIGH:
|
2022-08-30 17:38:30 +00:00
|
|
|
high.push_back(io.get());
|
2021-10-06 17:24:02 +00:00
|
|
|
[[fallthrough]];
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
case TR_PRI_NORMAL:
|
2022-08-30 17:38:30 +00:00
|
|
|
normal.push_back(io.get());
|
2021-10-06 17:24:02 +00:00
|
|
|
[[fallthrough]];
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2023-11-28 01:59:26 +00:00
|
|
|
case TR_PRI_LOW:
|
2022-08-30 17:38:30 +00:00
|
|
|
low.push_back(io.get());
|
2023-11-28 01:59:26 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
TR_ASSERT_MSG(false, "invalid priority");
|
|
|
|
break;
|
2009-04-18 23:17:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-20 19:49:37 +00:00
|
|
|
// First phase of IO. Tries to distribute bandwidth fairly to keep faster
|
|
|
|
// peers from starving the others. Loop through the peers, giving each a
|
|
|
|
// small chunk of bandwidth. Keep looping until we run out of bandwidth
|
|
|
|
// and/or peers that can use it
|
|
|
|
for (auto& peers : peer_arrays)
|
|
|
|
{
|
2023-04-23 01:25:55 +00:00
|
|
|
phase_one(peers, TR_UP);
|
|
|
|
phase_one(peers, TR_DOWN);
|
2022-12-20 19:49:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Second phase of IO. To help us scale in high bandwidth situations,
|
|
|
|
// enable on-demand IO for peers with bandwidth left to burn.
|
|
|
|
// This on-demand IO is enabled until (1) the peer runs out of bandwidth,
|
|
|
|
// or (2) the next tr_bandwidth::allocate () call, when we start over again.
|
2022-11-28 15:45:39 +00:00
|
|
|
for (auto const& io : refs)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-12-20 19:49:37 +00:00
|
|
|
io->set_enabled(TR_UP, io->has_bandwidth_left(TR_UP));
|
|
|
|
io->set_enabled(TR_DOWN, io->has_bandwidth_left(TR_DOWN));
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 22:55:00 +00:00
|
|
|
// ---
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2023-10-09 13:49:49 +00:00
|
|
|
size_t tr_bandwidth::clamp(tr_direction const dir, size_t byte_count) const noexcept
|
2008-11-25 21:35:17 +00:00
|
|
|
{
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(tr_isDirection(dir));
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2023-11-28 16:18:38 +00:00
|
|
|
if (band_[dir].is_limited_)
|
2008-11-25 21:35:17 +00:00
|
|
|
{
|
2023-11-28 16:18:38 +00:00
|
|
|
byte_count = std::min(byte_count, band_[dir].bytes_left_);
|
2008-11-25 21:35:17 +00:00
|
|
|
}
|
|
|
|
|
2023-11-28 16:18:38 +00:00
|
|
|
if (parent_ != nullptr && band_[dir].honor_parent_limits_ && byte_count > 0U)
|
2021-10-09 12:52:09 +00:00
|
|
|
{
|
2023-11-28 16:18:38 +00:00
|
|
|
byte_count = parent_->clamp(dir, byte_count);
|
2021-10-09 12:52:09 +00:00
|
|
|
}
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2021-10-12 06:04:22 +00:00
|
|
|
return byte_count;
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
|
|
|
|
2023-04-23 01:25:55 +00:00
|
|
|
void tr_bandwidth::notify_bandwidth_consumed(tr_direction dir, size_t byte_count, bool is_piece_data, uint64_t now)
|
2008-11-24 04:21:23 +00:00
|
|
|
{
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(tr_isDirection(dir));
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2023-11-28 16:18:38 +00:00
|
|
|
auto& band = band_[dir];
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2023-11-28 16:18:38 +00:00
|
|
|
if (band.is_limited_ && is_piece_data)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-11-28 16:18:38 +00:00
|
|
|
band.bytes_left_ -= std::min(band.bytes_left_, byte_count);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2023-11-28 16:18:38 +00:00
|
|
|
notify_bandwidth_consumed_bytes(now, band.raw_, byte_count);
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2021-10-12 06:04:22 +00:00
|
|
|
if (is_piece_data)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-11-28 16:18:38 +00:00
|
|
|
notify_bandwidth_consumed_bytes(now, band.piece_, byte_count);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2023-11-28 16:18:38 +00:00
|
|
|
if (parent_ != nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-11-28 16:18:38 +00:00
|
|
|
parent_->notify_bandwidth_consumed(dir, byte_count, is_piece_data, now);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2008-11-24 04:21:23 +00:00
|
|
|
}
|
2022-03-18 13:11:59 +00:00
|
|
|
|
2023-01-07 22:55:00 +00:00
|
|
|
// ---
|
2022-03-18 13:11:59 +00:00
|
|
|
|
2023-04-23 01:25:55 +00:00
|
|
|
tr_bandwidth_limits tr_bandwidth::get_limits() const
|
2022-03-18 13:11:59 +00:00
|
|
|
{
|
2023-11-10 23:12:24 +00:00
|
|
|
auto limits = tr_bandwidth_limits{};
|
2023-11-28 16:18:38 +00:00
|
|
|
limits.up_limit = get_desired_speed(TR_UP);
|
|
|
|
limits.down_limit = get_desired_speed(TR_DOWN);
|
|
|
|
limits.up_limited = is_limited(TR_UP);
|
|
|
|
limits.down_limited = is_limited(TR_DOWN);
|
2022-03-18 13:11:59 +00:00
|
|
|
return limits;
|
|
|
|
}
|
|
|
|
|
2023-10-06 23:30:04 +00:00
|
|
|
void tr_bandwidth::set_limits(tr_bandwidth_limits const& limits)
|
2022-03-18 13:11:59 +00:00
|
|
|
{
|
2023-11-28 16:18:38 +00:00
|
|
|
set_desired_speed(TR_UP, limits.up_limit);
|
|
|
|
set_desired_speed(TR_DOWN, limits.down_limit);
|
|
|
|
set_limited(TR_UP, limits.up_limited);
|
|
|
|
set_limited(TR_DOWN, limits.down_limited);
|
2022-03-18 13:11:59 +00:00
|
|
|
}
|