2023-11-01 21:11:11 +00:00
|
|
|
// This file Copyright © Mnemosyne LLC.
|
2022-02-07 16:25:02 +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.
|
2010-06-19 14:25:11 +00:00
|
|
|
|
2022-08-18 14:14:12 +00:00
|
|
|
#include <algorithm>
|
2023-07-08 15:24:03 +00:00
|
|
|
#include <cerrno>
|
|
|
|
#include <cstdint> // uint8_t
|
2022-06-12 00:46:30 +00:00
|
|
|
#include <iterator> // std::distance(), std::next(), std::prev()
|
2022-08-17 16:08:36 +00:00
|
|
|
#include <memory>
|
2022-06-12 00:46:30 +00:00
|
|
|
#include <numeric> // std::accumulate()
|
|
|
|
#include <utility> // std::make_pair()
|
2022-07-26 02:45:54 +00:00
|
|
|
#include <vector>
|
2011-03-16 18:04:23 +00:00
|
|
|
|
2022-03-15 14:52:16 +00:00
|
|
|
#include <fmt/core.h>
|
2023-07-08 15:24:03 +00:00
|
|
|
#include <small/vector.hpp>
|
2022-03-15 14:52:16 +00:00
|
|
|
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/transmission.h"
|
|
|
|
|
|
|
|
#include "libtransmission/cache.h"
|
|
|
|
#include "libtransmission/inout.h"
|
|
|
|
#include "libtransmission/log.h"
|
|
|
|
#include "libtransmission/torrent.h"
|
|
|
|
#include "libtransmission/torrents.h"
|
|
|
|
#include "libtransmission/tr-assert.h"
|
2023-06-27 17:08:29 +00:00
|
|
|
#include "libtransmission/utils.h" // tr_formatter
|
2010-06-19 14:25:11 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
Cache::Key Cache::make_key(tr_torrent const* torrent, tr_block_info::Location loc) noexcept
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
2022-06-17 15:43:04 +00:00
|
|
|
return std::make_pair(torrent->id(), loc.block);
|
2022-06-12 00:46:30 +00:00
|
|
|
}
|
2010-06-19 14:25:11 +00:00
|
|
|
|
2023-06-27 17:08:29 +00:00
|
|
|
Cache::CIter Cache::find_span_end(CIter span_begin, CIter end) noexcept
|
2010-06-26 16:21:50 +00:00
|
|
|
{
|
2023-06-27 17:08:29 +00:00
|
|
|
static constexpr auto NotAdjacent = [](CacheBlock const& block1, CacheBlock const& block2)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2023-06-27 17:08:29 +00:00
|
|
|
return block1.key.first != block2.key.first || block1.key.second + 1 != block2.key.second;
|
|
|
|
};
|
|
|
|
auto const span_end = std::adjacent_find(span_begin, end, NotAdjacent);
|
|
|
|
return span_end == end ? end : std::next(span_end);
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2023-06-27 17:08:29 +00:00
|
|
|
std::pair<Cache::CIter, Cache::CIter> Cache::find_biggest_span(CIter const begin, CIter const end) noexcept
|
|
|
|
{
|
|
|
|
auto biggest_begin = begin;
|
|
|
|
auto biggest_end = begin;
|
|
|
|
auto biggest_len = std::distance(biggest_begin, biggest_end);
|
2010-06-19 14:25:11 +00:00
|
|
|
|
2023-06-27 17:08:29 +00:00
|
|
|
for (auto span_begin = begin; span_begin < end;)
|
2010-07-08 17:38:11 +00:00
|
|
|
{
|
2023-06-27 17:08:29 +00:00
|
|
|
auto span_end = find_span_end(span_begin, end);
|
|
|
|
auto const len = std::distance(span_begin, span_end);
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2023-06-27 17:08:29 +00:00
|
|
|
if (len > biggest_len)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-06-27 17:08:29 +00:00
|
|
|
biggest_begin = span_begin;
|
|
|
|
biggest_end = span_end;
|
|
|
|
biggest_len = len;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2023-06-27 17:08:29 +00:00
|
|
|
|
|
|
|
std::advance(span_begin, len);
|
2010-07-08 17:38:11 +00:00
|
|
|
}
|
|
|
|
|
2023-06-27 17:08:29 +00:00
|
|
|
return { biggest_begin, biggest_end };
|
2010-07-08 17:38:11 +00:00
|
|
|
}
|
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
int Cache::write_contiguous(CIter const begin, CIter const end) const
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
2023-01-30 18:04:40 +00:00
|
|
|
// The most common case without an extra data copy.
|
2023-05-13 19:16:00 +00:00
|
|
|
auto const* out = std::data(*begin->buf);
|
|
|
|
auto outlen = std::size(*begin->buf);
|
2023-01-30 18:04:40 +00:00
|
|
|
|
|
|
|
// Contiguous area to join more than one block, if any.
|
2022-06-12 00:46:30 +00:00
|
|
|
auto buf = std::vector<uint8_t>{};
|
2023-01-30 18:04:40 +00:00
|
|
|
|
|
|
|
if (end - begin > 1)
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
2023-07-02 20:23:32 +00:00
|
|
|
// copy blocks into contiguous memory
|
2023-01-30 18:04:40 +00:00
|
|
|
auto const buflen = std::accumulate(
|
|
|
|
begin,
|
|
|
|
end,
|
|
|
|
size_t{},
|
|
|
|
[](size_t sum, auto const& block) { return sum + std::size(*block.buf); });
|
2023-07-02 20:23:32 +00:00
|
|
|
buf.resize(buflen);
|
|
|
|
auto* walk = std::data(buf);
|
2023-01-30 18:04:40 +00:00
|
|
|
for (auto iter = begin; iter != end; ++iter)
|
|
|
|
{
|
|
|
|
TR_ASSERT(begin->key.first == iter->key.first);
|
|
|
|
TR_ASSERT(begin->key.second + std::distance(begin, iter) == iter->key.second);
|
2023-07-02 20:23:32 +00:00
|
|
|
walk = std::copy_n(std::data(*iter->buf), std::size(*iter->buf), walk);
|
2023-01-30 18:04:40 +00:00
|
|
|
}
|
2023-07-02 20:23:32 +00:00
|
|
|
TR_ASSERT(std::data(buf) + std::size(buf) == walk);
|
2023-05-13 19:16:00 +00:00
|
|
|
out = std::data(buf);
|
|
|
|
outlen = std::size(buf);
|
2022-06-12 00:46:30 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-06-12 00:46:30 +00:00
|
|
|
// save it
|
2022-08-26 19:46:46 +00:00
|
|
|
auto const& [torrent_id, block] = begin->key;
|
|
|
|
auto* const tor = torrents_.get(torrent_id);
|
|
|
|
if (tor == nullptr)
|
|
|
|
{
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
2023-04-23 01:25:55 +00:00
|
|
|
auto const loc = tor->block_loc(block);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2023-05-13 19:16:00 +00:00
|
|
|
if (auto const err = tr_ioWrite(tor, loc, outlen, out); err != 0)
|
2022-06-12 00:46:30 +00:00
|
|
|
{
|
|
|
|
return err;
|
2010-06-19 14:25:11 +00:00
|
|
|
}
|
|
|
|
|
2022-06-12 00:46:30 +00:00
|
|
|
++disk_writes_;
|
2023-05-13 19:16:00 +00:00
|
|
|
disk_write_bytes_ += outlen;
|
2022-06-12 00:46:30 +00:00
|
|
|
return {};
|
2010-06-19 14:25:11 +00:00
|
|
|
}
|
|
|
|
|
2023-06-25 18:29:58 +00:00
|
|
|
size_t Cache::get_max_blocks(size_t max_bytes) noexcept
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
2023-06-25 18:29:58 +00:00
|
|
|
return max_bytes / tr_block_info::BlockSize;
|
2010-06-19 14:25:11 +00:00
|
|
|
}
|
|
|
|
|
2023-06-25 18:29:58 +00:00
|
|
|
int Cache::set_limit(size_t new_limit)
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
2023-05-06 04:11:05 +00:00
|
|
|
max_blocks_ = get_max_blocks(new_limit);
|
2010-06-19 14:25:11 +00:00
|
|
|
|
2023-06-28 13:57:26 +00:00
|
|
|
tr_logAddDebug(fmt::format("Maximum cache size set to {} ({} blocks)", tr_formatter_mem_B(new_limit), max_blocks_));
|
2010-06-19 14:25:11 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
return cache_trim();
|
2010-06-19 14:25:11 +00:00
|
|
|
}
|
|
|
|
|
2023-06-25 18:29:58 +00:00
|
|
|
Cache::Cache(tr_torrents& torrents, size_t max_bytes)
|
2022-06-12 00:46:30 +00:00
|
|
|
: torrents_{ torrents }
|
2023-05-06 04:11:05 +00:00
|
|
|
, max_blocks_(get_max_blocks(max_bytes))
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-01-22 19:21:30 +00:00
|
|
|
// ---
|
2010-06-19 14:25:11 +00:00
|
|
|
|
2023-05-13 19:16:00 +00:00
|
|
|
int Cache::write_block(tr_torrent_id_t tor_id, tr_block_index_t block, std::unique_ptr<BlockData> writeme)
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
2023-06-28 13:57:26 +00:00
|
|
|
if (max_blocks_ == 0U)
|
|
|
|
{
|
|
|
|
TR_ASSERT(std::empty(blocks_));
|
|
|
|
|
|
|
|
// Bypass cache. This may be helpful for those whose filesystem
|
|
|
|
// already has a cache layer for the very purpose of this cache
|
|
|
|
// https://github.com/transmission/transmission/pull/5668
|
|
|
|
auto* const tor = torrents_.get(tor_id);
|
|
|
|
return tr_ioWrite(tor, tor->block_loc(block), std::size(*writeme), std::data(*writeme));
|
|
|
|
}
|
|
|
|
|
2022-06-20 04:08:58 +00:00
|
|
|
auto const key = Key{ tor_id, block };
|
2023-06-28 15:23:38 +00:00
|
|
|
auto iter = std::lower_bound(std::begin(blocks_), std::end(blocks_), key, CompareCacheBlockByKey);
|
2022-06-12 00:46:30 +00:00
|
|
|
if (iter == std::end(blocks_) || iter->key != key)
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
2022-06-12 00:46:30 +00:00
|
|
|
iter = blocks_.emplace(iter);
|
|
|
|
iter->key = key;
|
2010-06-19 14:25:11 +00:00
|
|
|
}
|
|
|
|
|
2022-08-03 06:15:37 +00:00
|
|
|
iter->buf = std::move(writeme);
|
2010-06-19 14:25:11 +00:00
|
|
|
|
2022-06-12 00:46:30 +00:00
|
|
|
++cache_writes_;
|
2022-06-20 04:08:58 +00:00
|
|
|
cache_write_bytes_ += std::size(*iter->buf);
|
2010-06-19 14:25:11 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
return cache_trim();
|
2010-06-19 14:25:11 +00:00
|
|
|
}
|
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
Cache::CIter Cache::get_block(tr_torrent const* torrent, tr_block_info::Location const& loc) noexcept
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
2022-06-12 00:46:30 +00:00
|
|
|
if (auto const [begin, end] = std::equal_range(
|
|
|
|
std::begin(blocks_),
|
|
|
|
std::end(blocks_),
|
2023-05-06 04:11:05 +00:00
|
|
|
make_key(torrent, loc),
|
2023-06-28 15:23:38 +00:00
|
|
|
CompareCacheBlockByKey);
|
2022-06-12 00:46:30 +00:00
|
|
|
begin < end)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-06-12 00:46:30 +00:00
|
|
|
return begin;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2022-06-12 00:46:30 +00:00
|
|
|
|
|
|
|
return std::end(blocks_);
|
|
|
|
}
|
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
int Cache::read_block(tr_torrent* torrent, tr_block_info::Location const& loc, uint32_t len, uint8_t* setme)
|
2022-06-12 00:46:30 +00:00
|
|
|
{
|
2023-05-06 04:11:05 +00:00
|
|
|
if (auto const iter = get_block(torrent, loc); iter != std::end(blocks_))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-06-20 04:08:58 +00:00
|
|
|
std::copy_n(std::begin(*iter->buf), len, setme);
|
2022-06-12 00:46:30 +00:00
|
|
|
return {};
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2010-06-19 14:25:11 +00:00
|
|
|
|
2022-06-12 00:46:30 +00:00
|
|
|
return tr_ioRead(torrent, loc, len, setme);
|
2010-06-19 14:25:11 +00:00
|
|
|
}
|
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
int Cache::prefetch_block(tr_torrent* torrent, tr_block_info::Location const& loc, uint32_t len)
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
2023-05-06 04:11:05 +00:00
|
|
|
if (auto const iter = get_block(torrent, loc); iter != std::end(blocks_))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-06-12 00:46:30 +00:00
|
|
|
return {}; // already have it
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2010-06-19 14:25:11 +00:00
|
|
|
|
2022-06-12 00:46:30 +00:00
|
|
|
return tr_ioPrefetch(torrent, loc, len);
|
2010-06-19 14:25:11 +00:00
|
|
|
}
|
|
|
|
|
2023-01-22 19:21:30 +00:00
|
|
|
// ---
|
2010-06-19 14:25:11 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
int Cache::flush_span(CIter const begin, CIter const end)
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
2023-06-27 17:08:29 +00:00
|
|
|
for (auto span_begin = begin; span_begin < end;)
|
2010-07-08 17:38:11 +00:00
|
|
|
{
|
2023-06-27 17:08:29 +00:00
|
|
|
auto const span_end = find_span_end(span_begin, end);
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2023-06-27 17:08:29 +00:00
|
|
|
if (auto const err = write_contiguous(span_begin, span_end); err != 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-06-12 00:46:30 +00:00
|
|
|
return err;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2023-06-27 17:08:29 +00:00
|
|
|
span_begin = span_end;
|
2010-07-08 17:38:11 +00:00
|
|
|
}
|
|
|
|
|
2022-06-12 00:46:30 +00:00
|
|
|
blocks_.erase(begin, end);
|
|
|
|
return {};
|
2010-07-08 17:38:11 +00:00
|
|
|
}
|
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
int Cache::flush_file(tr_torrent const* torrent, tr_file_index_t file)
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
2022-06-17 15:43:04 +00:00
|
|
|
auto const tor_id = torrent->id();
|
2022-08-03 06:15:37 +00:00
|
|
|
auto const [block_begin, block_end] = tr_torGetFileBlockSpan(torrent, file);
|
2021-10-23 15:43:15 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
return flush_span(
|
2023-06-28 15:23:38 +00:00
|
|
|
std::lower_bound(std::begin(blocks_), std::end(blocks_), std::make_pair(tor_id, block_begin), CompareCacheBlockByKey),
|
|
|
|
std::lower_bound(std::begin(blocks_), std::end(blocks_), std::make_pair(tor_id, block_end), CompareCacheBlockByKey));
|
2022-06-12 00:46:30 +00:00
|
|
|
}
|
2022-03-15 14:52:16 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
int Cache::flush_torrent(tr_torrent const* torrent)
|
2022-06-12 00:46:30 +00:00
|
|
|
{
|
2022-06-17 15:43:04 +00:00
|
|
|
auto const tor_id = torrent->id();
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
return flush_span(
|
2023-06-28 15:23:38 +00:00
|
|
|
std::lower_bound(std::begin(blocks_), std::end(blocks_), std::make_pair(tor_id, 0), CompareCacheBlockByKey),
|
|
|
|
std::lower_bound(std::begin(blocks_), std::end(blocks_), std::make_pair(tor_id + 1, 0), CompareCacheBlockByKey));
|
2022-06-12 00:46:30 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2023-06-27 17:08:29 +00:00
|
|
|
int Cache::flush_biggest()
|
2022-06-12 00:46:30 +00:00
|
|
|
{
|
2023-06-27 17:08:29 +00:00
|
|
|
auto const [begin, end] = find_biggest_span(std::begin(blocks_), std::end(blocks_));
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2023-06-27 17:08:29 +00:00
|
|
|
if (begin == end) // nothing to flush
|
2022-06-12 00:46:30 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
if (auto const err = write_contiguous(begin, end); err != 0)
|
2022-06-12 00:46:30 +00:00
|
|
|
{
|
|
|
|
return err;
|
2010-06-19 14:25:11 +00:00
|
|
|
}
|
|
|
|
|
2022-06-12 00:46:30 +00:00
|
|
|
blocks_.erase(begin, end);
|
|
|
|
return 0;
|
2010-06-19 14:25:11 +00:00
|
|
|
}
|
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
int Cache::cache_trim()
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
2022-06-12 00:46:30 +00:00
|
|
|
while (std::size(blocks_) > max_blocks_)
|
2010-06-19 14:25:11 +00:00
|
|
|
{
|
2023-06-27 17:08:29 +00:00
|
|
|
if (auto const err = flush_biggest(); err != 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-06-12 00:46:30 +00:00
|
|
|
return err;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2010-06-19 14:25:11 +00:00
|
|
|
}
|
|
|
|
|
2022-06-12 00:46:30 +00:00
|
|
|
return 0;
|
2010-06-19 14:25:11 +00:00
|
|
|
}
|