source: trunk/libtransmission/cache.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: 11.0 KB
Line 
1/*
2 * This file Copyright (C) 2010-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: cache.c 14644 2015-12-29 19:37:31Z mikedld $
8 */
9
10#include <stdlib.h> /* qsort () */
11
12#include <event2/buffer.h>
13
14#include "transmission.h"
15#include "cache.h"
16#include "inout.h"
17#include "log.h"
18#include "peer-common.h" /* MAX_BLOCK_SIZE */
19#include "ptrarray.h"
20#include "torrent.h"
21#include "trevent.h"
22#include "utils.h"
23
24#define MY_NAME "Cache"
25
26#define dbgmsg(...) \
27  do \
28    { \
29      if (tr_logGetDeepEnabled ()) \
30        tr_logAddDeep (__FILE__, __LINE__, MY_NAME, __VA_ARGS__); \
31    } \
32  while (0)
33
34/****
35*****
36****/
37
38struct cache_block
39{
40  tr_torrent * tor;
41
42  tr_piece_index_t piece;
43  uint32_t offset;
44  uint32_t length;
45
46  time_t time;
47  tr_block_index_t block;
48
49  struct evbuffer * evbuf;
50};
51
52struct tr_cache
53{
54  tr_ptrArray blocks;
55  int max_blocks;
56  size_t max_bytes;
57
58  size_t disk_writes;
59  size_t disk_write_bytes;
60  size_t cache_writes;
61  size_t cache_write_bytes;
62};
63
64/****
65*****
66****/
67
68struct run_info
69{
70  int pos;
71  int rank;
72  time_t last_block_time;
73  bool is_multi_piece;
74  bool is_piece_done;
75  unsigned int len;
76};
77
78
79/* return a count of how many contiguous blocks there are starting at this pos */
80static int
81getBlockRun (const tr_cache * cache, int pos, struct run_info * info)
82{
83  int i;
84  const int n = tr_ptrArraySize (&cache->blocks);
85  const struct cache_block * const * blocks = (const struct cache_block* const *) tr_ptrArrayBase (&cache->blocks);
86  const struct cache_block * ref = blocks[pos];
87  tr_block_index_t block = ref->block;
88
89  for (i=pos; i<n; ++i, ++block)
90    {
91      const struct cache_block * b = blocks[i];
92      if (b->block != block)
93        break;
94      if (b->tor != ref->tor)
95        break;
96      //fprintf (stderr, "pos %d tor %d block %zu time %zu\n", i, b->tor->uniqueId, (size_t)b->block, (size_t)b->time);
97    }
98
99  //fprintf (stderr, "run is %d long from [%d to %d)\n", (int)(i-pos), i, (int)pos);
100
101  if (info != NULL)
102    {
103      const struct cache_block * b = blocks[i-1];
104      info->last_block_time = b->time;
105      info->is_piece_done = tr_torrentPieceIsComplete (b->tor, b->piece);
106      info->is_multi_piece = b->piece != blocks[pos]->piece;
107      info->len = i - pos;
108      info->pos = pos;
109    }
110
111  return i-pos;
112}
113
114/* higher rank comes before lower rank */
115static int
116compareRuns (const void * va, const void * vb)
117{
118  const struct run_info * a = va;
119  const struct run_info * b = vb;
120  return b->rank - a->rank;
121}
122
123enum
124{
125  MULTIFLAG   = 0x1000,
126  DONEFLAG    = 0x2000,
127  SESSIONFLAG = 0x4000
128};
129
130/* Calculte runs
131 *   - Stale runs, runs sitting in cache for a long time or runs not growing, get priority.
132 *     Returns number of runs.
133 */
134static int
135calcRuns (tr_cache * cache, struct run_info * runs)
136{
137  const int n = tr_ptrArraySize (&cache->blocks);
138  int i = 0, pos;
139  const time_t now = tr_time ();
140
141  for (pos = 0; pos < n; pos += runs[i++].len)
142    {
143      int rank = getBlockRun (cache, pos, &runs[i]);
144
145      /* This adds ~2 to the relative length of a run for every minute it has
146       * languished in the cache. */
147      rank += (now - runs[i].last_block_time) / 32;
148
149      /* Flushing stale blocks should be a top priority as the probability of them
150       * growing is very small, for blocks on piece boundaries, and nonexistant for
151       * blocks inside pieces. */
152      rank |= runs[i].is_piece_done ? DONEFLAG : 0;
153
154      /* Move the multi piece runs higher */
155      rank |= runs[i].is_multi_piece ? MULTIFLAG : 0;
156
157      runs[i].rank = rank;
158
159      //fprintf (stderr,"block run at pos %d of length %d and age %ld adjusted +%d\n",runs[i].pos,runs[i].len,now-runs[i].last_block_time,rank-runs[i].len);
160    }
161
162  //fprintf (stderr, "%d block runs\n", i);
163  qsort (runs, i, sizeof (struct run_info), compareRuns);
164  return i;
165}
166
167static int
168flushContiguous (tr_cache * cache, int pos, int n)
169{
170  int i;
171  int err = 0;
172  uint8_t * buf = tr_new (uint8_t, n * MAX_BLOCK_SIZE);
173  uint8_t * walk = buf;
174  struct cache_block ** blocks = (struct cache_block**) tr_ptrArrayBase (&cache->blocks);
175
176  struct cache_block * b = blocks[pos];
177  tr_torrent * tor = b->tor;
178  const tr_piece_index_t piece = b->piece;
179  const uint32_t offset = b->offset;
180
181  for (i=pos; i<pos+n; ++i)
182    {
183      b = blocks[i];
184      evbuffer_copyout (b->evbuf, walk, b->length);
185      walk += b->length;
186      evbuffer_free (b->evbuf);
187      tr_free (b);
188    }
189  tr_ptrArrayErase (&cache->blocks, pos, pos+n);
190
191  err = tr_ioWrite (tor, piece, offset, walk-buf, buf);
192  tr_free (buf);
193
194  ++cache->disk_writes;
195  cache->disk_write_bytes += walk-buf;
196  return err;
197}
198
199static int
200flushRuns (tr_cache * cache, struct run_info * runs, int n)
201{
202  int i;
203  int err = 0;
204
205  for (i=0; !err && i<n; i++)
206    {
207      int j;
208
209      err = flushContiguous (cache, runs[i].pos, runs[i].len);
210
211      for (j=i+1; j<n; j++)
212        if (runs[j].pos > runs[i].pos)
213          runs[j].pos -= runs[i].len;
214    }
215
216  return err;
217}
218
219static int
220cacheTrim (tr_cache * cache)
221{
222  int err = 0;
223
224  if (tr_ptrArraySize (&cache->blocks) > cache->max_blocks)
225    {
226      /* Amount of cache that should be removed by the flush. This influences how large
227       * runs can grow as well as how often flushes will happen. */
228      const int cacheCutoff = 1 + cache->max_blocks / 4;
229      struct run_info * runs = tr_new (struct run_info, tr_ptrArraySize (&cache->blocks));
230      int i=0, j=0;
231
232      calcRuns (cache, runs);
233      while (j < cacheCutoff)
234        j += runs[i++].len;
235      err = flushRuns (cache, runs, i);
236      tr_free (runs);
237    }
238
239  return err;
240}
241
242/***
243****
244***/
245
246static int
247getMaxBlocks (int64_t max_bytes)
248{
249  return max_bytes / (double)MAX_BLOCK_SIZE;
250}
251
252int
253tr_cacheSetLimit (tr_cache * cache, int64_t max_bytes)
254{
255  char buf[128];
256
257  cache->max_bytes = max_bytes;
258  cache->max_blocks = getMaxBlocks (max_bytes);
259
260  tr_formatter_mem_B (buf, cache->max_bytes, sizeof (buf));
261  tr_logAddNamedDbg (MY_NAME, "Maximum cache size set to %s (%d blocks)", buf, cache->max_blocks);
262
263  return cacheTrim (cache);
264}
265
266int64_t
267tr_cacheGetLimit (const tr_cache * cache)
268{
269  return cache->max_bytes;
270}
271
272tr_cache *
273tr_cacheNew (int64_t max_bytes)
274{
275  tr_cache * cache = tr_new0 (tr_cache, 1);
276  cache->blocks = TR_PTR_ARRAY_INIT;
277  cache->max_bytes = max_bytes;
278  cache->max_blocks = getMaxBlocks (max_bytes);
279  return cache;
280}
281
282void
283tr_cacheFree (tr_cache * cache)
284{
285  assert (tr_ptrArrayEmpty (&cache->blocks));
286  tr_ptrArrayDestruct (&cache->blocks, NULL);
287  tr_free (cache);
288}
289
290/***
291****
292***/
293
294static int
295cache_block_compare (const void * va, const void * vb)
296{
297  const struct cache_block * a = va;
298  const struct cache_block * b = vb;
299
300  /* primary key: torrent id */
301  if (a->tor->uniqueId != b->tor->uniqueId)
302    return a->tor->uniqueId < b->tor->uniqueId ? -1 : 1;
303
304  /* secondary key: block # */
305  if (a->block != b->block)
306    return a->block < b->block ? -1 : 1;
307
308  /* they're equal */
309  return 0;
310}
311
312static struct cache_block *
313findBlock (tr_cache           * cache,
314           tr_torrent         * torrent,
315           tr_piece_index_t     piece,
316           uint32_t             offset)
317{
318  struct cache_block key;
319  key.tor = torrent;
320  key.block = _tr_block (torrent, piece, offset);
321  return tr_ptrArrayFindSorted (&cache->blocks, &key, cache_block_compare);
322}
323
324int
325tr_cacheWriteBlock (tr_cache         * cache,
326                    tr_torrent       * torrent,
327                    tr_piece_index_t   piece,
328                    uint32_t           offset,
329                    uint32_t           length,
330                    struct evbuffer  * writeme)
331{
332  struct cache_block * cb = findBlock (cache, torrent, piece, offset);
333
334  assert (tr_amInEventThread (torrent->session));
335
336  if (cb == NULL)
337    {
338      cb = tr_new (struct cache_block, 1);
339      cb->tor = torrent;
340      cb->piece = piece;
341      cb->offset = offset;
342      cb->length = length;
343      cb->block = _tr_block (torrent, piece, offset);
344      cb->evbuf = evbuffer_new ();
345      tr_ptrArrayInsertSorted (&cache->blocks, cb, cache_block_compare);
346    }
347
348  cb->time = tr_time ();
349
350  assert (cb->length == length);
351  evbuffer_drain (cb->evbuf, evbuffer_get_length (cb->evbuf));
352  evbuffer_remove_buffer (writeme, cb->evbuf, cb->length);
353
354  cache->cache_writes++;
355  cache->cache_write_bytes += cb->length;
356
357  return cacheTrim (cache);
358}
359
360int
361tr_cacheReadBlock (tr_cache         * cache,
362                   tr_torrent       * torrent,
363                   tr_piece_index_t   piece,
364                   uint32_t           offset,
365                   uint32_t           len,
366                   uint8_t          * setme)
367{
368  int err = 0;
369  struct cache_block * cb = findBlock (cache, torrent, piece, offset);
370
371  if (cb)
372    evbuffer_copyout (cb->evbuf, setme, len);
373  else
374    err = tr_ioRead (torrent, piece, offset, len, setme);
375
376  return err;
377}
378
379int
380tr_cachePrefetchBlock (tr_cache         * cache,
381                       tr_torrent       * torrent,
382                       tr_piece_index_t   piece,
383                       uint32_t           offset,
384                       uint32_t           len)
385{
386  int err = 0;
387  struct cache_block * cb = findBlock (cache, torrent, piece, offset);
388
389  if (cb == NULL)
390    err = tr_ioPrefetch (torrent, piece, offset, len);
391
392  return err;
393}
394
395/***
396****
397***/
398
399static int
400findBlockPos (tr_cache * cache, tr_torrent * torrent, tr_piece_index_t block)
401{
402  struct cache_block key;
403  key.tor = torrent;
404  key.block = block;
405  return tr_ptrArrayLowerBound (&cache->blocks, &key, cache_block_compare, NULL);
406}
407
408int tr_cacheFlushDone (tr_cache * cache)
409{
410  int err = 0;
411
412  if (tr_ptrArraySize (&cache->blocks) > 0)
413    {
414      int i, n;
415      struct run_info * runs;
416
417      runs = tr_new (struct run_info, tr_ptrArraySize (&cache->blocks));
418      i = 0;
419      n = calcRuns (cache, runs);
420
421      while (i < n && (runs[i].is_piece_done || runs[i].is_multi_piece))
422        runs[i++].rank |= SESSIONFLAG;
423
424      err = flushRuns (cache, runs, i);
425      tr_free (runs);
426    }
427
428  return err;
429}
430
431int
432tr_cacheFlushFile (tr_cache * cache, tr_torrent * torrent, tr_file_index_t i)
433{
434  int pos;
435  int err = 0;
436  tr_block_index_t first;
437  tr_block_index_t last;
438
439  tr_torGetFileBlockRange (torrent, i, &first, &last);
440  pos = findBlockPos (cache, torrent, first);
441  dbgmsg ("flushing file %d from cache to disk: blocks [%zu...%zu]", (int)i, (size_t)first, (size_t)last);
442
443  /* flush out all the blocks in that file */
444  while (!err && (pos < tr_ptrArraySize (&cache->blocks)))
445    {
446      const struct cache_block * b = tr_ptrArrayNth (&cache->blocks, pos);
447
448      if (b->tor != torrent)
449        break;
450      if ((b->block < first) || (b->block > last))
451        break;
452
453      err = flushContiguous (cache, pos, getBlockRun (cache, pos, NULL));
454    }
455
456  return err;
457}
458
459int
460tr_cacheFlushTorrent (tr_cache * cache, tr_torrent * torrent)
461{
462  int err = 0;
463  const int pos = findBlockPos (cache, torrent, 0);
464
465  /* flush out all the blocks in that torrent */
466  while (!err && (pos < tr_ptrArraySize (&cache->blocks)))
467    {
468      const struct cache_block * b = tr_ptrArrayNth (&cache->blocks, pos);
469
470      if (b->tor != torrent)
471        break;
472
473      err = flushContiguous (cache, pos, getBlockRun (cache, pos, NULL));
474    }
475
476  return err;
477}
Note: See TracBrowser for help on using the repository browser.