refactor: modernize cache.cc (#3108)
* Modernize cache.cc: Convert tr_cache to a class * Modernize cache.cc: Replaced ptrArray with vector * refactor: Cache now takes a tr_torrents reference * refactor: add Key type to Cache * refactor: avoid std::back_inserter * refactor: add Cache::flushOldest()
This commit is contained in:
parent
1e796477dc
commit
d34bd0b4d6
|
@ -3,6 +3,7 @@
|
||||||
# untracked files
|
# untracked files
|
||||||
.*/*
|
.*/*
|
||||||
build/*
|
build/*
|
||||||
|
cmake-build-*/*
|
||||||
libtransmission/version.h
|
libtransmission/version.h
|
||||||
web/node_modules/*
|
web/node_modules/*
|
||||||
|
|
||||||
|
|
|
@ -23,3 +23,6 @@ po/*.mo
|
||||||
third-party/miniupnp/miniupnpcstrings.h
|
third-party/miniupnp/miniupnpcstrings.h
|
||||||
web/public_html/transmission-app.js.map
|
web/public_html/transmission-app.js.map
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# CLion IDE build directory
|
||||||
|
/cmake-build-*/
|
||||||
|
|
|
@ -3,8 +3,11 @@
|
||||||
// or any future license endorsed by Mnemosyne LLC.
|
// or any future license endorsed by Mnemosyne LLC.
|
||||||
// License text can be found in the licenses/ folder.
|
// License text can be found in the licenses/ folder.
|
||||||
|
|
||||||
#include <cstdlib> /* qsort() */
|
#include <cstdlib> // std::lldiv()
|
||||||
#include <ctime>
|
#include <iterator> // std::distance(), std::next(), std::prev()
|
||||||
|
#include <limits> // std::numeric_limits<size_t>::max()
|
||||||
|
#include <numeric> // std::accumulate()
|
||||||
|
#include <utility> // std::make_pair()
|
||||||
|
|
||||||
#include <event2/buffer.h>
|
#include <event2/buffer.h>
|
||||||
|
|
||||||
|
@ -14,438 +17,252 @@
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "inout.h"
|
#include "inout.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "ptrarray.h"
|
|
||||||
#include "torrent.h"
|
#include "torrent.h"
|
||||||
|
#include "torrents.h"
|
||||||
#include "tr-assert.h"
|
#include "tr-assert.h"
|
||||||
#include "trevent.h"
|
#include "trevent.h"
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
/****
|
Cache::Key Cache::makeKey(tr_torrent const* torrent, tr_block_info::Location loc) noexcept
|
||||||
*****
|
|
||||||
****/
|
|
||||||
|
|
||||||
struct cache_block
|
|
||||||
{
|
{
|
||||||
tr_torrent* tor;
|
return std::make_pair(torrent->uniqueId, loc.block);
|
||||||
|
}
|
||||||
|
|
||||||
tr_block_info::Location loc;
|
std::pair<Cache::CIter, Cache::CIter> Cache::findContiguous(CIter const begin, CIter const end, CIter const iter) noexcept
|
||||||
uint32_t length;
|
|
||||||
|
|
||||||
time_t time;
|
|
||||||
|
|
||||||
struct evbuffer* evbuf;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tr_cache
|
|
||||||
{
|
{
|
||||||
tr_ptrArray blocks;
|
if (iter == end)
|
||||||
int max_blocks;
|
|
||||||
size_t max_bytes;
|
|
||||||
|
|
||||||
size_t disk_writes;
|
|
||||||
size_t disk_write_bytes;
|
|
||||||
size_t cache_writes;
|
|
||||||
size_t cache_write_bytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
/****
|
|
||||||
*****
|
|
||||||
****/
|
|
||||||
|
|
||||||
struct run_info
|
|
||||||
{
|
|
||||||
int pos;
|
|
||||||
int rank;
|
|
||||||
time_t last_block_time;
|
|
||||||
bool is_multi_piece;
|
|
||||||
bool is_piece_done;
|
|
||||||
unsigned int len;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* return a count of how many contiguous blocks there are starting at this pos */
|
|
||||||
static int getBlockRun(tr_cache const* cache, int pos, struct run_info* info)
|
|
||||||
{
|
|
||||||
int const n = tr_ptrArraySize(&cache->blocks);
|
|
||||||
auto const* const* blocks = (struct cache_block const* const*)tr_ptrArrayBase(&cache->blocks);
|
|
||||||
struct cache_block const* ref = blocks[pos];
|
|
||||||
auto block = ref->loc.block;
|
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
for (int i = pos; i < n; ++i)
|
|
||||||
{
|
{
|
||||||
struct cache_block const* b = blocks[i];
|
return std::make_pair(end, end);
|
||||||
|
}
|
||||||
|
|
||||||
if (b->loc.block != block)
|
auto span_begin = iter;
|
||||||
|
for (auto key = iter->key;;)
|
||||||
|
{
|
||||||
|
if (span_begin == begin)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (b->tor != ref->tor)
|
--key.second;
|
||||||
|
auto const prev = std::prev(span_begin);
|
||||||
|
if (prev->key != key)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto span_end = std::next(iter);
|
||||||
|
for (auto key = iter->key;;)
|
||||||
|
{
|
||||||
|
if (span_end == end)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
++block;
|
++key.second;
|
||||||
++len;
|
if (span_end->key != key)
|
||||||
}
|
|
||||||
|
|
||||||
if (info != nullptr)
|
|
||||||
{
|
{
|
||||||
struct cache_block const* b = blocks[pos + len - 1];
|
break;
|
||||||
info->last_block_time = b->time;
|
}
|
||||||
info->is_piece_done = b->tor->hasPiece(b->loc.piece);
|
|
||||||
info->is_multi_piece = b->loc.piece != blocks[pos]->loc.piece;
|
|
||||||
info->len = len;
|
|
||||||
info->pos = pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return std::make_pair(span_begin, span_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* higher rank comes before lower rank */
|
int Cache::writeContiguous(CIter const begin, CIter const end) const
|
||||||
static int compareRuns(void const* va, void const* vb)
|
|
||||||
{
|
{
|
||||||
auto const* const a = static_cast<struct run_info const*>(va);
|
// join the blocks together into contiguous memory `buf`
|
||||||
auto const* const b = static_cast<struct run_info const*>(vb);
|
auto buf = std::vector<uint8_t>{};
|
||||||
return b->rank - a->rank;
|
auto const buflen = std::accumulate(
|
||||||
}
|
begin,
|
||||||
|
end,
|
||||||
enum
|
size_t{},
|
||||||
{
|
[](size_t sum, auto const& block) { return sum + std::size(block.buf); });
|
||||||
MULTIFLAG = 0x1000,
|
buf.reserve(buflen);
|
||||||
DONEFLAG = 0x2000,
|
for (auto iter = begin; iter != end; ++iter)
|
||||||
SESSIONFLAG = 0x4000
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Calculte runs
|
|
||||||
* - Stale runs, runs sitting in cache for a long time or runs not growing, get priority.
|
|
||||||
* Returns number of runs.
|
|
||||||
*/
|
|
||||||
// TODO: return std::vector
|
|
||||||
static int calcRuns(tr_cache const* cache, struct run_info* runs)
|
|
||||||
{
|
|
||||||
int const n = tr_ptrArraySize(&cache->blocks);
|
|
||||||
int i = 0;
|
|
||||||
time_t const now = tr_time();
|
|
||||||
|
|
||||||
for (int pos = 0; pos < n; pos += runs[i++].len)
|
|
||||||
{
|
{
|
||||||
int rank = getBlockRun(cache, pos, &runs[i]);
|
TR_ASSERT(begin->key.first == iter->key.first);
|
||||||
|
TR_ASSERT(begin->key.second + std::distance(begin, iter) == iter->key.second);
|
||||||
/* This adds ~2 to the relative length of a run for every minute it has
|
buf.insert(std::end(buf), std::begin(iter->buf), std::end(iter->buf));
|
||||||
* languished in the cache. */
|
|
||||||
rank += (now - runs[i].last_block_time) / 32;
|
|
||||||
|
|
||||||
/* Flushing stale blocks should be a top priority as the probability of them
|
|
||||||
* growing is very small, for blocks on piece boundaries, and nonexistant for
|
|
||||||
* blocks inside pieces. */
|
|
||||||
rank |= runs[i].is_piece_done ? DONEFLAG : 0;
|
|
||||||
|
|
||||||
/* Move the multi piece runs higher */
|
|
||||||
rank |= runs[i].is_multi_piece ? MULTIFLAG : 0;
|
|
||||||
|
|
||||||
runs[i].rank = rank;
|
|
||||||
}
|
}
|
||||||
|
TR_ASSERT(std::size(buf) == buflen);
|
||||||
|
|
||||||
qsort(runs, i, sizeof(struct run_info), compareRuns);
|
// save it
|
||||||
return i;
|
auto* const tor = torrents_.get(begin->key.first);
|
||||||
}
|
auto const loc = tor->blockLoc(begin->key.second);
|
||||||
|
|
||||||
static int flushContiguous(tr_cache* cache, int pos, int n)
|
if (auto const err = tr_ioWrite(tor, loc, std::size(buf), std::data(buf)); err != 0)
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
auto* const buf = tr_new(uint8_t, n * tr_block_info::BlockSize);
|
|
||||||
auto* walk = buf;
|
|
||||||
auto** blocks = (struct cache_block**)tr_ptrArrayBase(&cache->blocks);
|
|
||||||
|
|
||||||
auto* b = blocks[pos];
|
|
||||||
auto* const tor = b->tor;
|
|
||||||
auto const loc = b->loc;
|
|
||||||
|
|
||||||
for (int i = 0; i < n; ++i)
|
|
||||||
{
|
{
|
||||||
b = blocks[pos + i];
|
|
||||||
evbuffer_copyout(b->evbuf, walk, b->length);
|
|
||||||
walk += b->length;
|
|
||||||
evbuffer_free(b->evbuf);
|
|
||||||
tr_free(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
tr_ptrArrayErase(&cache->blocks, pos, pos + n);
|
|
||||||
|
|
||||||
err = tr_ioWrite(tor, loc, walk - buf, buf);
|
|
||||||
tr_free(buf);
|
|
||||||
|
|
||||||
++cache->disk_writes;
|
|
||||||
cache->disk_write_bytes += walk - buf;
|
|
||||||
return err;
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
++disk_writes_;
|
||||||
|
disk_write_bytes_ += std::size(buf);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static int flushRuns(tr_cache* cache, struct run_info* runs, int n)
|
size_t Cache::getMaxBlocks(int64_t max_bytes) noexcept
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
for (int i = 0; err == 0 && i < n; i++)
|
|
||||||
{
|
|
||||||
err = flushContiguous(cache, runs[i].pos, runs[i].len);
|
|
||||||
|
|
||||||
for (int j = i + 1; j < n; j++)
|
|
||||||
{
|
|
||||||
if (runs[j].pos > runs[i].pos)
|
|
||||||
{
|
|
||||||
runs[j].pos -= runs[i].len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cacheTrim(tr_cache* cache)
|
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
if (tr_ptrArraySize(&cache->blocks) > cache->max_blocks)
|
|
||||||
{
|
|
||||||
/* Amount of cache that should be removed by the flush. This influences how large
|
|
||||||
* runs can grow as well as how often flushes will happen. */
|
|
||||||
int const cacheCutoff = 1 + cache->max_blocks / 4;
|
|
||||||
auto* const runs = tr_new(struct run_info, tr_ptrArraySize(&cache->blocks));
|
|
||||||
int i = 0;
|
|
||||||
int j = 0;
|
|
||||||
|
|
||||||
calcRuns(cache, runs);
|
|
||||||
|
|
||||||
while (j < cacheCutoff)
|
|
||||||
{
|
|
||||||
j += runs[i++].len;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = flushRuns(cache, runs, i);
|
|
||||||
tr_free(runs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
****
|
|
||||||
***/
|
|
||||||
|
|
||||||
static int getMaxBlocks(int64_t max_bytes)
|
|
||||||
{
|
{
|
||||||
return std::lldiv(max_bytes, tr_block_info::BlockSize).quot;
|
return std::lldiv(max_bytes, tr_block_info::BlockSize).quot;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tr_cacheSetLimit(tr_cache* cache, int64_t max_bytes)
|
int Cache::setLimit(int64_t new_limit)
|
||||||
{
|
{
|
||||||
cache->max_bytes = max_bytes;
|
max_bytes_ = new_limit;
|
||||||
cache->max_blocks = getMaxBlocks(max_bytes);
|
max_blocks_ = getMaxBlocks(new_limit);
|
||||||
|
|
||||||
tr_logAddDebug(
|
tr_logAddDebug(fmt::format("Maximum cache size set to {} ({} blocks)", tr_formatter_mem_B(max_bytes_), max_blocks_));
|
||||||
fmt::format("Maximum cache size set to {} ({} blocks)", tr_formatter_mem_B(cache->max_bytes), cache->max_blocks));
|
|
||||||
|
|
||||||
return cacheTrim(cache);
|
return cacheTrim();
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t tr_cacheGetLimit(tr_cache const* cache)
|
Cache::Cache(tr_torrents& torrents, int64_t max_bytes)
|
||||||
|
: torrents_{ torrents }
|
||||||
|
, max_blocks_(getMaxBlocks(max_bytes))
|
||||||
|
, max_bytes_(max_bytes)
|
||||||
{
|
{
|
||||||
return cache->max_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr_cache* tr_cacheNew(int64_t max_bytes)
|
|
||||||
{
|
|
||||||
auto* const cache = tr_new0(tr_cache, 1);
|
|
||||||
cache->blocks = {};
|
|
||||||
cache->max_bytes = max_bytes;
|
|
||||||
cache->max_blocks = getMaxBlocks(max_bytes);
|
|
||||||
return cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tr_cacheFree(tr_cache* cache)
|
|
||||||
{
|
|
||||||
tr_ptrArrayDestruct(&cache->blocks, nullptr);
|
|
||||||
tr_free(cache);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
****
|
****
|
||||||
***/
|
***/
|
||||||
|
|
||||||
static int cache_block_compare(void const* va, void const* vb)
|
int Cache::writeBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t length, struct evbuffer* writeme)
|
||||||
{
|
|
||||||
auto const* a = static_cast<struct cache_block const*>(va);
|
|
||||||
auto const* b = static_cast<struct cache_block const*>(vb);
|
|
||||||
|
|
||||||
/* primary key: torrent id */
|
|
||||||
if (a->tor->uniqueId != b->tor->uniqueId)
|
|
||||||
{
|
|
||||||
return a->tor->uniqueId < b->tor->uniqueId ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* secondary key: block # */
|
|
||||||
if (a->loc.block != b->loc.block)
|
|
||||||
{
|
|
||||||
return a->loc.block < b->loc.block ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* they're equal */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct cache_block* findBlock(tr_cache* cache, tr_torrent* torrent, tr_block_info::Location loc)
|
|
||||||
{
|
|
||||||
auto key = cache_block{};
|
|
||||||
key.tor = torrent;
|
|
||||||
key.loc = loc;
|
|
||||||
return static_cast<struct cache_block*>(tr_ptrArrayFindSorted(&cache->blocks, &key, cache_block_compare));
|
|
||||||
}
|
|
||||||
|
|
||||||
int tr_cacheWriteBlock(
|
|
||||||
tr_cache* cache,
|
|
||||||
tr_torrent* torrent,
|
|
||||||
tr_block_info::Location loc,
|
|
||||||
uint32_t length,
|
|
||||||
struct evbuffer* writeme)
|
|
||||||
{
|
{
|
||||||
TR_ASSERT(tr_amInEventThread(torrent->session));
|
TR_ASSERT(tr_amInEventThread(torrent->session));
|
||||||
TR_ASSERT(loc.block_offset == 0);
|
TR_ASSERT(loc.block_offset == 0);
|
||||||
TR_ASSERT(torrent->blockSize(loc.block) == length);
|
TR_ASSERT(torrent->blockSize(loc.block) == length);
|
||||||
TR_ASSERT(torrent->blockSize(loc.block) <= evbuffer_get_length(writeme));
|
TR_ASSERT(torrent->blockSize(loc.block) <= evbuffer_get_length(writeme));
|
||||||
|
|
||||||
auto* cb = findBlock(cache, torrent, loc);
|
auto const key = makeKey(torrent, loc);
|
||||||
if (cb == nullptr)
|
auto iter = std::lower_bound(std::begin(blocks_), std::end(blocks_), key, CompareCacheBlockByKey{});
|
||||||
|
if (iter == std::end(blocks_) || iter->key != key)
|
||||||
{
|
{
|
||||||
cb = tr_new(struct cache_block, 1);
|
iter = blocks_.emplace(iter);
|
||||||
cb->tor = torrent;
|
iter->key = key;
|
||||||
cb->loc = loc;
|
|
||||||
cb->length = length;
|
|
||||||
cb->evbuf = evbuffer_new();
|
|
||||||
tr_ptrArrayInsertSorted(&cache->blocks, cb, cache_block_compare);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TR_ASSERT(cb->length == length);
|
iter->time_added = tr_time();
|
||||||
|
|
||||||
cb->time = tr_time();
|
iter->buf.resize(length);
|
||||||
|
evbuffer_remove(writeme, std::data(iter->buf), std::size(iter->buf));
|
||||||
|
|
||||||
evbuffer_drain(cb->evbuf, evbuffer_get_length(cb->evbuf));
|
++cache_writes_;
|
||||||
evbuffer_remove_buffer(writeme, cb->evbuf, cb->length);
|
cache_write_bytes_ += length;
|
||||||
|
|
||||||
cache->cache_writes++;
|
return cacheTrim();
|
||||||
cache->cache_write_bytes += cb->length;
|
|
||||||
|
|
||||||
return cacheTrim(cache);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int tr_cacheReadBlock(tr_cache* cache, tr_torrent* torrent, tr_block_info::Location loc, uint32_t len, uint8_t* setme)
|
Cache::CIter Cache::getBlock(tr_torrent const* torrent, tr_block_info::Location loc) noexcept
|
||||||
{
|
{
|
||||||
int err = 0;
|
if (auto const [begin, end] = std::equal_range(
|
||||||
|
std::begin(blocks_),
|
||||||
if (auto* const cb = findBlock(cache, torrent, loc); cb != nullptr)
|
std::end(blocks_),
|
||||||
|
makeKey(torrent, loc),
|
||||||
|
CompareCacheBlockByKey{});
|
||||||
|
begin < end)
|
||||||
{
|
{
|
||||||
evbuffer_copyout(cb->evbuf, setme, len);
|
return begin;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
err = tr_ioRead(torrent, loc, len, setme);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return std::end(blocks_);
|
||||||
}
|
}
|
||||||
|
|
||||||
int tr_cachePrefetchBlock(tr_cache* cache, tr_torrent* torrent, tr_block_info::Location loc, uint32_t len)
|
int Cache::readBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t len, uint8_t* setme)
|
||||||
{
|
{
|
||||||
int err = 0;
|
if (auto const iter = getBlock(torrent, loc); iter != std::end(blocks_))
|
||||||
|
|
||||||
if (auto const* const cb = findBlock(cache, torrent, loc); cb == nullptr)
|
|
||||||
{
|
{
|
||||||
err = tr_ioPrefetch(torrent, loc, len);
|
std::copy_n(std::begin(iter->buf), len, setme);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return tr_ioRead(torrent, loc, len, setme);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Cache::prefetchBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t len)
|
||||||
|
{
|
||||||
|
if (auto const iter = getBlock(torrent, loc); iter != std::end(blocks_))
|
||||||
|
{
|
||||||
|
return {}; // already have it
|
||||||
|
}
|
||||||
|
|
||||||
|
return tr_ioPrefetch(torrent, loc, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
****
|
****
|
||||||
***/
|
***/
|
||||||
|
|
||||||
static int findBlockPos(tr_cache const* cache, tr_torrent* torrent, tr_block_info::Location loc)
|
int Cache::flushSpan(CIter const begin, CIter const end)
|
||||||
{
|
{
|
||||||
struct cache_block key;
|
for (auto walk = begin; walk < end;)
|
||||||
key.tor = torrent;
|
|
||||||
key.loc = loc;
|
|
||||||
return tr_ptrArrayLowerBound(&cache->blocks, &key, cache_block_compare, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
int tr_cacheFlushDone(tr_cache* cache)
|
|
||||||
{
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
if (tr_ptrArraySize(&cache->blocks) > 0)
|
|
||||||
{
|
{
|
||||||
auto* const runs = tr_new(struct run_info, tr_ptrArraySize(&cache->blocks));
|
auto const [contig_begin, contig_end] = findContiguous(begin, end, walk);
|
||||||
int i = 0;
|
|
||||||
int const n = calcRuns(cache, runs);
|
|
||||||
|
|
||||||
while (i < n && (runs[i].is_piece_done || runs[i].is_multi_piece))
|
if (auto const err = writeContiguous(contig_begin, contig_end); err != 0)
|
||||||
{
|
{
|
||||||
runs[i++].rank |= SESSIONFLAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = flushRuns(cache, runs, i);
|
|
||||||
tr_free(runs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
walk = contig_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks_.erase(begin, end);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
int tr_cacheFlushFile(tr_cache* cache, tr_torrent* torrent, tr_file_index_t i)
|
int Cache::flushFile(tr_torrent* torrent, tr_file_index_t i)
|
||||||
{
|
{
|
||||||
auto const [begin, end] = tr_torGetFileBlockSpan(torrent, i);
|
auto const compare = CompareCacheBlockByKey{};
|
||||||
|
auto const tor_id = torrent->uniqueId;
|
||||||
|
auto const [block_begin, block_end] = tr_torGetFileBlockSpan(torrent, i);
|
||||||
|
|
||||||
int const pos = findBlockPos(cache, torrent, torrent->blockLoc(begin));
|
return flushSpan(
|
||||||
|
std::lower_bound(std::begin(blocks_), std::end(blocks_), std::make_pair(tor_id, block_begin), compare),
|
||||||
tr_logAddTrace(fmt::format("flushing file {} from cache to disk: blocks [{}...{})", i, begin, end));
|
std::lower_bound(std::begin(blocks_), std::end(blocks_), std::make_pair(tor_id, block_end), compare));
|
||||||
|
|
||||||
/* flush out all the blocks in that file */
|
|
||||||
int err = 0;
|
|
||||||
while (err == 0 && pos < tr_ptrArraySize(&cache->blocks))
|
|
||||||
{
|
|
||||||
auto const* b = static_cast<struct cache_block const*>(tr_ptrArrayNth(&cache->blocks, pos));
|
|
||||||
|
|
||||||
if (b->tor != torrent)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b->loc.block < begin || b->loc.block >= end)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = flushContiguous(cache, pos, getBlockRun(cache, pos, nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int tr_cacheFlushTorrent(tr_cache* cache, tr_torrent* torrent)
|
int Cache::flushTorrent(tr_torrent* torrent)
|
||||||
{
|
{
|
||||||
int err = 0;
|
auto const compare = CompareCacheBlockByKey{};
|
||||||
int const pos = findBlockPos(cache, torrent, {});
|
auto const tor_id = torrent->uniqueId;
|
||||||
|
|
||||||
/* flush out all the blocks in that torrent */
|
return flushSpan(
|
||||||
while (err == 0 && pos < tr_ptrArraySize(&cache->blocks))
|
std::lower_bound(std::begin(blocks_), std::end(blocks_), std::make_pair(tor_id, 0), compare),
|
||||||
{
|
std::lower_bound(std::begin(blocks_), std::end(blocks_), std::make_pair(tor_id + 1, 0), compare));
|
||||||
auto const* b = static_cast<struct cache_block const*>(tr_ptrArrayNth(&cache->blocks, pos));
|
}
|
||||||
|
|
||||||
if (b->tor != torrent)
|
int Cache::flushOldest()
|
||||||
{
|
{
|
||||||
break;
|
auto const oldest = std::min_element(
|
||||||
}
|
std::begin(blocks_),
|
||||||
|
std::end(blocks_),
|
||||||
err = flushContiguous(cache, pos, getBlockRun(cache, pos, nullptr));
|
[](auto const& a, auto const& b) { return a.time_added < b.time_added; });
|
||||||
}
|
|
||||||
|
if (oldest == std::end(blocks_)) // nothing to flush
|
||||||
return err;
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const [begin, end] = findContiguous(std::begin(blocks_), std::end(blocks_), oldest);
|
||||||
|
|
||||||
|
if (auto const err = writeContiguous(begin, end); err != 0)
|
||||||
|
{
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks_.erase(begin, end);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Cache::cacheTrim()
|
||||||
|
{
|
||||||
|
while (std::size(blocks_) > max_blocks_)
|
||||||
|
{
|
||||||
|
if (auto const err = flushOldest(); err != 0)
|
||||||
|
{
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,48 +10,88 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstdint> // intX_t, uintX_t
|
#include <cstdint> // intX_t, uintX_t
|
||||||
|
#include <utility> // std::pair
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "transmission.h"
|
#include "transmission.h"
|
||||||
|
|
||||||
#include "block-info.h"
|
#include "block-info.h"
|
||||||
|
|
||||||
|
class tr_torrents;
|
||||||
struct evbuffer;
|
struct evbuffer;
|
||||||
struct tr_cache;
|
|
||||||
struct tr_torrent;
|
struct tr_torrent;
|
||||||
|
|
||||||
/***
|
class Cache
|
||||||
****
|
{
|
||||||
***/
|
public:
|
||||||
|
Cache(tr_torrents& torrents, int64_t max_bytes);
|
||||||
|
|
||||||
tr_cache* tr_cacheNew(int64_t max_bytes);
|
int setLimit(int64_t new_limit);
|
||||||
|
|
||||||
void tr_cacheFree(tr_cache*);
|
[[nodiscard]] constexpr auto getLimit() const noexcept
|
||||||
|
{
|
||||||
|
return max_bytes_;
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
int writeBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t length, struct evbuffer* writeme);
|
||||||
****
|
int readBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t len, uint8_t* setme);
|
||||||
***/
|
int prefetchBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t len);
|
||||||
|
int flushTorrent(tr_torrent* torrent);
|
||||||
|
int flushFile(tr_torrent* torrent, tr_file_index_t file);
|
||||||
|
|
||||||
int tr_cacheSetLimit(tr_cache* cache, int64_t max_bytes);
|
private:
|
||||||
|
using Key = std::pair<tr_torrent_id_t, tr_block_index_t>;
|
||||||
|
|
||||||
int64_t tr_cacheGetLimit(tr_cache const*);
|
struct CacheBlock
|
||||||
|
{
|
||||||
|
Key key;
|
||||||
|
std::vector<uint8_t> buf;
|
||||||
|
time_t time_added = {};
|
||||||
|
};
|
||||||
|
|
||||||
int tr_cacheWriteBlock(
|
using Blocks = std::vector<CacheBlock>;
|
||||||
tr_cache* cache,
|
using CIter = Blocks::const_iterator;
|
||||||
tr_torrent* torrent,
|
|
||||||
tr_block_info::Location loc,
|
|
||||||
uint32_t len,
|
|
||||||
struct evbuffer* writeme);
|
|
||||||
|
|
||||||
int tr_cacheReadBlock(tr_cache* cache, tr_torrent* torrent, tr_block_info::Location loc, uint32_t len, uint8_t* setme);
|
struct CompareCacheBlockByKey
|
||||||
|
{
|
||||||
|
[[nodiscard]] constexpr bool operator()(Key const& key, CacheBlock const& block)
|
||||||
|
{
|
||||||
|
return key < block.key;
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr bool operator()(CacheBlock const& block, Key const& key)
|
||||||
|
{
|
||||||
|
return block.key < key;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
int tr_cachePrefetchBlock(tr_cache* cache, tr_torrent* torrent, tr_block_info::Location loc, uint32_t len);
|
[[nodiscard]] static Key makeKey(tr_torrent const* torrent, tr_block_info::Location loc) noexcept;
|
||||||
|
|
||||||
/***
|
[[nodiscard]] static std::pair<CIter, CIter> findContiguous(CIter const begin, CIter const end, CIter const iter) noexcept;
|
||||||
****
|
|
||||||
***/
|
|
||||||
|
|
||||||
int tr_cacheFlushDone(tr_cache* cache);
|
// @return any error code from tr_ioWrite()
|
||||||
|
[[nodiscard]] int writeContiguous(CIter const begin, CIter const end) const;
|
||||||
|
|
||||||
int tr_cacheFlushTorrent(tr_cache* cache, tr_torrent* torrent);
|
// @return any error code from writeContiguous()
|
||||||
|
[[nodiscard]] int flushSpan(CIter const begin, CIter const end);
|
||||||
|
|
||||||
int tr_cacheFlushFile(tr_cache* cache, tr_torrent* torrent, tr_file_index_t file);
|
// @return any error code from writeContiguous()
|
||||||
|
[[nodiscard]] int flushOldest();
|
||||||
|
|
||||||
|
// @return any error code from writeContiguous()
|
||||||
|
[[nodiscard]] int cacheTrim();
|
||||||
|
|
||||||
|
[[nodiscard]] static size_t getMaxBlocks(int64_t max_bytes) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] CIter getBlock(tr_torrent const* torrent, tr_block_info::Location loc) noexcept;
|
||||||
|
|
||||||
|
tr_torrents& torrents_;
|
||||||
|
|
||||||
|
Blocks blocks_ = {};
|
||||||
|
size_t max_blocks_ = 0;
|
||||||
|
size_t max_bytes_ = 0;
|
||||||
|
|
||||||
|
mutable size_t disk_writes_ = 0;
|
||||||
|
mutable size_t disk_write_bytes_ = 0;
|
||||||
|
mutable size_t cache_writes_ = 0;
|
||||||
|
mutable size_t cache_write_bytes_ = 0;
|
||||||
|
};
|
||||||
|
|
|
@ -242,7 +242,7 @@ std::optional<tr_sha1_digest_t> recalculateHash(tr_torrent* tor, tr_piece_index_
|
||||||
while (bytes_left != 0)
|
while (bytes_left != 0)
|
||||||
{
|
{
|
||||||
size_t const len = std::min(bytes_left, std::size(buffer));
|
size_t const len = std::min(bytes_left, std::size(buffer));
|
||||||
if (auto const success = tr_cacheReadBlock(tor->session->cache, tor, loc, len, std::data(buffer)) == 0; !success)
|
if (auto const success = tor->session->cache->readBlock(tor, loc, len, std::data(buffer)) == 0; !success)
|
||||||
{
|
{
|
||||||
tr_sha1_final(sha);
|
tr_sha1_final(sha);
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -1444,11 +1444,7 @@ static void prefetchPieces(tr_peerMsgsImpl* msgs)
|
||||||
{
|
{
|
||||||
if (auto& req = requests[i]; !req.prefetched)
|
if (auto& req = requests[i]; !req.prefetched)
|
||||||
{
|
{
|
||||||
tr_cachePrefetchBlock(
|
msgs->session->cache->prefetchBlock(msgs->torrent, msgs->torrent->pieceLoc(req.index, req.offset), req.length);
|
||||||
msgs->session->cache,
|
|
||||||
msgs->torrent,
|
|
||||||
msgs->torrent->pieceLoc(req.index, req.offset),
|
|
||||||
req.length);
|
|
||||||
req.prefetched = true;
|
req.prefetched = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1920,8 +1916,7 @@ static int clientGotBlock(tr_peerMsgsImpl* msgs, struct evbuffer* data, struct p
|
||||||
*** Save the block
|
*** Save the block
|
||||||
**/
|
**/
|
||||||
|
|
||||||
if (int const
|
if (int const err = msgs->session->cache->writeBlock(tor, tor->pieceLoc(req->index, req->offset), req->length, data);
|
||||||
err = tr_cacheWriteBlock(msgs->session->cache, tor, tor->pieceLoc(req->index, req->offset), req->length, data);
|
|
||||||
err != 0)
|
err != 0)
|
||||||
{
|
{
|
||||||
return err;
|
return err;
|
||||||
|
@ -2218,8 +2213,7 @@ static size_t fillOutputBuffer(tr_peerMsgsImpl* msgs, time_t now)
|
||||||
evbuffer_add_uint32(out, req.offset);
|
evbuffer_add_uint32(out, req.offset);
|
||||||
|
|
||||||
evbuffer_reserve_space(out, req.length, iovec, 1);
|
evbuffer_reserve_space(out, req.length, iovec, 1);
|
||||||
bool err = tr_cacheReadBlock(
|
bool err = msgs->session->cache->readBlock(
|
||||||
msgs->session->cache,
|
|
||||||
msgs->torrent,
|
msgs->torrent,
|
||||||
msgs->torrent->pieceLoc(req.index, req.offset),
|
msgs->torrent->pieceLoc(req.index, req.offset),
|
||||||
req.length,
|
req.length,
|
||||||
|
|
|
@ -566,11 +566,6 @@ static void onSaveTimer(evutil_socket_t /*fd*/, short /*what*/, void* vsession)
|
||||||
{
|
{
|
||||||
auto* session = static_cast<tr_session*>(vsession);
|
auto* session = static_cast<tr_session*>(vsession);
|
||||||
|
|
||||||
if (tr_cacheFlushDone(session->cache) != 0)
|
|
||||||
{
|
|
||||||
tr_logAddError("Error while flushing completed pieces from cache");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto* const tor : session->torrents())
|
for (auto* const tor : session->torrents())
|
||||||
{
|
{
|
||||||
tr_torrentSave(tor);
|
tr_torrentSave(tor);
|
||||||
|
@ -606,7 +601,7 @@ tr_session* tr_sessionInit(char const* config_dir, bool messageQueuingEnabled, t
|
||||||
auto* session = new tr_session{};
|
auto* session = new tr_session{};
|
||||||
session->udp_socket = TR_BAD_SOCKET;
|
session->udp_socket = TR_BAD_SOCKET;
|
||||||
session->udp6_socket = TR_BAD_SOCKET;
|
session->udp6_socket = TR_BAD_SOCKET;
|
||||||
session->cache = tr_cacheNew(1024 * 1024 * 2);
|
session->cache = std::make_unique<Cache>(session->torrents(), 1024 * 1024 * 2);
|
||||||
session->magicNumber = SESSION_MAGIC_NUMBER;
|
session->magicNumber = SESSION_MAGIC_NUMBER;
|
||||||
session->session_id = tr_session_id_new();
|
session->session_id = tr_session_id_new();
|
||||||
bandwidthGroupRead(session, config_dir);
|
bandwidthGroupRead(session, config_dir);
|
||||||
|
@ -1880,8 +1875,7 @@ static void sessionCloseImplStart(tr_session* session)
|
||||||
it won't be idle until the announce events are sent... */
|
it won't be idle until the announce events are sent... */
|
||||||
session->web->closeSoon();
|
session->web->closeSoon();
|
||||||
|
|
||||||
tr_cacheFree(session->cache);
|
session->cache.reset();
|
||||||
session->cache = nullptr;
|
|
||||||
|
|
||||||
/* saveTimer is not used at this point, reusing for UDP shutdown wait */
|
/* saveTimer is not used at this point, reusing for UDP shutdown wait */
|
||||||
TR_ASSERT(session->saveTimer == nullptr);
|
TR_ASSERT(session->saveTimer == nullptr);
|
||||||
|
@ -2238,14 +2232,14 @@ void tr_sessionSetCacheLimit_MB(tr_session* session, int max_bytes)
|
||||||
{
|
{
|
||||||
TR_ASSERT(tr_isSession(session));
|
TR_ASSERT(tr_isSession(session));
|
||||||
|
|
||||||
tr_cacheSetLimit(session->cache, tr_toMemBytes(max_bytes));
|
session->cache->setLimit(tr_toMemBytes(max_bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
int tr_sessionGetCacheLimit_MB(tr_session const* session)
|
int tr_sessionGetCacheLimit_MB(tr_session const* session)
|
||||||
{
|
{
|
||||||
TR_ASSERT(tr_isSession(session));
|
TR_ASSERT(tr_isSession(session));
|
||||||
|
|
||||||
return tr_toMemMB(tr_cacheGetLimit(session->cache));
|
return tr_toMemMB(session->cache->getLimit());
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
@ -2932,12 +2926,12 @@ static int bandwidthGroupWrite(tr_session const* session, std::string_view confi
|
||||||
|
|
||||||
void tr_session::closeTorrentFiles(tr_torrent* tor) noexcept
|
void tr_session::closeTorrentFiles(tr_torrent* tor) noexcept
|
||||||
{
|
{
|
||||||
tr_cacheFlushTorrent(this->cache, tor);
|
this->cache->flushTorrent(tor);
|
||||||
openFiles().closeTorrent(tor->uniqueId);
|
openFiles().closeTorrent(tor->uniqueId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tr_session::closeTorrentFile(tr_torrent* tor, tr_file_index_t file_num) noexcept
|
void tr_session::closeTorrentFile(tr_torrent* tor, tr_file_index_t file_num) noexcept
|
||||||
{
|
{
|
||||||
tr_cacheFlushFile(this->cache, tor, file_num);
|
this->cache->flushFile(tor, file_num);
|
||||||
openFiles().closeFile(tor->uniqueId, file_num);
|
openFiles().closeFile(tor->uniqueId, file_num);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#include "announce-list.h"
|
#include "announce-list.h"
|
||||||
#include "bandwidth.h"
|
#include "bandwidth.h"
|
||||||
|
#include "cache.h"
|
||||||
#include "interned-string.h"
|
#include "interned-string.h"
|
||||||
#include "net.h" // tr_socket_t
|
#include "net.h" // tr_socket_t
|
||||||
#include "open-files.h"
|
#include "open-files.h"
|
||||||
|
@ -54,7 +55,6 @@ struct tr_announcer;
|
||||||
struct tr_announcer_udp;
|
struct tr_announcer_udp;
|
||||||
struct tr_bindsockets;
|
struct tr_bindsockets;
|
||||||
struct BlocklistFile;
|
struct BlocklistFile;
|
||||||
struct tr_cache;
|
|
||||||
struct tr_fdInfo;
|
struct tr_fdInfo;
|
||||||
|
|
||||||
struct tr_bindinfo
|
struct tr_bindinfo
|
||||||
|
@ -296,60 +296,60 @@ public:
|
||||||
TR_SCRIPT_ON_TORRENT_DONE_SEEDING } }
|
TR_SCRIPT_ON_TORRENT_DONE_SEEDING } }
|
||||||
};
|
};
|
||||||
|
|
||||||
bool isPortRandom;
|
bool isPortRandom = false;
|
||||||
bool isPexEnabled;
|
bool isPexEnabled = false;
|
||||||
bool isDHTEnabled;
|
bool isDHTEnabled = false;
|
||||||
bool isUTPEnabled;
|
bool isUTPEnabled = false;
|
||||||
bool isLPDEnabled;
|
bool isLPDEnabled = false;
|
||||||
bool isPrefetchEnabled;
|
bool isPrefetchEnabled = false;
|
||||||
bool is_closing_ = false;
|
bool is_closing_ = false;
|
||||||
bool isClosed;
|
bool isClosed = false;
|
||||||
bool isRatioLimited;
|
bool isRatioLimited = false;
|
||||||
bool isIdleLimited;
|
bool isIdleLimited = false;
|
||||||
bool isIncompleteFileNamingEnabled;
|
bool isIncompleteFileNamingEnabled = false;
|
||||||
bool pauseAddedTorrent;
|
bool pauseAddedTorrent = false;
|
||||||
bool deleteSourceTorrent;
|
bool deleteSourceTorrent = false;
|
||||||
bool scrapePausedTorrents;
|
bool scrapePausedTorrents = false;
|
||||||
|
|
||||||
uint8_t peer_id_ttl_hours;
|
uint8_t peer_id_ttl_hours = 0;
|
||||||
|
|
||||||
bool stalledEnabled;
|
bool stalledEnabled = false;
|
||||||
bool queueEnabled[2];
|
bool queueEnabled[2] = { false, false };
|
||||||
int queueSize[2];
|
int queueSize[2] = { 0, 0 };
|
||||||
int queueStalledMinutes;
|
int queueStalledMinutes = 0;
|
||||||
|
|
||||||
int umask;
|
int umask = 0;
|
||||||
|
|
||||||
unsigned int speedLimit_Bps[2];
|
unsigned int speedLimit_Bps[2] = { 0, 0 };
|
||||||
bool speedLimitEnabled[2];
|
bool speedLimitEnabled[2] = { false, false };
|
||||||
|
|
||||||
struct tr_turtle_info turtle;
|
struct tr_turtle_info turtle;
|
||||||
|
|
||||||
int magicNumber;
|
int magicNumber = 0;
|
||||||
|
|
||||||
tr_encryption_mode encryptionMode;
|
tr_encryption_mode encryptionMode;
|
||||||
|
|
||||||
tr_preallocation_mode preallocationMode;
|
tr_preallocation_mode preallocationMode;
|
||||||
|
|
||||||
struct event_base* event_base;
|
struct event_base* event_base = nullptr;
|
||||||
struct evdns_base* evdns_base;
|
struct evdns_base* evdns_base = nullptr;
|
||||||
struct tr_event_handle* events;
|
struct tr_event_handle* events = nullptr;
|
||||||
|
|
||||||
uint16_t peerCount = 0;
|
uint16_t peerCount = 0;
|
||||||
uint16_t peerLimit = 200;
|
uint16_t peerLimit = 200;
|
||||||
uint16_t peerLimitPerTorrent = 50;
|
uint16_t peerLimitPerTorrent = 50;
|
||||||
|
|
||||||
int uploadSlotsPerTorrent;
|
int uploadSlotsPerTorrent = 0;
|
||||||
|
|
||||||
/* The UDP sockets used for the DHT and uTP. */
|
/* The UDP sockets used for the DHT and uTP. */
|
||||||
tr_port udp_port;
|
tr_port udp_port;
|
||||||
tr_socket_t udp_socket = TR_BAD_SOCKET;
|
tr_socket_t udp_socket = TR_BAD_SOCKET;
|
||||||
tr_socket_t udp6_socket = TR_BAD_SOCKET;
|
tr_socket_t udp6_socket = TR_BAD_SOCKET;
|
||||||
unsigned char* udp6_bound;
|
unsigned char* udp6_bound = nullptr;
|
||||||
struct event* udp_event;
|
struct event* udp_event = nullptr;
|
||||||
struct event* udp6_event;
|
struct event* udp6_event = nullptr;
|
||||||
|
|
||||||
struct event* utp_timer;
|
struct event* utp_timer = nullptr;
|
||||||
|
|
||||||
/* The open port on the local machine for incoming peer requests */
|
/* The open port on the local machine for incoming peer requests */
|
||||||
tr_port private_peer_port;
|
tr_port private_peer_port;
|
||||||
|
@ -380,10 +380,10 @@ public:
|
||||||
std::string torrent_dir;
|
std::string torrent_dir;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<BlocklistFile>> blocklists;
|
std::vector<std::unique_ptr<BlocklistFile>> blocklists;
|
||||||
struct tr_peerMgr* peerMgr;
|
struct tr_peerMgr* peerMgr = nullptr;
|
||||||
struct tr_shared* shared;
|
struct tr_shared* shared = nullptr;
|
||||||
|
|
||||||
struct tr_cache* cache;
|
std::unique_ptr<Cache> cache;
|
||||||
|
|
||||||
class WebMediator final : public tr_web::Mediator
|
class WebMediator final : public tr_web::Mediator
|
||||||
{
|
{
|
||||||
|
|
|
@ -316,7 +316,7 @@ public:
|
||||||
{
|
{
|
||||||
auto const len = evbuffer_get_length(buf);
|
auto const len = evbuffer_get_length(buf);
|
||||||
TR_ASSERT(tor->blockSize(this->loc.block) == len);
|
TR_ASSERT(tor->blockSize(this->loc.block) == len);
|
||||||
tr_cacheWriteBlock(tor->session->cache, tor, this->loc, len, buf);
|
tor->session->cache->writeBlock(tor, this->loc, len, buf);
|
||||||
webseed->publishGotBlock(tor, this->loc);
|
webseed->publishGotBlock(tor, this->loc);
|
||||||
TR_ASSERT(evbuffer_get_length(buf) == 0);
|
TR_ASSERT(evbuffer_get_length(buf) == 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,12 +89,7 @@ TEST_P(IncompleteDirTest, incompleteDir)
|
||||||
auto const test_incomplete_dir_threadfunc = [](void* vdata) noexcept
|
auto const test_incomplete_dir_threadfunc = [](void* vdata) noexcept
|
||||||
{
|
{
|
||||||
auto* data = static_cast<TestIncompleteDirData*>(vdata);
|
auto* data = static_cast<TestIncompleteDirData*>(vdata);
|
||||||
tr_cacheWriteBlock(
|
data->session->cache->writeBlock(data->tor, data->tor->pieceLoc(0, data->offset), tr_block_info::BlockSize, data->buf);
|
||||||
data->session->cache,
|
|
||||||
data->tor,
|
|
||||||
data->tor->pieceLoc(0, data->offset),
|
|
||||||
tr_block_info::BlockSize,
|
|
||||||
data->buf);
|
|
||||||
tr_torrentGotBlock(data->tor, data->block);
|
tr_torrentGotBlock(data->tor, data->block);
|
||||||
data->done = true;
|
data->done = true;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue