2011-08-01 10:03:26 +02:00
|
|
|
/*
|
|
|
|
|
2016-01-18 00:57:46 +01:00
|
|
|
Copyright (c) 2007-2016, Arvid Norberg
|
2011-08-01 10:03:26 +02:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#include "libtorrent/config.hpp"
|
2011-08-01 10:03:26 +02:00
|
|
|
#include "libtorrent/disk_buffer_pool.hpp"
|
|
|
|
#include "libtorrent/assert.hpp"
|
2014-07-06 21:18:00 +02:00
|
|
|
#include "libtorrent/allocator.hpp"
|
|
|
|
#include "libtorrent/aux_/session_settings.hpp"
|
|
|
|
#include "libtorrent/io_service.hpp"
|
|
|
|
#include "libtorrent/alert.hpp"
|
|
|
|
#include "libtorrent/alert_types.hpp"
|
|
|
|
#include "libtorrent/disk_observer.hpp"
|
2015-11-08 04:00:20 +01:00
|
|
|
#include "libtorrent/platform_util.hpp" // for total_physical_ram
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2015-08-18 16:42:03 +02:00
|
|
|
#include "libtorrent/aux_/disable_warnings_push.hpp"
|
|
|
|
|
2011-11-16 08:09:12 +01:00
|
|
|
#include <algorithm>
|
2016-05-25 06:31:52 +02:00
|
|
|
#include <functional>
|
2014-07-06 21:18:00 +02:00
|
|
|
#include <boost/system/error_code.hpp>
|
|
|
|
#include <boost/shared_ptr.hpp>
|
2011-08-01 10:03:26 +02:00
|
|
|
|
2016-03-17 06:00:04 +01:00
|
|
|
#if TORRENT_HAVE_MMAP
|
2011-08-01 10:03:26 +02:00
|
|
|
#include <sys/mman.h>
|
|
|
|
#endif
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_BSD
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef TORRENT_LINUX
|
|
|
|
#include <linux/unistd.h>
|
|
|
|
#endif
|
|
|
|
|
2015-08-18 16:42:03 +02:00
|
|
|
#include "libtorrent/aux_/disable_warnings_pop.hpp"
|
|
|
|
|
2011-08-01 10:03:26 +02:00
|
|
|
namespace libtorrent
|
|
|
|
{
|
2016-03-16 02:10:58 +01:00
|
|
|
namespace {
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// this is posted to the network thread
|
2016-05-07 06:55:38 +02:00
|
|
|
void watermark_callback(std::vector<boost::weak_ptr<disk_observer>> const& cbs)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-05-07 06:55:38 +02:00
|
|
|
for (auto const& i : cbs)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-05-07 06:55:38 +02:00
|
|
|
boost::shared_ptr<disk_observer> o = i.lock();
|
|
|
|
if (o) o->on_disk();
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-16 02:10:58 +01:00
|
|
|
} // anonymous namespace
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
disk_buffer_pool::disk_buffer_pool(int block_size, io_service& ios
|
2015-04-03 22:15:48 +02:00
|
|
|
, boost::function<void()> const& trigger_trim)
|
2011-08-01 10:03:26 +02:00
|
|
|
: m_block_size(block_size)
|
|
|
|
, m_in_use(0)
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_max_use(64)
|
|
|
|
, m_low_watermark((std::max)(m_max_use - 32, 0))
|
|
|
|
, m_trigger_cache_trim(trigger_trim)
|
|
|
|
, m_exceeded_max_size(false)
|
|
|
|
, m_ios(ios)
|
|
|
|
, m_cache_buffer_chunk_size(0)
|
|
|
|
#if TORRENT_HAVE_MMAP
|
|
|
|
, m_cache_fd(-1)
|
|
|
|
, m_cache_pool(0)
|
|
|
|
#endif
|
2013-03-21 02:18:39 +01:00
|
|
|
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
|
|
|
, m_using_pool_allocator(false)
|
2014-07-06 21:18:00 +02:00
|
|
|
, m_want_pool_allocator(false)
|
|
|
|
, m_pool(block_size, 32)
|
2013-03-21 02:18:39 +01:00
|
|
|
#endif
|
2011-08-01 10:03:26 +02:00
|
|
|
{
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2011-08-01 10:03:26 +02:00
|
|
|
m_magic = 0x1337;
|
2014-07-06 21:18:00 +02:00
|
|
|
m_settings_set = false;
|
2011-08-01 10:03:26 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
disk_buffer_pool::~disk_buffer_pool()
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_magic == 0x1337);
|
2014-07-06 21:18:00 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2011-08-01 10:03:26 +02:00
|
|
|
m_magic = 0;
|
|
|
|
#endif
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#if TORRENT_HAVE_MMAP
|
|
|
|
if (m_cache_pool)
|
|
|
|
{
|
2016-06-18 20:01:38 +02:00
|
|
|
munmap(m_cache_pool, std::uint64_t(m_max_use) * 0x4000);
|
2014-07-06 21:18:00 +02:00
|
|
|
m_cache_pool = 0;
|
|
|
|
// attempt to make MacOS not flush this to disk, making close()
|
|
|
|
// block for a long time
|
|
|
|
ftruncate(m_cache_fd, 0);
|
|
|
|
close(m_cache_fd);
|
|
|
|
m_cache_fd = -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::uint32_t disk_buffer_pool::num_to_evict(int num_needed)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
2016-05-01 00:54:23 +02:00
|
|
|
std::unique_lock<std::mutex> l(m_pool_mutex);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (m_exceeded_max_size)
|
2016-03-17 01:13:28 +01:00
|
|
|
ret = m_in_use - (std::min)(m_low_watermark, int(m_max_use - m_observers.size()*2));
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (m_in_use + num_needed > m_max_use)
|
|
|
|
ret = (std::max)(ret, int(m_in_use + num_needed - m_max_use));
|
|
|
|
|
|
|
|
if (ret < 0) ret = 0;
|
|
|
|
else if (ret > m_in_use) ret = m_in_use;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// checks to see if we're no longer exceeding the high watermark,
|
|
|
|
// and if we're in fact below the low watermark. If so, we need to
|
|
|
|
// post the notification messages to the peers that are waiting for
|
|
|
|
// more buffers to received data into
|
2016-05-01 00:54:23 +02:00
|
|
|
void disk_buffer_pool::check_buffer_level(std::unique_lock<std::mutex>& l)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2016-05-01 00:54:23 +02:00
|
|
|
TORRENT_ASSERT(l.owns_lock());
|
2014-07-06 21:18:00 +02:00
|
|
|
if (!m_exceeded_max_size || m_in_use > m_low_watermark) return;
|
|
|
|
|
|
|
|
m_exceeded_max_size = false;
|
|
|
|
|
2016-05-07 06:55:38 +02:00
|
|
|
std::vector<boost::weak_ptr<disk_observer>> cbs;
|
|
|
|
m_observers.swap(cbs);
|
2014-07-06 21:18:00 +02:00
|
|
|
l.unlock();
|
2016-05-07 06:55:38 +02:00
|
|
|
m_ios.post(std::bind(&watermark_callback, std::move(cbs)));
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
2014-07-19 10:20:20 +02:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2011-08-01 10:03:26 +02:00
|
|
|
bool disk_buffer_pool::is_disk_buffer(char* buffer
|
2016-05-01 00:54:23 +02:00
|
|
|
, std::unique_lock<std::mutex>& l) const
|
2011-08-01 10:03:26 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_magic == 0x1337);
|
2016-05-01 00:54:23 +02:00
|
|
|
TORRENT_ASSERT(l.owns_lock());
|
2015-04-22 06:24:45 +02:00
|
|
|
TORRENT_UNUSED(l);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
#if TORRENT_HAVE_MMAP
|
|
|
|
if (m_cache_pool)
|
|
|
|
{
|
2015-04-21 06:30:34 +02:00
|
|
|
return buffer >= m_cache_pool && buffer < m_cache_pool
|
2016-06-18 20:01:38 +02:00
|
|
|
+ std::uint64_t(m_max_use) * 0x4000;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-07-02 01:46:59 +02:00
|
|
|
#if TORRENT_USE_INVARIANT_CHECKS
|
2014-07-06 21:18:00 +02:00
|
|
|
return m_buffers_in_use.count(buffer) == 1;
|
2015-04-21 06:30:34 +02:00
|
|
|
#elif defined TORRENT_DEBUG_BUFFERS
|
2014-07-06 21:18:00 +02:00
|
|
|
return page_aligned_allocator::in_use(buffer);
|
2015-04-21 06:30:34 +02:00
|
|
|
#elif defined TORRENT_DISABLE_POOL_ALLOCATOR
|
2011-08-01 10:03:26 +02:00
|
|
|
return true;
|
2013-03-21 02:18:39 +01:00
|
|
|
#else
|
|
|
|
if (m_using_pool_allocator)
|
|
|
|
return m_pool.is_from(buffer);
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
#endif
|
2011-08-01 10:03:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool disk_buffer_pool::is_disk_buffer(char* buffer) const
|
|
|
|
{
|
2016-05-01 00:54:23 +02:00
|
|
|
std::unique_lock<std::mutex> l(m_pool_mutex);
|
2011-08-01 10:03:26 +02:00
|
|
|
return is_disk_buffer(buffer, l);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
char* disk_buffer_pool::allocate_buffer(char const* category)
|
|
|
|
{
|
2016-05-01 00:54:23 +02:00
|
|
|
std::unique_lock<std::mutex> l(m_pool_mutex);
|
2014-07-06 21:18:00 +02:00
|
|
|
return allocate_buffer_impl(l, category);
|
|
|
|
}
|
|
|
|
|
2014-11-17 04:10:00 +01:00
|
|
|
// we allow allocating more blocks even after we exceed the max size,
|
|
|
|
// but communicate back to the allocator (typically the peer_connection)
|
|
|
|
// that we have exceeded the limit via the out-parameter "exceeded". The
|
|
|
|
// caller is expected to honor this by not allocating any more buffers
|
|
|
|
// until the disk_observer object (passed in as "o") is invoked, indicating
|
|
|
|
// that there's more room in the pool now. This caps the amount of over-
|
|
|
|
// allocation to one block per peer connection.
|
2014-07-06 21:18:00 +02:00
|
|
|
char* disk_buffer_pool::allocate_buffer(bool& exceeded
|
|
|
|
, boost::shared_ptr<disk_observer> o, char const* category)
|
|
|
|
{
|
2016-05-01 00:54:23 +02:00
|
|
|
std::unique_lock<std::mutex> l(m_pool_mutex);
|
2014-07-06 21:18:00 +02:00
|
|
|
char* ret = allocate_buffer_impl(l, category);
|
|
|
|
if (m_exceeded_max_size)
|
|
|
|
{
|
|
|
|
exceeded = true;
|
|
|
|
if (o) m_observers.push_back(o);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-02-15 00:20:45 +01:00
|
|
|
// this function allocates buffers and
|
|
|
|
// fills in the iovec array with the buffers
|
|
|
|
int disk_buffer_pool::allocate_iovec(file::iovec_t* iov, int iov_len)
|
|
|
|
{
|
2016-05-01 00:54:23 +02:00
|
|
|
std::unique_lock<std::mutex> l(m_pool_mutex);
|
2015-02-15 00:20:45 +01:00
|
|
|
for (int i = 0; i < iov_len; ++i)
|
|
|
|
{
|
|
|
|
iov[i].iov_base = allocate_buffer_impl(l, "pending read");
|
|
|
|
iov[i].iov_len = block_size();
|
2016-06-20 17:32:06 +02:00
|
|
|
if (iov[i].iov_base == nullptr)
|
2015-02-15 00:20:45 +01:00
|
|
|
{
|
|
|
|
// uh oh. We failed to allocate the buffer!
|
|
|
|
// we need to roll back and free all the buffers
|
|
|
|
// we've already allocated
|
|
|
|
for (int j = 0; j < i; ++j)
|
2015-08-08 08:33:54 +02:00
|
|
|
free_buffer_impl(static_cast<char*>(iov[j].iov_base), l);
|
2015-02-15 00:20:45 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void disk_buffer_pool::free_iovec(file::iovec_t* iov, int iov_len)
|
|
|
|
{
|
|
|
|
// TODO: perhaps we should sort the buffers here?
|
2016-05-01 00:54:23 +02:00
|
|
|
std::unique_lock<std::mutex> l(m_pool_mutex);
|
2015-02-15 00:20:45 +01:00
|
|
|
for (int i = 0; i < iov_len; ++i)
|
2015-08-08 08:33:54 +02:00
|
|
|
free_buffer_impl(static_cast<char*>(iov[i].iov_base), l);
|
2015-02-15 00:20:45 +01:00
|
|
|
check_buffer_level(l);
|
|
|
|
}
|
|
|
|
|
2016-05-01 00:54:23 +02:00
|
|
|
char* disk_buffer_pool::allocate_buffer_impl(std::unique_lock<std::mutex>& l
|
2015-04-19 08:28:21 +02:00
|
|
|
, char const*)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_settings_set);
|
2011-08-01 10:03:26 +02:00
|
|
|
TORRENT_ASSERT(m_magic == 0x1337);
|
2016-05-01 00:54:23 +02:00
|
|
|
TORRENT_ASSERT(l.owns_lock());
|
2015-04-22 06:24:45 +02:00
|
|
|
TORRENT_UNUSED(l);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2013-03-21 02:18:39 +01:00
|
|
|
char* ret;
|
2014-07-06 21:18:00 +02:00
|
|
|
#if TORRENT_HAVE_MMAP
|
|
|
|
if (m_cache_pool)
|
2013-03-21 02:18:39 +01:00
|
|
|
{
|
2015-02-15 00:20:45 +01:00
|
|
|
if (m_free_list.size() <= (m_max_use - m_low_watermark)
|
|
|
|
/ 2 && !m_exceeded_max_size)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
m_exceeded_max_size = true;
|
|
|
|
m_trigger_cache_trim();
|
|
|
|
}
|
|
|
|
if (m_free_list.empty()) return 0;
|
2016-06-18 20:01:38 +02:00
|
|
|
std::uint64_t slot_index = m_free_list.back();
|
2014-07-06 21:18:00 +02:00
|
|
|
m_free_list.pop_back();
|
|
|
|
ret = m_cache_pool + (slot_index * 0x4000);
|
|
|
|
TORRENT_ASSERT(is_disk_buffer(ret, l));
|
2013-03-21 02:18:39 +01:00
|
|
|
}
|
|
|
|
else
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
2013-03-21 02:18:39 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
#if defined TORRENT_DISABLE_POOL_ALLOCATOR
|
|
|
|
|
2013-03-21 02:18:39 +01:00
|
|
|
ret = page_aligned_allocator::malloc(m_block_size);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
#else
|
|
|
|
if (m_using_pool_allocator)
|
|
|
|
{
|
2016-03-05 17:02:41 +01:00
|
|
|
int const effective_block_size
|
|
|
|
= m_in_use >= m_max_use
|
|
|
|
? 20 // use small increments once we've exceeded the cache size
|
|
|
|
: m_cache_buffer_chunk_size
|
2014-07-06 21:18:00 +02:00
|
|
|
? m_cache_buffer_chunk_size
|
|
|
|
: (std::max)(m_max_use / 10, 1);
|
|
|
|
m_pool.set_next_size(effective_block_size);
|
2016-03-05 17:02:41 +01:00
|
|
|
ret = static_cast<char*>(m_pool.malloc());
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret = page_aligned_allocator::malloc(m_block_size);
|
|
|
|
}
|
|
|
|
#endif
|
2016-06-20 17:32:06 +02:00
|
|
|
if (ret == nullptr)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
m_exceeded_max_size = true;
|
|
|
|
m_trigger_cache_trim();
|
|
|
|
return 0;
|
|
|
|
}
|
2013-03-21 02:18:39 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2016-07-02 01:46:59 +02:00
|
|
|
#if TORRENT_USE_INVARIANT_CHECKS
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(m_buffers_in_use.count(ret) == 0);
|
|
|
|
m_buffers_in_use.insert(ret);
|
2013-03-21 02:18:39 +01:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2011-08-01 10:03:26 +02:00
|
|
|
++m_in_use;
|
2015-02-15 00:20:45 +01:00
|
|
|
if (m_in_use >= m_low_watermark + (m_max_use - m_low_watermark)
|
|
|
|
/ 2 && !m_exceeded_max_size)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
m_exceeded_max_size = true;
|
|
|
|
m_trigger_cache_trim();
|
|
|
|
}
|
2011-08-01 10:03:26 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(is_disk_buffer(ret, l));
|
2011-08-01 10:03:26 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void disk_buffer_pool::free_multiple_buffers(char** bufvec, int numbufs)
|
|
|
|
{
|
|
|
|
char** end = bufvec + numbufs;
|
|
|
|
// sort the pointers in order to maximize cache hits
|
|
|
|
std::sort(bufvec, end);
|
|
|
|
|
2016-05-01 00:54:23 +02:00
|
|
|
std::unique_lock<std::mutex> l(m_pool_mutex);
|
2011-08-01 10:03:26 +02:00
|
|
|
for (; bufvec != end; ++bufvec)
|
|
|
|
{
|
|
|
|
char* buf = *bufvec;
|
|
|
|
TORRENT_ASSERT(buf);
|
2014-07-06 21:18:00 +02:00
|
|
|
free_buffer_impl(buf, l);
|
2011-08-01 10:03:26 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
check_buffer_level(l);
|
2011-08-01 10:03:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void disk_buffer_pool::free_buffer(char* buf)
|
|
|
|
{
|
2016-05-01 00:54:23 +02:00
|
|
|
std::unique_lock<std::mutex> l(m_pool_mutex);
|
2011-08-01 10:03:26 +02:00
|
|
|
free_buffer_impl(buf, l);
|
2014-07-06 21:18:00 +02:00
|
|
|
check_buffer_level(l);
|
|
|
|
}
|
|
|
|
|
2015-04-03 22:15:48 +02:00
|
|
|
void disk_buffer_pool::set_settings(aux::session_settings const& sett
|
|
|
|
, error_code& ec)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
2015-11-19 01:51:17 +01:00
|
|
|
TORRENT_UNUSED(ec);
|
|
|
|
|
2016-05-01 00:54:23 +02:00
|
|
|
std::unique_lock<std::mutex> l(m_pool_mutex);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
// 0 cache_buffer_chunk_size means 'automatic' (i.e.
|
|
|
|
// proportional to the total disk cache size)
|
|
|
|
m_cache_buffer_chunk_size = sett.get_int(settings_pack::cache_buffer_chunk_size);
|
|
|
|
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
2016-03-05 17:02:41 +01:00
|
|
|
// if the chunk size is set to 1, there's no point in creating a pool
|
|
|
|
m_want_pool_allocator = sett.get_bool(settings_pack::use_disk_cache_pool)
|
|
|
|
&& (m_cache_buffer_chunk_size != 1);
|
2014-07-06 21:18:00 +02:00
|
|
|
// if there are no allocated blocks, it's OK to switch allocator
|
|
|
|
if (m_in_use == 0)
|
|
|
|
m_using_pool_allocator = m_want_pool_allocator;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if TORRENT_HAVE_MMAP
|
|
|
|
// if we've already allocated an mmap, we can't change
|
|
|
|
// anything unless there are no allocations in use
|
|
|
|
if (m_cache_pool && m_in_use > 0) return;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// only allow changing size if we're not using mmapped
|
|
|
|
// cache, or if we're just about to turn it off
|
|
|
|
if (
|
|
|
|
#if TORRENT_HAVE_MMAP
|
|
|
|
m_cache_pool == 0 ||
|
|
|
|
#endif
|
|
|
|
sett.get_str(settings_pack::mmap_cache).empty())
|
|
|
|
{
|
2016-03-05 02:20:49 +01:00
|
|
|
int const cache_size = sett.get_int(settings_pack::cache_size);
|
2014-07-06 21:18:00 +02:00
|
|
|
if (cache_size < 0)
|
|
|
|
{
|
2016-06-18 20:01:38 +02:00
|
|
|
std::uint64_t phys_ram = total_physical_ram();
|
2014-07-06 21:18:00 +02:00
|
|
|
if (phys_ram == 0) m_max_use = 1024;
|
2016-03-06 07:42:46 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// this is the logic to calculate the automatic disk cache size
|
|
|
|
// based on the amount of physical RAM.
|
|
|
|
// The more physical RAM, the smaller portion of it is allocated
|
|
|
|
// for the cache.
|
|
|
|
|
|
|
|
// we take a 30th of everything exceeding 4 GiB
|
|
|
|
// a 20th of everything exceeding 1 GiB
|
|
|
|
// and a 10th of everything below a GiB
|
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t const gb = 1024 * 1024 * 1024;
|
2016-03-06 07:42:46 +01:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t result = 0;
|
2016-03-06 07:42:46 +01:00
|
|
|
if (phys_ram > 4 * gb)
|
|
|
|
{
|
|
|
|
result += (phys_ram - 4 * gb) / 30;
|
|
|
|
phys_ram = 4 * gb;
|
|
|
|
}
|
|
|
|
if (phys_ram > 1 * gb)
|
|
|
|
{
|
|
|
|
result += (phys_ram - 1 * gb) / 20;
|
|
|
|
phys_ram = 1 * gb;
|
|
|
|
}
|
|
|
|
result += phys_ram / 10;
|
|
|
|
m_max_use = result / m_block_size;
|
|
|
|
}
|
2015-11-08 04:00:20 +01:00
|
|
|
|
2016-04-17 22:56:07 +02:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(push)
|
|
|
|
#pragma warning(disable : 4127 ) /* warning C4127: conditional expression is constant */
|
|
|
|
#endif // _MSC_VER
|
2015-11-08 04:00:20 +01:00
|
|
|
if (sizeof(void*) == 4)
|
2016-04-17 22:56:07 +02:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif // _MSC_VER
|
2015-11-08 04:00:20 +01:00
|
|
|
{
|
|
|
|
// 32 bit builds should capped below 2 GB of memory, even
|
|
|
|
// when more actual ram is available, because we're still
|
|
|
|
// constrained by the 32 bit virtual address space.
|
2015-11-08 16:49:38 +01:00
|
|
|
m_max_use = (std::min)(2 * 1024 * 1024 * 3 / 4 * 1024
|
|
|
|
/ m_block_size, m_max_use);
|
2015-11-08 04:00:20 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_max_use = cache_size;
|
|
|
|
}
|
|
|
|
m_low_watermark = m_max_use - (std::max)(16, sett.get_int(settings_pack::max_queued_disk_bytes) / 0x4000);
|
|
|
|
if (m_low_watermark < 0) m_low_watermark = 0;
|
|
|
|
if (m_in_use >= m_max_use && !m_exceeded_max_size)
|
|
|
|
{
|
|
|
|
m_exceeded_max_size = true;
|
|
|
|
m_trigger_cache_trim();
|
|
|
|
}
|
2016-03-05 17:02:41 +01:00
|
|
|
if (m_cache_buffer_chunk_size > m_max_use)
|
|
|
|
m_cache_buffer_chunk_size = m_max_use;
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if TORRENT_USE_ASSERTS
|
|
|
|
m_settings_set = true;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if TORRENT_HAVE_MMAP
|
|
|
|
// #error support resizing the map
|
|
|
|
if (m_cache_pool && sett.get_str(settings_pack::mmap_cache).empty())
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_in_use == 0);
|
2016-06-18 20:01:38 +02:00
|
|
|
munmap(m_cache_pool, std::uint64_t(m_max_use) * 0x4000);
|
2014-07-06 21:18:00 +02:00
|
|
|
m_cache_pool = 0;
|
|
|
|
// attempt to make MacOS not flush this to disk, making close()
|
|
|
|
// block for a long time
|
|
|
|
ftruncate(m_cache_fd, 0);
|
|
|
|
close(m_cache_fd);
|
|
|
|
m_cache_fd = -1;
|
|
|
|
std::vector<int>().swap(m_free_list);
|
|
|
|
}
|
|
|
|
else if (m_cache_pool == 0 && !sett.get_str(settings_pack::mmap_cache).empty())
|
|
|
|
{
|
|
|
|
// O_TRUNC here is because we don't actually care about what's
|
|
|
|
// in the file now, there's no need to ever read that into RAM
|
|
|
|
#ifndef O_EXLOCK
|
|
|
|
#define O_EXLOCK 0
|
|
|
|
#endif
|
|
|
|
m_cache_fd = open(sett.get_str(settings_pack::mmap_cache).c_str(), O_RDWR | O_CREAT | O_EXLOCK | O_TRUNC, 0700);
|
|
|
|
if (m_cache_fd < 0)
|
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, boost::system::system_category());
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifndef MAP_NOCACHE
|
|
|
|
#define MAP_NOCACHE 0
|
|
|
|
#endif
|
2016-06-18 20:01:38 +02:00
|
|
|
ftruncate(m_cache_fd, std::uint64_t(m_max_use) * 0x4000);
|
|
|
|
m_cache_pool = static_cast<char*>(mmap(0, std::uint64_t(m_max_use) * 0x4000, PROT_READ | PROT_WRITE
|
2015-08-08 08:33:54 +02:00
|
|
|
, MAP_SHARED | MAP_NOCACHE, m_cache_fd, 0));
|
2014-07-06 21:18:00 +02:00
|
|
|
if (intptr_t(m_cache_pool) == -1)
|
|
|
|
{
|
2015-11-24 06:50:51 +01:00
|
|
|
ec.assign(errno, boost::system::system_category());
|
2015-04-03 22:15:48 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
m_cache_pool = 0;
|
|
|
|
// attempt to make MacOS not flush this to disk, making close()
|
|
|
|
// block for a long time
|
|
|
|
ftruncate(m_cache_fd, 0);
|
|
|
|
close(m_cache_fd);
|
|
|
|
m_cache_fd = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT((size_t(m_cache_pool) & 0xfff) == 0);
|
|
|
|
m_free_list.reserve(m_max_use);
|
|
|
|
for (int i = 0; i < m_max_use; ++i)
|
|
|
|
m_free_list.push_back(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2011-08-01 10:03:26 +02:00
|
|
|
}
|
|
|
|
|
2016-05-01 00:54:23 +02:00
|
|
|
void disk_buffer_pool::free_buffer_impl(char* buf, std::unique_lock<std::mutex>& l)
|
2011-08-01 10:03:26 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(buf);
|
|
|
|
TORRENT_ASSERT(m_magic == 0x1337);
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(m_settings_set);
|
2011-08-01 10:03:26 +02:00
|
|
|
TORRENT_ASSERT(is_disk_buffer(buf, l));
|
2016-05-01 00:54:23 +02:00
|
|
|
TORRENT_ASSERT(l.owns_lock());
|
2015-04-22 06:24:45 +02:00
|
|
|
TORRENT_UNUSED(l);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
#if TORRENT_HAVE_MMAP
|
|
|
|
if (m_cache_pool)
|
2011-08-01 10:03:26 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(buf >= m_cache_pool);
|
2016-06-18 20:01:38 +02:00
|
|
|
TORRENT_ASSERT(buf < m_cache_pool + std::uint64_t(m_max_use) * 0x4000);
|
2014-07-06 21:18:00 +02:00
|
|
|
int slot_index = (buf - m_cache_pool) / 0x4000;
|
|
|
|
m_free_list.push_back(slot_index);
|
|
|
|
#if defined MADV_FREE
|
|
|
|
// tell the virtual memory system that we don't actually care
|
|
|
|
// about the data in these pages anymore. If this block was
|
|
|
|
// swapped out to the SSD, it (hopefully) means it won't have
|
|
|
|
// to be read back in once we start writing our new data to it
|
|
|
|
madvise(buf, 0x4000, MADV_FREE);
|
|
|
|
#elif defined MADV_DONTNEED && defined TORRENT_LINUX
|
|
|
|
// rumor has it that MADV_DONTNEED is in fact destructive
|
|
|
|
// on linux (i.e. it won't flush it to disk or re-read from disk)
|
|
|
|
// http://kerneltrap.org/mailarchive/linux-kernel/2007/5/1/84410
|
|
|
|
madvise(buf, 0x4000, MADV_DONTNEED);
|
|
|
|
#endif
|
2011-08-01 10:03:26 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
else
|
2011-08-01 10:03:26 +02:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
#if defined TORRENT_DISABLE_POOL_ALLOCATOR
|
|
|
|
|
2011-08-01 10:03:26 +02:00
|
|
|
page_aligned_allocator::free(buf);
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2013-03-21 02:18:39 +01:00
|
|
|
#else
|
|
|
|
if (m_using_pool_allocator)
|
|
|
|
m_pool.free(buf);
|
|
|
|
else
|
|
|
|
page_aligned_allocator::free(buf);
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif // TORRENT_DISABLE_POOL_ALLOCATOR
|
|
|
|
}
|
|
|
|
|
2016-07-02 01:46:59 +02:00
|
|
|
#if TORRENT_USE_INVARIANT_CHECKS
|
2014-07-06 21:18:00 +02:00
|
|
|
std::set<char*>::iterator i = m_buffers_in_use.find(buf);
|
|
|
|
TORRENT_ASSERT(i != m_buffers_in_use.end());
|
|
|
|
m_buffers_in_use.erase(i);
|
2013-03-21 02:18:39 +01:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2011-08-01 10:03:26 +02:00
|
|
|
--m_in_use;
|
2013-03-21 02:18:39 +01:00
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
|
|
|
// should we switch which allocator to use?
|
2014-07-06 21:18:00 +02:00
|
|
|
if (m_in_use == 0 && m_want_pool_allocator != m_using_pool_allocator)
|
2013-03-21 02:18:39 +01:00
|
|
|
{
|
|
|
|
m_pool.release_memory();
|
2014-07-06 21:18:00 +02:00
|
|
|
m_using_pool_allocator = m_want_pool_allocator;
|
2013-03-21 02:18:39 +01:00
|
|
|
}
|
|
|
|
#endif
|
2011-08-01 10:03:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void disk_buffer_pool::release_memory()
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_magic == 0x1337);
|
2013-03-21 02:18:39 +01:00
|
|
|
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
2016-05-01 00:54:23 +02:00
|
|
|
std::unique_lock<std::mutex> l(m_pool_mutex);
|
2013-03-21 02:18:39 +01:00
|
|
|
if (m_using_pool_allocator)
|
|
|
|
m_pool.release_memory();
|
|
|
|
#endif
|
2011-08-01 10:03:26 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2011-08-01 10:03:26 +02:00
|
|
|
}
|
|
|
|
|