2023-02-11 20:49:42 +00:00
|
|
|
// This file Copyright © 2008-2023 Mnemosyne LLC.
|
2022-08-08 18:05:39 +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
|
|
|
|
2017-11-14 20:21:28 +00:00
|
|
|
#pragma once
|
|
|
|
|
2008-11-24 20:17:36 +00:00
|
|
|
#ifndef __TRANSMISSION__
|
2017-04-19 12:04:45 +00:00
|
|
|
#error only libtransmission should #include this header.
|
2008-11-24 20:17:36 +00:00
|
|
|
#endif
|
|
|
|
|
2021-10-09 12:52:09 +00:00
|
|
|
#include <array>
|
2021-12-15 21:25:42 +00:00
|
|
|
#include <cstddef> // size_t
|
2022-04-08 01:50:26 +00:00
|
|
|
#include <cstdint> // uint64_t
|
2022-08-30 17:38:30 +00:00
|
|
|
#include <memory>
|
2022-09-21 23:34:18 +00:00
|
|
|
#include <utility> // for std::move()
|
2021-11-09 03:30:03 +00:00
|
|
|
#include <vector>
|
2021-10-09 12:52:09 +00:00
|
|
|
|
2009-01-02 17:46:22 +00:00
|
|
|
#include "transmission.h"
|
2021-12-15 21:25:42 +00:00
|
|
|
|
2017-06-08 07:24:12 +00:00
|
|
|
#include "tr-assert.h"
|
2009-01-02 17:46:22 +00:00
|
|
|
|
2021-10-11 21:54:16 +00:00
|
|
|
class tr_peerIo;
|
2009-01-02 17:46:22 +00:00
|
|
|
|
2009-05-29 19:17:12 +00:00
|
|
|
/**
|
|
|
|
* @addtogroup networked_io Networked IO
|
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
|
2022-03-18 13:11:59 +00:00
|
|
|
struct tr_bandwidth_limits
|
|
|
|
{
|
2022-10-25 16:14:42 +00:00
|
|
|
tr_kilobytes_per_second_t up_limit_KBps = 0;
|
|
|
|
tr_kilobytes_per_second_t down_limit_KBps = 0;
|
2022-03-18 16:34:29 +00:00
|
|
|
bool up_limited = false;
|
|
|
|
bool down_limited = false;
|
2022-03-18 13:11:59 +00:00
|
|
|
};
|
|
|
|
|
2008-11-25 21:35:17 +00:00
|
|
|
/**
|
2023-01-23 16:26:11 +00:00
|
|
|
* `tr_bandwidth` is an object for measuring and constraining bandwidth speeds.
|
2008-11-25 21:35:17 +00:00
|
|
|
*
|
2023-01-23 16:26:11 +00:00
|
|
|
* `tr_bandwidth` objects can be "stacked" so that a peer can be made to obey
|
2008-11-25 21:35:17 +00:00
|
|
|
* multiple constraints (for example, obeying the global speed limit and a
|
|
|
|
* per-torrent speed limit).
|
|
|
|
*
|
|
|
|
* HIERARCHY
|
|
|
|
*
|
|
|
|
* Transmission's bandwidth hierarchy is a tree.
|
2023-01-23 16:26:11 +00:00
|
|
|
* At the top is the global bandwidth object owned by `tr_session`.
|
|
|
|
* Its children are per-torrent bandwidth objects owned by `tr_torrent`.
|
|
|
|
* Underneath those are per-peer bandwidth objects owned by `tr_peer`.
|
2008-11-25 21:35:17 +00:00
|
|
|
*
|
2023-01-23 16:26:11 +00:00
|
|
|
* `tr_session` also owns a `tr_handshake`'s bandwidths, so that the handshake
|
2010-12-27 19:18:17 +00:00
|
|
|
* I/O can be counted in the global raw totals. When the handshake is done,
|
2023-01-23 16:26:11 +00:00
|
|
|
* the bandwidth's ownership passes to a `tr_peer`.
|
2009-08-10 20:04:08 +00:00
|
|
|
*
|
2008-11-25 21:35:17 +00:00
|
|
|
* MEASURING
|
|
|
|
*
|
|
|
|
* When you ask a bandwidth object for its speed, it gives the speed of the
|
2010-12-27 19:18:17 +00:00
|
|
|
* subtree underneath it as well. So you can get Transmission's overall
|
2023-01-23 16:26:11 +00:00
|
|
|
* speed by querying `tr_session`'s bandwidth, per-torrent speeds by asking
|
|
|
|
* `tr_torrent`'s bandwidth, and per-peer speeds by asking `tr_peer`'s bandwidth.
|
2008-11-25 21:35:17 +00:00
|
|
|
*
|
|
|
|
* CONSTRAINING
|
2009-08-10 20:04:08 +00:00
|
|
|
*
|
2023-01-23 16:26:11 +00:00
|
|
|
* Call `tr_bandwidth::allocate()` periodically. `tr_bandwidth` knows its current
|
2008-11-25 21:35:17 +00:00
|
|
|
* speed and will decide how many bytes to make available over the
|
|
|
|
* user-specified period to reach the user-specified desired speed.
|
2008-12-29 21:19:31 +00:00
|
|
|
* If appropriate, it notifies its peer-ios that new bandwidth is available.
|
2009-08-10 20:04:08 +00:00
|
|
|
*
|
2023-01-23 16:26:11 +00:00
|
|
|
* `tr_bandwidth::allocate()` operates on the `tr_bandwidth` subtree, so usually
|
|
|
|
* you'll only need to invoke it for the top-level `tr_session` bandwidth.
|
2008-11-25 21:35:17 +00:00
|
|
|
*
|
2023-01-23 16:26:11 +00:00
|
|
|
* The peer-ios all have a pointer to their associated `tr_bandwidth` object,
|
|
|
|
* and call `tr_bandwidth::clamp()` before performing I/O to see how much
|
2008-11-25 21:35:17 +00:00
|
|
|
* bandwidth they can safely use.
|
|
|
|
*/
|
2022-06-29 20:08:58 +00:00
|
|
|
struct tr_bandwidth
|
2009-01-02 17:46:22 +00:00
|
|
|
{
|
2022-06-29 20:08:58 +00:00
|
|
|
private:
|
|
|
|
static constexpr size_t HistoryMSec = 2000U;
|
|
|
|
static constexpr size_t IntervalMSec = HistoryMSec;
|
|
|
|
static constexpr size_t GranularityMSec = 250;
|
|
|
|
static constexpr size_t HistorySize = (IntervalMSec / GranularityMSec);
|
|
|
|
|
2021-10-09 12:52:09 +00:00
|
|
|
public:
|
2022-06-29 20:08:58 +00:00
|
|
|
explicit tr_bandwidth(tr_bandwidth* newParent);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-06-29 20:08:58 +00:00
|
|
|
tr_bandwidth()
|
|
|
|
: tr_bandwidth(nullptr)
|
2021-10-09 12:52:09 +00:00
|
|
|
{
|
|
|
|
}
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2022-08-16 14:30:05 +00:00
|
|
|
~tr_bandwidth() noexcept
|
2021-10-09 12:52:09 +00:00
|
|
|
{
|
2022-08-16 14:30:05 +00:00
|
|
|
deparent();
|
2021-10-09 12:52:09 +00:00
|
|
|
}
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2022-06-29 20:08:58 +00:00
|
|
|
tr_bandwidth& operator=(tr_bandwidth&&) = delete;
|
|
|
|
tr_bandwidth& operator=(tr_bandwidth) = delete;
|
|
|
|
tr_bandwidth(tr_bandwidth&&) = delete;
|
|
|
|
tr_bandwidth(tr_bandwidth&) = delete;
|
2021-10-12 06:04:22 +00:00
|
|
|
|
2022-08-30 17:38:30 +00:00
|
|
|
// @brief Sets the peer. nullptr is allowed.
|
|
|
|
void setPeer(std::weak_ptr<tr_peerIo> peer) noexcept
|
2021-10-09 12:52:09 +00:00
|
|
|
{
|
2022-08-30 17:38:30 +00:00
|
|
|
this->peer_ = std::move(peer);
|
2021-10-09 12:52:09 +00:00
|
|
|
}
|
2009-01-02 19:56:06 +00:00
|
|
|
|
2021-10-09 12:52:09 +00:00
|
|
|
/**
|
|
|
|
* @brief Notify the bandwidth object that some of its allocated bandwidth has been consumed.
|
|
|
|
* This is is usually invoked by the peer-io after a read or write.
|
|
|
|
*/
|
2021-10-12 06:04:22 +00:00
|
|
|
void notifyBandwidthConsumed(tr_direction dir, size_t byte_count, bool is_piece_data, uint64_t now);
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2021-10-09 12:52:09 +00:00
|
|
|
/**
|
2023-01-23 16:26:11 +00:00
|
|
|
* @brief allocate the next `period_msec`'s worth of bandwidth for the peer-ios to consume
|
2021-10-09 12:52:09 +00:00
|
|
|
*/
|
2022-12-20 19:49:37 +00:00
|
|
|
void allocate(unsigned int period_msec);
|
2008-12-21 18:15:00 +00:00
|
|
|
|
2022-08-03 06:15:37 +00:00
|
|
|
void setParent(tr_bandwidth* new_parent);
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2022-04-05 03:51:56 +00:00
|
|
|
[[nodiscard]] constexpr tr_priority_t getPriority() const noexcept
|
2021-10-09 12:52:09 +00:00
|
|
|
{
|
2021-10-12 06:04:22 +00:00
|
|
|
return this->priority_;
|
2021-10-09 12:52:09 +00:00
|
|
|
}
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2022-04-05 03:51:56 +00:00
|
|
|
constexpr void setPriority(tr_priority_t prio) noexcept
|
2021-10-09 12:52:09 +00:00
|
|
|
{
|
2021-10-12 06:04:22 +00:00
|
|
|
this->priority_ = prio;
|
2021-10-09 12:52:09 +00:00
|
|
|
}
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2021-10-09 12:52:09 +00:00
|
|
|
/**
|
2023-01-23 16:26:11 +00:00
|
|
|
* @brief clamps `byte_count` down to a number that this bandwidth will allow to be consumed
|
2021-10-12 06:04:22 +00:00
|
|
|
*/
|
2022-11-21 16:19:45 +00:00
|
|
|
[[nodiscard]] size_t clamp(tr_direction dir, size_t byte_count) const noexcept
|
2021-10-09 12:52:09 +00:00
|
|
|
{
|
2021-10-12 06:04:22 +00:00
|
|
|
return this->clamp(0, dir, byte_count);
|
2021-10-09 12:52:09 +00:00
|
|
|
}
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2021-10-09 12:52:09 +00:00
|
|
|
/** @brief Get the raw total of bytes read or sent by this bandwidth subtree. */
|
2023-01-27 20:25:08 +00:00
|
|
|
[[nodiscard]] auto getRawSpeedBytesPerSecond(uint64_t const now, tr_direction const dir) const
|
2021-10-09 12:52:09 +00:00
|
|
|
{
|
|
|
|
TR_ASSERT(tr_isDirection(dir));
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2021-10-12 06:04:22 +00:00
|
|
|
return getSpeedBytesPerSecond(this->band_[dir].raw_, HistoryMSec, now);
|
2021-10-09 12:52:09 +00:00
|
|
|
}
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2021-10-09 12:52:09 +00:00
|
|
|
/** @brief Get the number of piece data bytes read or sent by this bandwidth subtree. */
|
2023-01-27 20:25:08 +00:00
|
|
|
[[nodiscard]] auto getPieceSpeedBytesPerSecond(uint64_t const now, tr_direction const dir) const
|
2021-10-09 12:52:09 +00:00
|
|
|
{
|
|
|
|
TR_ASSERT(tr_isDirection(dir));
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2021-10-12 06:04:22 +00:00
|
|
|
return getSpeedBytesPerSecond(this->band_[dir].piece_, HistoryMSec, now);
|
2021-10-09 12:52:09 +00:00
|
|
|
}
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2021-10-09 12:52:09 +00:00
|
|
|
/**
|
|
|
|
* @brief Set the desired speed for this bandwidth subtree.
|
2023-01-23 16:26:11 +00:00
|
|
|
* @see `tr_bandwidth::allocate`
|
|
|
|
* @see `tr_bandwidth::getDesiredSpeed`
|
2021-10-09 12:52:09 +00:00
|
|
|
*/
|
2022-10-25 16:14:42 +00:00
|
|
|
constexpr bool setDesiredSpeedBytesPerSecond(tr_direction dir, tr_bytes_per_second_t desired_speed)
|
2021-10-09 12:52:09 +00:00
|
|
|
{
|
2021-10-12 06:04:22 +00:00
|
|
|
auto& value = this->band_[dir].desired_speed_bps_;
|
|
|
|
bool const did_change = desired_speed != value;
|
|
|
|
value = desired_speed;
|
|
|
|
return did_change;
|
2021-10-09 12:52:09 +00:00
|
|
|
}
|
|
|
|
|
2023-01-27 20:25:08 +00:00
|
|
|
/**
|
|
|
|
* @brief Get the desired speed for the bandwidth subtree.
|
|
|
|
* @see `tr_bandwidth::setDesiredSpeed`
|
|
|
|
*/
|
|
|
|
[[nodiscard]] constexpr auto getDesiredSpeedBytesPerSecond(tr_direction dir) const
|
|
|
|
{
|
|
|
|
return this->band_[dir].desired_speed_bps_;
|
|
|
|
}
|
|
|
|
|
2023-01-07 22:55:00 +00:00
|
|
|
[[nodiscard]] bool is_maxed_out(tr_direction dir, uint64_t now_msec) const noexcept
|
|
|
|
{
|
|
|
|
if (!isLimited(dir))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto const got = getPieceSpeedBytesPerSecond(now_msec, dir);
|
|
|
|
auto const want = getDesiredSpeedBytesPerSecond(dir);
|
|
|
|
return got >= want;
|
|
|
|
}
|
|
|
|
|
2021-10-09 12:52:09 +00:00
|
|
|
/**
|
|
|
|
* @brief Set whether or not this bandwidth should throttle its peer-io's speeds
|
|
|
|
*/
|
2021-10-12 06:04:22 +00:00
|
|
|
constexpr bool setLimited(tr_direction dir, bool is_limited)
|
2021-10-09 12:52:09 +00:00
|
|
|
{
|
2021-10-10 01:12:03 +00:00
|
|
|
bool* value = &this->band_[dir].is_limited_;
|
2021-10-12 06:04:22 +00:00
|
|
|
bool const did_change = is_limited != *value;
|
|
|
|
*value = is_limited;
|
|
|
|
return did_change;
|
2021-10-09 12:52:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return nonzero if this bandwidth throttles its peer-ios speeds
|
|
|
|
*/
|
2022-11-03 23:08:02 +00:00
|
|
|
[[nodiscard]] constexpr bool isLimited(tr_direction dir) const noexcept
|
2021-10-09 12:52:09 +00:00
|
|
|
{
|
2021-10-10 01:12:03 +00:00
|
|
|
return this->band_[dir].is_limited_;
|
2021-10-09 12:52:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Almost all the time we do want to honor a parents' bandwidth cap, so that
|
|
|
|
* (for example) a peer is constrained by a per-torrent cap and the global cap.
|
2023-01-23 16:26:11 +00:00
|
|
|
* But when we set a torrent's speed mode to `TR_SPEEDLIMIT_UNLIMITED`, then
|
2021-10-09 12:52:09 +00:00
|
|
|
* in that particular case we want to ignore the global speed limit...
|
|
|
|
*/
|
2021-10-12 06:04:22 +00:00
|
|
|
constexpr bool honorParentLimits(tr_direction direction, bool is_enabled)
|
2021-10-09 12:52:09 +00:00
|
|
|
{
|
2021-10-10 01:12:03 +00:00
|
|
|
bool* value = &this->band_[direction].honor_parent_limits_;
|
2021-10-12 06:04:22 +00:00
|
|
|
bool const did_change = is_enabled != *value;
|
|
|
|
*value = is_enabled;
|
|
|
|
return did_change;
|
2021-10-09 12:52:09 +00:00
|
|
|
}
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2021-10-09 12:52:09 +00:00
|
|
|
[[nodiscard]] constexpr bool areParentLimitsHonored(tr_direction direction) const
|
|
|
|
{
|
|
|
|
TR_ASSERT(tr_isDirection(direction));
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2021-10-10 01:12:03 +00:00
|
|
|
return this->band_[direction].honor_parent_limits_;
|
2021-10-09 12:52:09 +00:00
|
|
|
}
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2023-01-27 20:25:08 +00:00
|
|
|
[[nodiscard]] tr_bandwidth_limits getLimits() const;
|
|
|
|
|
|
|
|
void setLimits(tr_bandwidth_limits const* limits);
|
|
|
|
|
|
|
|
private:
|
2021-10-10 01:12:03 +00:00
|
|
|
struct RateControl
|
|
|
|
{
|
2022-04-28 13:44:24 +00:00
|
|
|
std::array<uint64_t, HistorySize> date_;
|
2022-10-25 16:14:42 +00:00
|
|
|
std::array<size_t, HistorySize> size_;
|
2021-10-10 01:12:03 +00:00
|
|
|
uint64_t cache_time_;
|
2022-10-25 16:14:42 +00:00
|
|
|
tr_bytes_per_second_t cache_val_;
|
2021-10-11 18:11:24 +00:00
|
|
|
int newest_;
|
2021-10-10 01:12:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Band
|
|
|
|
{
|
|
|
|
RateControl raw_;
|
|
|
|
RateControl piece_;
|
2022-11-21 16:19:45 +00:00
|
|
|
size_t bytes_left_;
|
2022-10-25 16:14:42 +00:00
|
|
|
tr_bytes_per_second_t desired_speed_bps_;
|
2022-03-18 16:34:29 +00:00
|
|
|
bool is_limited_ = false;
|
|
|
|
bool honor_parent_limits_ = true;
|
2021-10-10 01:12:03 +00:00
|
|
|
};
|
|
|
|
|
2023-01-27 20:25:08 +00:00
|
|
|
static tr_bytes_per_second_t getSpeedBytesPerSecond(RateControl& r, unsigned int interval_msec, uint64_t now);
|
2022-03-18 13:11:59 +00:00
|
|
|
|
2022-06-29 01:48:39 +00:00
|
|
|
[[nodiscard]] constexpr auto* parent() noexcept
|
|
|
|
{
|
|
|
|
return parent_;
|
|
|
|
}
|
|
|
|
|
2023-01-27 20:25:08 +00:00
|
|
|
void deparent() noexcept;
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2021-10-10 01:12:03 +00:00
|
|
|
static void notifyBandwidthConsumedBytes(uint64_t now, RateControl* r, size_t size);
|
2008-11-24 04:21:23 +00:00
|
|
|
|
2022-11-21 16:19:45 +00:00
|
|
|
[[nodiscard]] size_t clamp(uint64_t now, tr_direction dir, size_t byte_count) const;
|
2009-03-04 19:52:57 +00:00
|
|
|
|
2022-12-19 23:31:24 +00:00
|
|
|
static void phaseOne(std::vector<tr_peerIo*>& peers, tr_direction dir);
|
2009-03-04 19:52:57 +00:00
|
|
|
|
2021-10-09 12:52:09 +00:00
|
|
|
void allocateBandwidth(
|
|
|
|
tr_priority_t parent_priority,
|
|
|
|
unsigned int 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
|
|
|
|
2021-10-12 06:04:22 +00:00
|
|
|
mutable std::array<Band, 2> band_ = {};
|
2022-06-29 20:08:58 +00:00
|
|
|
std::vector<tr_bandwidth*> children_;
|
|
|
|
tr_bandwidth* parent_ = nullptr;
|
2022-08-30 17:38:30 +00:00
|
|
|
std::weak_ptr<tr_peerIo> peer_;
|
2021-10-12 06:04:22 +00:00
|
|
|
tr_priority_t priority_ = 0;
|
2021-10-09 12:52:09 +00:00
|
|
|
};
|
2008-11-25 21:35:17 +00:00
|
|
|
|
2009-05-29 19:17:12 +00:00
|
|
|
/* @} */
|