source: trunk/libtransmission/bandwidth.c

Last change on this file was 14644, checked in by mikedld, 5 years ago

Remove useless checks and definitions (C99)

Now that MSVC support for C99 is quite good, remove previously needed but
now unused checks and definitions, like PRI* format macros (including
PRIdMAX and TR_PRIuSIZE, replaced with %jd and %zu) and inline macro.
Also, remove ssize_t typedef and replace few occurences with ev_ssize_t.
Also, remove check for stdbool.h availability (guaranteed by C99) and
include it unconditionally (except when in C++ mode).

  • Property svn:keywords set to Date Rev Author Id
File size: 10.6 KB
Line 
1/*
2 * This file Copyright (C) 2008-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: bandwidth.c 14644 2015-12-29 19:37:31Z mikedld $
8 */
9
10#include <assert.h>
11#include <string.h> /* memset () */
12
13#include "transmission.h"
14#include "bandwidth.h"
15#include "crypto-utils.h" /* tr_rand_int_weak () */
16#include "log.h"
17#include "peer-io.h"
18#include "utils.h"
19
20#define dbgmsg(...) \
21  do \
22    { \
23      if (tr_logGetDeepEnabled ()) \
24        tr_logAddDeep (__FILE__, __LINE__, NULL, __VA_ARGS__); \
25    } \
26  while (0)
27
28/***
29****
30***/
31
32static unsigned int
33getSpeed_Bps (const struct bratecontrol * r, unsigned int interval_msec, uint64_t now)
34{
35  if (!now)
36    now = tr_time_msec ();
37
38  if (now != r->cache_time)
39    {
40      int i = r->newest;
41      uint64_t bytes = 0;
42      const uint64_t cutoff = now - interval_msec;
43      struct bratecontrol * rvolatile = (struct bratecontrol*) r;
44
45      for (;;)
46        {
47          if (r->transfers[i].date <= cutoff)
48            break;
49
50          bytes += r->transfers[i].size;
51
52          if (--i == -1)
53            i = HISTORY_SIZE - 1; /* circular history */
54
55          if (i == r->newest)
56            break; /* we've come all the way around */
57        }
58
59      rvolatile->cache_val = (unsigned int)((bytes * 1000u) / interval_msec);
60      rvolatile->cache_time = now;
61    }
62
63  return r->cache_val;
64}
65
66static void
67bytesUsed (const uint64_t now, struct bratecontrol * r, size_t size)
68{
69  if (r->transfers[r->newest].date + GRANULARITY_MSEC >= now)
70    {
71      r->transfers[r->newest].size += size;
72    }
73  else
74    {
75      if (++r->newest == HISTORY_SIZE)
76        r->newest = 0;
77      r->transfers[r->newest].date = now;
78      r->transfers[r->newest].size = size;
79    }
80
81  /* invalidate cache_val*/
82  r->cache_time = 0;
83}
84
85/******
86*******
87*******
88******/
89
90static int
91compareBandwidth (const void * va, const void * vb)
92{
93  const tr_bandwidth * a = va;
94  const tr_bandwidth * b = vb;
95  return a->uniqueKey - b->uniqueKey;
96}
97
98/***
99****
100***/
101
102void
103tr_bandwidthConstruct (tr_bandwidth * b, tr_session * session, tr_bandwidth * parent)
104{
105  static unsigned int uniqueKey = 0;
106
107  b->session = session;
108  b->children = TR_PTR_ARRAY_INIT;
109  b->magicNumber = BANDWIDTH_MAGIC_NUMBER;
110  b->uniqueKey = uniqueKey++;
111  b->band[TR_UP].honorParentLimits = true;
112  b->band[TR_DOWN].honorParentLimits = true;
113  tr_bandwidthSetParent (b, parent);
114}
115
116void
117tr_bandwidthDestruct (tr_bandwidth * b)
118{
119  assert (tr_isBandwidth (b));
120
121  tr_bandwidthSetParent (b, NULL);
122  tr_ptrArrayDestruct (&b->children, NULL);
123
124  memset (b, ~0, sizeof (tr_bandwidth));
125}
126
127/***
128****
129***/
130
131void
132tr_bandwidthSetParent (tr_bandwidth  * b,
133                       tr_bandwidth  * parent)
134{
135  assert (tr_isBandwidth (b));
136  assert (b != parent);
137
138  if (b->parent)
139    {
140      assert (tr_isBandwidth (b->parent));
141      tr_ptrArrayRemoveSortedPointer (&b->parent->children, b, compareBandwidth);
142      b->parent = NULL;
143    }
144
145  if (parent)
146    {
147      assert (tr_isBandwidth (parent));
148      assert (parent->parent != b);
149
150      assert (tr_ptrArrayFindSorted (&parent->children, b, compareBandwidth) == NULL);
151      tr_ptrArrayInsertSorted (&parent->children, b, compareBandwidth);
152      assert (tr_ptrArrayFindSorted (&parent->children, b, compareBandwidth) == b);
153      b->parent = parent;
154    }
155}
156
157/***
158****
159***/
160
161static void
162allocateBandwidth (tr_bandwidth  * b,
163                   tr_priority_t   parent_priority,
164                   tr_direction    dir,
165                   unsigned int    period_msec,
166                   tr_ptrArray   * peer_pool)
167{
168  const tr_priority_t priority = MAX (parent_priority, b->priority);
169
170  assert (tr_isBandwidth (b));
171  assert (tr_isDirection (dir));
172
173  /* set the available bandwidth */
174  if (b->band[dir].isLimited)
175    {
176      const uint64_t nextPulseSpeed = b->band[dir].desiredSpeed_Bps;
177      b->band[dir].bytesLeft = nextPulseSpeed * period_msec / 1000u;
178    }
179
180  /* add this bandwidth's peer, if any, to the peer pool */
181  if (b->peer != NULL)
182    {
183      b->peer->priority = priority;
184      tr_ptrArrayAppend (peer_pool, b->peer);
185    }
186
187  /* traverse & repeat for the subtree */
188  if (1)
189    {
190      int i;
191      struct tr_bandwidth ** children = (struct tr_bandwidth**) tr_ptrArrayBase (&b->children);
192      const int n = tr_ptrArraySize (&b->children);
193      for (i=0; i<n; ++i)
194        allocateBandwidth (children[i], priority, dir, period_msec, peer_pool);
195    }
196}
197
198static void
199phaseOne (tr_ptrArray * peerArray, tr_direction dir)
200{
201  int n;
202  int peerCount = tr_ptrArraySize (peerArray);
203  struct tr_peerIo ** peers = (struct tr_peerIo**) tr_ptrArrayBase (peerArray);
204
205  /* First phase of IO. Tries to distribute bandwidth fairly to keep faster
206   * peers from starving the others. Loop through the peers, giving each a
207   * small chunk of bandwidth. Keep looping until we run out of bandwidth
208   * and/or peers that can use it */
209  n = peerCount;
210  dbgmsg ("%d peers to go round-robin for %s", n, (dir==TR_UP?"upload":"download"));
211  while (n > 0)
212    {
213      const int i = tr_rand_int_weak (n); /* pick a peer at random */
214
215      /* value of 3000 bytes chosen so that when using uTP we'll send a full-size
216       * frame right away and leave enough buffered data for the next frame to go
217       * out in a timely manner. */
218      const size_t increment = 3000;
219
220      const int bytesUsed = tr_peerIoFlush (peers[i], dir, increment);
221
222      dbgmsg ("peer #%d of %d used %d bytes in this pass", i, n, bytesUsed);
223
224      if (bytesUsed != (int)increment)
225        {
226          /* peer is done writing for now; move it to the end of the list */
227          tr_peerIo * pio = peers[i];
228          peers[i] = peers[n-1];
229          peers[n-1] = pio;
230          --n;
231        }
232    }
233}
234
235void
236tr_bandwidthAllocate (tr_bandwidth  * b,
237                      tr_direction    dir,
238                      unsigned int    period_msec)
239{
240  int i, peerCount;
241  tr_ptrArray tmp = TR_PTR_ARRAY_INIT;
242  tr_ptrArray low = TR_PTR_ARRAY_INIT;
243  tr_ptrArray high = TR_PTR_ARRAY_INIT;
244  tr_ptrArray normal = TR_PTR_ARRAY_INIT;
245  struct tr_peerIo ** peers;
246
247  /* allocateBandwidth () is a helper function with two purposes:
248   * 1. allocate bandwidth to b and its subtree
249   * 2. accumulate an array of all the peerIos from b and its subtree. */
250  allocateBandwidth (b, TR_PRI_LOW, dir, period_msec, &tmp);
251  peers = (struct tr_peerIo**) tr_ptrArrayBase (&tmp);
252  peerCount = tr_ptrArraySize (&tmp);
253
254  for (i=0; i<peerCount; ++i)
255    {
256      tr_peerIo * io = peers[i];
257      tr_peerIoRef (io);
258
259      tr_peerIoFlushOutgoingProtocolMsgs (io);
260
261      switch (io->priority)
262        {
263          case TR_PRI_HIGH:   tr_ptrArrayAppend (&high,   io); /* fall through */
264          case TR_PRI_NORMAL: tr_ptrArrayAppend (&normal, io); /* fall through */
265          default:            tr_ptrArrayAppend (&low,    io);
266        }
267    }
268
269  /* First phase of IO. Tries to distribute bandwidth fairly to keep faster
270   * peers from starving the others. Loop through the peers, giving each a
271   * small chunk of bandwidth. Keep looping until we run out of bandwidth
272   * and/or peers that can use it */
273  phaseOne (&high, dir);
274  phaseOne (&normal, dir);
275  phaseOne (&low, dir);
276
277  /* Second phase of IO. To help us scale in high bandwidth situations,
278   * enable on-demand IO for peers with bandwidth left to burn.
279   * This on-demand IO is enabled until (1) the peer runs out of bandwidth,
280   * or (2) the next tr_bandwidthAllocate () call, when we start over again. */
281  for (i=0; i<peerCount; ++i)
282    tr_peerIoSetEnabled (peers[i], dir, tr_peerIoHasBandwidthLeft (peers[i], dir));
283
284  for (i=0; i<peerCount; ++i)
285    tr_peerIoUnref (peers[i]);
286
287  /* cleanup */
288  tr_ptrArrayDestruct (&normal, NULL);
289  tr_ptrArrayDestruct (&high, NULL);
290  tr_ptrArrayDestruct (&low, NULL);
291  tr_ptrArrayDestruct (&tmp, NULL);
292}
293
294void
295tr_bandwidthSetPeer (tr_bandwidth * b, tr_peerIo * peer)
296{
297  assert (tr_isBandwidth (b));
298  assert ((peer == NULL) || tr_isPeerIo (peer));
299
300  b->peer = peer;
301}
302
303/***
304****
305***/
306
307static unsigned int
308bandwidthClamp (const tr_bandwidth  * b,
309                uint64_t              now,
310                tr_direction          dir,
311                unsigned int          byteCount)
312{
313  assert (tr_isBandwidth (b));
314  assert (tr_isDirection (dir));
315
316  if (b)
317    {
318      if (b->band[dir].isLimited)
319        {
320          byteCount = MIN (byteCount, b->band[dir].bytesLeft);
321
322          /* if we're getting close to exceeding the speed limit,
323           * clamp down harder on the bytes available */
324          if (byteCount > 0)
325            {
326              double current;
327              double desired;
328              double r;
329
330              if (now == 0)
331                now = tr_time_msec ();
332
333              current = tr_bandwidthGetRawSpeed_Bps (b, now, TR_DOWN);
334              desired = tr_bandwidthGetDesiredSpeed_Bps (b, TR_DOWN);
335              r = desired >= 1 ? current / desired : 0;
336
337                   if (r > 1.0) byteCount = 0;
338              else if (r > 0.9) byteCount *= 0.8;
339              else if (r > 0.8) byteCount *= 0.9;
340            }
341        }
342
343      if (b->parent && b->band[dir].honorParentLimits && (byteCount > 0))
344        byteCount = bandwidthClamp (b->parent, now, dir, byteCount);
345    }
346
347  return byteCount;
348}
349
350unsigned int
351tr_bandwidthClamp (const tr_bandwidth  * b,
352                   tr_direction          dir,
353                   unsigned int          byteCount)
354{
355  return bandwidthClamp (b, 0, dir, byteCount);
356}
357
358
359unsigned int
360tr_bandwidthGetRawSpeed_Bps (const tr_bandwidth * b, const uint64_t now, const tr_direction dir)
361{
362  assert (tr_isBandwidth (b));
363  assert (tr_isDirection (dir));
364
365  return getSpeed_Bps (&b->band[dir].raw, HISTORY_MSEC, now);
366}
367
368unsigned int
369tr_bandwidthGetPieceSpeed_Bps (const tr_bandwidth * b, const uint64_t now, const tr_direction dir)
370{
371  assert (tr_isBandwidth (b));
372  assert (tr_isDirection (dir));
373
374  return getSpeed_Bps (&b->band[dir].piece, HISTORY_MSEC, now);
375}
376
377void
378tr_bandwidthUsed (tr_bandwidth  * b,
379                  tr_direction    dir,
380                  size_t          byteCount,
381                  bool            isPieceData,
382                  uint64_t        now)
383{
384  struct tr_band * band;
385
386  assert (tr_isBandwidth (b));
387  assert (tr_isDirection (dir));
388
389  band = &b->band[dir];
390
391  if (band->isLimited && isPieceData)
392    band->bytesLeft -= MIN (band->bytesLeft, byteCount);
393
394#ifdef DEBUG_DIRECTION
395if ((dir == DEBUG_DIRECTION) && (band->isLimited))
396fprintf (stderr, "%p consumed %5zu bytes of %5s data... was %6zu, now %6zu left\n",
397         b, byteCount, (isPieceData?"piece":"raw"), oldBytesLeft, band->bytesLeft);
398#endif
399
400  bytesUsed (now, &band->raw, byteCount);
401
402  if (isPieceData)
403    bytesUsed (now, &band->piece, byteCount);
404
405  if (b->parent != NULL)
406    tr_bandwidthUsed (b->parent, dir, byteCount, isPieceData, now);
407}
Note: See TracBrowser for help on using the repository browser.