transmission/libtransmission/bandwidth.h

267 lines
8.8 KiB
C

/*
* This file Copyright (C) Mnemosyne LLC
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
*
* $Id$
*/
#ifndef __TRANSMISSION__
#error only libtransmission should #include this header.
#endif
#ifndef TR_BANDWIDTH_H
#define TR_BANDWIDTH_H
#include <assert.h>
#include "transmission.h"
#include "ptrarray.h"
#include "utils.h" /* tr_new(), tr_free() */
struct tr_peerIo;
/**
* @addtogroup networked_io Networked IO
* @{
*/
/* these are PRIVATE IMPLEMENTATION details that should not be touched.
* it's included in the header for inlining and composition. */
enum
{
HISTORY_MSEC = 2000u,
INTERVAL_MSEC = HISTORY_MSEC,
GRANULARITY_MSEC = 200,
HISTORY_SIZE = ( INTERVAL_MSEC / GRANULARITY_MSEC ),
BANDWIDTH_MAGIC_NUMBER = 43143
};
/* these are PRIVATE IMPLEMENTATION details that should not be touched.
* it's included in the header for inlining and composition. */
struct bratecontrol
{
int newest;
struct { uint64_t date, size; } transfers[HISTORY_SIZE];
uint64_t cache_time;
unsigned int cache_val;
};
/* these are PRIVATE IMPLEMENTATION details that should not be touched.
* it's included in the header for inlining and composition. */
struct tr_band
{
bool isLimited;
bool honorParentLimits;
unsigned int bytesLeft;
unsigned int desiredSpeed_Bps;
struct bratecontrol raw;
struct bratecontrol piece;
};
/**
* Bandwidth is an object for measuring and constraining bandwidth speeds.
*
* Bandwidth objects can be "stacked" so that a peer can be made to obey
* multiple constraints (for example, obeying the global speed limit and a
* per-torrent speed limit).
*
* HIERARCHY
*
* Transmission's bandwidth hierarchy is a tree.
* 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.
*
* tr_session also owns a tr_handshake's bandwidths, so that the handshake
* I/O can be counted in the global raw totals. When the handshake is done,
* the bandwidth's ownership passes to a tr_peer.
*
* MEASURING
*
* When you ask a bandwidth object for its speed, it gives the speed of the
* subtree underneath it as well. So you can get Transmission's overall
* speed by quering tr_session's bandwidth, per-torrent speeds by asking
* tr_torrent's bandwidth, and per-peer speeds by asking tr_peer's bandwidth.
*
* CONSTRAINING
*
* Call tr_bandwidthAllocate() periodically. tr_bandwidth knows its current
* speed and will decide how many bytes to make available over the
* user-specified period to reach the user-specified desired speed.
* If appropriate, it notifies its peer-ios that new bandwidth is available.
*
* tr_bandwidthAllocate() operates on the tr_bandwidth subtree, so usually
* you'll only need to invoke it for the top-level tr_session bandwidth.
*
* The peer-ios all have a pointer to their associated tr_bandwidth object,
* and call tr_bandwidthClamp() before performing I/O to see how much
* bandwidth they can safely use.
*/
typedef struct tr_bandwidth
{
/* these are PRIVATE IMPLEMENTATION details that should not be touched.
* it's included in the header for inlining and composition. */
struct tr_band band[2];
struct tr_bandwidth * parent;
tr_priority_t priority;
int magicNumber;
tr_session * session;
tr_ptrArray children; /* struct tr_bandwidth */
struct tr_peerIo * peer;
}
tr_bandwidth;
/**
***
**/
void tr_bandwidthConstruct( tr_bandwidth * bandwidth,
tr_session * session,
tr_bandwidth * parent );
void tr_bandwidthDestruct( tr_bandwidth * bandwidth );
/** @brief test to see if the pointer refers to a live bandwidth object */
static inline bool tr_isBandwidth( const tr_bandwidth * b )
{
return ( b != NULL ) && ( b->magicNumber == BANDWIDTH_MAGIC_NUMBER );
}
/******
*******
******/
/**
* @brief Set the desired speed for this bandwidth subtree.
* @see tr_bandwidthAllocate
* @see tr_bandwidthGetDesiredSpeed
*/
static inline bool tr_bandwidthSetDesiredSpeed_Bps( tr_bandwidth * bandwidth,
tr_direction dir,
unsigned int desiredSpeed )
{
unsigned int * value = &bandwidth->band[dir].desiredSpeed_Bps;
const bool didChange = desiredSpeed != *value;
*value = desiredSpeed;
return didChange;
}
/**
* @brief Get the desired speed for the bandwidth subtree.
* @see tr_bandwidthSetDesiredSpeed
*/
static inline double
tr_bandwidthGetDesiredSpeed_Bps( const tr_bandwidth * bandwidth, tr_direction dir )
{
return bandwidth->band[dir].desiredSpeed_Bps;
}
/**
* @brief Set whether or not this bandwidth should throttle its peer-io's speeds
*/
static inline bool tr_bandwidthSetLimited( tr_bandwidth * bandwidth,
tr_direction dir,
bool isLimited )
{
bool * value = &bandwidth->band[dir].isLimited;
const bool didChange = isLimited != *value;
*value = isLimited;
return didChange;
}
/**
* @return nonzero if this bandwidth throttles its peer-ios speeds
*/
static inline bool tr_bandwidthIsLimited( const tr_bandwidth * bandwidth,
tr_direction dir )
{
return bandwidth->band[dir].isLimited;
}
/**
* @brief allocate the next period_msec's worth of bandwidth for the peer-ios to consume
*/
void tr_bandwidthAllocate ( tr_bandwidth * bandwidth,
tr_direction direction,
unsigned int period_msec );
/**
* @brief clamps byteCount down to a number that this bandwidth will allow to be consumed
*/
unsigned int tr_bandwidthClamp ( const tr_bandwidth * bandwidth,
tr_direction direction,
unsigned int byteCount );
/******
*******
******/
/** @brief Get the raw total of bytes read or sent by this bandwidth subtree. */
unsigned int tr_bandwidthGetRawSpeed_Bps( const tr_bandwidth * bandwidth,
const uint64_t now,
const tr_direction direction );
/** @brief Get the number of piece data bytes read or sent by this bandwidth subtree. */
unsigned int tr_bandwidthGetPieceSpeed_Bps( const tr_bandwidth * bandwidth,
const uint64_t now,
const tr_direction direction );
/**
* @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.
*/
void tr_bandwidthUsed ( tr_bandwidth * bandwidth,
tr_direction direction,
size_t byteCount,
bool isPieceData,
uint64_t now );
/******
*******
******/
void tr_bandwidthSetParent ( tr_bandwidth * bandwidth,
tr_bandwidth * parent );
/**
* 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.
* But when we set a torrent's speed mode to TR_SPEEDLIMIT_UNLIMITED, then
* in that particular case we want to ignore the global speed limit...
*/
static inline bool tr_bandwidthHonorParentLimits ( tr_bandwidth * bandwidth,
tr_direction direction,
bool isEnabled )
{
bool * value = &bandwidth->band[direction].honorParentLimits;
const bool didChange = isEnabled != *value;
*value = isEnabled;
return didChange;
}
static inline bool tr_bandwidthAreParentLimitsHonored( const tr_bandwidth * bandwidth,
tr_direction direction )
{
assert( tr_isBandwidth( bandwidth ) );
assert( tr_isDirection( direction ) );
return bandwidth->band[direction].honorParentLimits;
}
/******
*******
******/
void tr_bandwidthSetPeer( tr_bandwidth * bandwidth,
struct tr_peerIo * peerIo );
/* @} */
#endif