Merge pull request #402 from arvidn/resume-data-no-timestamps

Resume data no timestamps
This commit is contained in:
Arvid Norberg 2016-02-07 01:47:53 -05:00
commit ee94ac6ef7
25 changed files with 240 additions and 681 deletions

View File

@ -1,3 +1,4 @@
* resume data no longer has timestamps of files
1.1.0 release 1.1.0 release

View File

@ -110,7 +110,7 @@ namespace libtorrent {
#endif #endif
bool pending() const; bool pending() const;
void get_all(std::vector<alert*>& alerts, int& num_resume); void get_all(std::vector<alert*>& alerts);
template <class T> template <class T>
bool should_post() const bool should_post() const
@ -147,8 +147,6 @@ namespace libtorrent {
void set_dispatch_function(boost::function<void(std::auto_ptr<alert>)> const&); void set_dispatch_function(boost::function<void(std::auto_ptr<alert>)> const&);
#endif #endif
int num_queued_resume() const;
#ifndef TORRENT_DISABLE_EXTENSIONS #ifndef TORRENT_DISABLE_EXTENSIONS
void add_extension(boost::shared_ptr<plugin> ext); void add_extension(boost::shared_ptr<plugin> ext);
#endif #endif
@ -179,9 +177,6 @@ namespace libtorrent {
// posted to the queue // posted to the queue
boost::function<void()> m_notify; boost::function<void()> m_notify;
// the number of resume data alerts in the alert queue
int m_num_queued_resume;
// this is either 0 or 1, it indicates which m_alerts and m_allocations // this is either 0 or 1, it indicates which m_alerts and m_allocations
// the alert_manager is allowed to use right now. This is swapped when // the alert_manager is allowed to use right now. This is swapped when
// the client calls get_all(), at which point all of the alert objects // the client calls get_all(), at which point all of the alert objects

View File

@ -781,16 +781,6 @@ namespace libtorrent
std::map<std::string, boost::shared_ptr<torrent> > m_uuids; std::map<std::string, boost::shared_ptr<torrent> > m_uuids;
// when saving resume data for many torrents, torrents are
// queued up in this list in order to not have too many of them
// outstanding at any given time, since the resume data may use
// a lot of memory.
std::list<boost::shared_ptr<torrent> > m_save_resume_queue;
// the number of save resume data disk jobs that are currently
// outstanding
int m_num_save_resume;
// peer connections are put here when disconnected to avoid // peer connections are put here when disconnected to avoid
// race conditions with the disk thread. It's important that // race conditions with the disk thread. It's important that
// peer connections are destructed from the network thread, // peer connections are destructed from the network thread,
@ -1162,12 +1152,6 @@ namespace libtorrent
std::list<boost::shared_ptr<tracker_logger> > m_tracker_loggers; std::list<boost::shared_ptr<tracker_logger> > m_tracker_loggers;
#endif #endif
// TODO: 2 the throttling of saving resume data could probably be
// factored out into a separate class
virtual void queue_async_resume_data(boost::shared_ptr<torrent> const& t) TORRENT_OVERRIDE;
virtual void done_async_resume() TORRENT_OVERRIDE;
void async_resume_dispatched();
// state for keeping track of external IPs // state for keeping track of external IPs
external_ip m_external_ip; external_ip m_external_ip;

View File

@ -166,8 +166,6 @@ namespace libtorrent { namespace aux
virtual bool has_connection(peer_connection* p) const = 0; virtual bool has_connection(peer_connection* p) const = 0;
virtual void insert_peer(boost::shared_ptr<peer_connection> const& c) = 0; virtual void insert_peer(boost::shared_ptr<peer_connection> const& c) = 0;
virtual void queue_async_resume_data(boost::shared_ptr<torrent> const& t) = 0;
virtual void done_async_resume() = 0;
virtual void evict_torrent(torrent* t) = 0; virtual void evict_torrent(torrent* t) = 0;
virtual void remove_torrent(torrent_handle const& h, int options = 0) = 0; virtual void remove_torrent(torrent_handle const& h, int options = 0) = 0;

View File

@ -86,8 +86,6 @@ namespace libtorrent
, boost::function<void(disk_io_job const*)> const& handler) = 0; , boost::function<void(disk_io_job const*)> const& handler) = 0;
virtual void async_delete_files(piece_manager* storage virtual void async_delete_files(piece_manager* storage
, boost::function<void(disk_io_job const*)> const& handler) = 0; , boost::function<void(disk_io_job const*)> const& handler) = 0;
virtual void async_save_resume_data(piece_manager* storage
, boost::function<void(disk_io_job const*)> const& handler) = 0;
virtual void async_set_file_priority(piece_manager* storage virtual void async_set_file_priority(piece_manager* storage
, std::vector<boost::uint8_t> const& prio , std::vector<boost::uint8_t> const& prio
, boost::function<void(disk_io_job const*)> const& handler) = 0; , boost::function<void(disk_io_job const*)> const& handler) = 0;

View File

@ -89,7 +89,6 @@ namespace libtorrent
, release_files , release_files
, delete_files , delete_files
, check_fastresume , check_fastresume
, save_resume_data
, rename_file , rename_file
, stop_torrent , stop_torrent
, cache_piece , cache_piece
@ -151,8 +150,6 @@ namespace libtorrent
// for other jobs, it may point to other job-specific types // for other jobs, it may point to other job-specific types
// for move_storage and rename_file this is a string allocated // for move_storage and rename_file this is a string allocated
// with malloc() // with malloc()
// an entry* for save_resume_data
// for aiocb_complete this points to the aiocb that completed
// for get_cache_info this points to a cache_status object which // for get_cache_info this points to a cache_status object which
// is filled in // is filled in
union union

View File

@ -317,8 +317,6 @@ namespace libtorrent
, bdecode_node const* resume_data , bdecode_node const* resume_data
, std::vector<std::string>& links , std::vector<std::string>& links
, boost::function<void(disk_io_job const*)> const& handler); , boost::function<void(disk_io_job const*)> const& handler);
void async_save_resume_data(piece_manager* storage
, boost::function<void(disk_io_job const*)> const& handler);
void async_rename_file(piece_manager* storage, int index, std::string const& name void async_rename_file(piece_manager* storage, int index, std::string const& name
, boost::function<void(disk_io_job const*)> const& handler); , boost::function<void(disk_io_job const*)> const& handler);
void async_stop_torrent(piece_manager* storage void async_stop_torrent(piece_manager* storage
@ -414,7 +412,6 @@ namespace libtorrent
int do_release_files(disk_io_job* j, jobqueue_t& completed_jobs); int do_release_files(disk_io_job* j, jobqueue_t& completed_jobs);
int do_delete_files(disk_io_job* j, jobqueue_t& completed_jobs); int do_delete_files(disk_io_job* j, jobqueue_t& completed_jobs);
int do_check_fastresume(disk_io_job* j, jobqueue_t& completed_jobs); int do_check_fastresume(disk_io_job* j, jobqueue_t& completed_jobs);
int do_save_resume_data(disk_io_job* j, jobqueue_t& completed_jobs);
int do_rename_file(disk_io_job* j, jobqueue_t& completed_jobs); int do_rename_file(disk_io_job* j, jobqueue_t& completed_jobs);
int do_stop_torrent(disk_io_job* j, jobqueue_t& completed_jobs); int do_stop_torrent(disk_io_job* j, jobqueue_t& completed_jobs);
int do_read_and_hash(disk_io_job* j, jobqueue_t& completed_jobs); int do_read_and_hash(disk_io_job* j, jobqueue_t& completed_jobs);

View File

@ -437,6 +437,7 @@ namespace libtorrent
enable_outgoing_tcp, enable_outgoing_tcp,
enable_incoming_tcp, enable_incoming_tcp,
#ifndef TORRENT_NO_DEPRECATE
// ``ignore_resume_timestamps`` determines if the storage, when // ``ignore_resume_timestamps`` determines if the storage, when
// loading resume data files, should verify that the file modification // loading resume data files, should verify that the file modification
// time with the timestamps in the resume data. This defaults to // time with the timestamps in the resume data. This defaults to
@ -447,6 +448,10 @@ namespace libtorrent
// redownload potentially missed pieces than to go through the whole // redownload potentially missed pieces than to go through the whole
// storage to look for them. // storage to look for them.
ignore_resume_timestamps, ignore_resume_timestamps,
#else
// hidden
deprecated8,
#endif
// ``no_recheck_incomplete_resume`` determines if the storage should // ``no_recheck_incomplete_resume`` determines if the storage should
// check the whole files when resume data is incomplete or missing or // check the whole files when resume data is incomplete or missing or

View File

@ -35,13 +35,15 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/aux_/disable_warnings_push.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp"
#include <time.h>
#include <vector> #include <vector>
#include <string>
#include <boost/cstdint.hpp> #include <boost/cstdint.hpp>
#include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/aux_/disable_warnings_pop.hpp"
#include "libtorrent/config.hpp" #include "libtorrent/config.hpp"
#include "libtorrent/error_code.hpp"
#include "libtorrent/file_storage.hpp"
namespace libtorrent namespace libtorrent
{ {
@ -50,36 +52,52 @@ namespace libtorrent
stat_cache(); stat_cache();
~stat_cache(); ~stat_cache();
void init(int num_files); void reserve(int num_files);
enum // returns the size of the file unless an error occurs, in which case ec
{ // is set to indicate the error
cache_error = -1, boost::int64_t get_filesize(int i, file_storage const& fs
not_in_cache = -2, , std::string const& save_path, error_code& ec);
no_exist = -3
};
// returns the size of the file or one
// of the enums, noent or not_in_cache
boost::int64_t get_filesize(int i) const;
time_t get_filetime(int i) const;
void set_cache(int i, boost::int64_t size, time_t time);
void set_noexist(int i);
void set_error(int i);
void set_dirty(int i); void set_dirty(int i);
void clear(); void clear();
// internal
enum
{
not_in_cache = -1,
file_error = -2 // (first index in m_errors)
};
// internal
void set_cache(int i, boost::int64_t size);
void set_error(int i, error_code const& ec);
private: private:
// returns the index to the specified error. Either an existing one or a
// newly added entry
int add_error(error_code const& ec);
struct stat_cache_t struct stat_cache_t
{ {
stat_cache_t(boost::int64_t s, time_t t = 0): file_size(s), file_time(t) {} stat_cache_t(boost::int64_t s): file_size(s) {}
// the size of the file. Negative values have special meaning. -1 means
// not-in-cache (i.e. there's no data for this file in the cache).
// lower values (larger negative values) indicate that an error
// occurred while stat()ing the file. The positive value is an index
// into m_errors, that recorded the actual error.
boost::int64_t file_size; boost::int64_t file_size;
time_t file_time;
}; };
// one entry per file
std::vector<stat_cache_t> m_stat_cache; std::vector<stat_cache_t> m_stat_cache;
// These are the errors that have happened when stating files. Each entry
// that had an error, refers to an index into this vector.
std::vector<error_code> m_errors;
}; };
} }

View File

@ -113,7 +113,6 @@ POSSIBILITY OF SUCH DAMAGE.
// virtual bool verify_resume_data(bdecode_node const& rd // virtual bool verify_resume_data(bdecode_node const& rd
// , std::vector<std::string> const* links // , std::vector<std::string> const* links
// , storage_error& error) { return false; } // , storage_error& error) { return false; }
// virtual bool write_resume_data(entry& rd) const { return false; }
// virtual boost::int64_t physical_offset(int piece, int offset) // virtual boost::int64_t physical_offset(int piece, int offset)
// { return piece * m_files.piece_length() + offset; }; // { return piece * m_files.piece_length() + offset; };
// virtual sha1_hash hash_for_slot(int piece, partial_hash& ph, int piece_size) // virtual sha1_hash hash_for_slot(int piece, partial_hash& ph, int piece_size)
@ -314,16 +313,6 @@ namespace libtorrent
, std::vector<std::string> const* links , std::vector<std::string> const* links
, storage_error& ec) = 0; , storage_error& ec) = 0;
// This function should fill in resume data, the current state of the
// storage, in ``rd``. The default storage adds file timestamps and
// sizes.
//
// Returning ``true`` indicates an error occurred.
//
// If an error occurs, ``storage_error`` should be set to reflect it.
//
virtual void write_resume_data(entry& rd, storage_error& ec) const = 0;
// This function should release all the file handles that it keeps open // This function should release all the file handles that it keeps open
// to files belonging to this storage. The default implementation just // to files belonging to this storage. The default implementation just
// calls file_pool::release_files(). // calls file_pool::release_files().
@ -433,7 +422,6 @@ namespace libtorrent
virtual bool verify_resume_data(bdecode_node const& rd virtual bool verify_resume_data(bdecode_node const& rd
, std::vector<std::string> const* links , std::vector<std::string> const* links
, storage_error& error) TORRENT_OVERRIDE; , storage_error& error) TORRENT_OVERRIDE;
virtual void write_resume_data(entry& rd, storage_error& ec) const TORRENT_OVERRIDE;
virtual bool tick() TORRENT_OVERRIDE; virtual bool tick() TORRENT_OVERRIDE;
int readv(file::iovec_t const* bufs, int num_bufs int readv(file::iovec_t const* bufs, int num_bufs
@ -518,7 +506,6 @@ namespace libtorrent
virtual bool verify_resume_data(bdecode_node const& virtual bool verify_resume_data(bdecode_node const&
, std::vector<std::string> const* , std::vector<std::string> const*
, storage_error&) TORRENT_OVERRIDE { return false; } , storage_error&) TORRENT_OVERRIDE { return false; }
virtual void write_resume_data(entry&, storage_error&) const TORRENT_OVERRIDE {}
}; };
// this storage implementation always reads zeroes, and always discards // this storage implementation always reads zeroes, and always discards
@ -541,7 +528,6 @@ namespace libtorrent
, std::vector<std::string> const* /* links */ , std::vector<std::string> const* /* links */
, storage_error&) TORRENT_OVERRIDE , storage_error&) TORRENT_OVERRIDE
{ return false; } { return false; }
virtual void write_resume_data(entry&, storage_error&) const TORRENT_OVERRIDE {}
virtual void release_files(storage_error&) TORRENT_OVERRIDE {} virtual void release_files(storage_error&) TORRENT_OVERRIDE {}
virtual void rename_file(int /* index */ virtual void rename_file(int /* index */
, std::string const& /* new_filenamem */, storage_error&) TORRENT_OVERRIDE {} , std::string const& /* new_filenamem */, storage_error&) TORRENT_OVERRIDE {}
@ -661,8 +647,6 @@ namespace libtorrent
storage_interface* get_storage_impl() { return m_storage.get(); } storage_interface* get_storage_impl() { return m_storage.get(); }
void write_resume_data(entry& rd, storage_error& ec) const;
#ifdef TORRENT_DEBUG #ifdef TORRENT_DEBUG
void assert_torrent_refcount() const; void assert_torrent_refcount() const;
#endif #endif

View File

@ -480,7 +480,6 @@ namespace libtorrent
bool is_torrent_paused() const { return !m_allow_peers || m_graceful_pause_mode; } bool is_torrent_paused() const { return !m_allow_peers || m_graceful_pause_mode; }
void force_recheck(); void force_recheck();
void save_resume_data(int flags); void save_resume_data(int flags);
bool do_async_save_resume_data();
bool need_save_resume_data() const bool need_save_resume_data() const
{ {
@ -1138,7 +1137,6 @@ namespace libtorrent
void on_files_deleted(disk_io_job const* j); void on_files_deleted(disk_io_job const* j);
void on_torrent_paused(disk_io_job const* j); void on_torrent_paused(disk_io_job const* j);
void on_storage_moved(disk_io_job const* j); void on_storage_moved(disk_io_job const* j);
void on_save_resume_data(disk_io_job const* j);
void on_file_renamed(disk_io_job const* j); void on_file_renamed(disk_io_job const* j);
void on_cache_flushed(disk_io_job const* j); void on_cache_flushed(disk_io_job const* j);

View File

@ -219,6 +219,7 @@ namespace libtorrent
void rename_file(int index, std::string const& new_filename) void rename_file(int index, std::string const& new_filename)
{ {
TORRENT_ASSERT(is_loaded()); TORRENT_ASSERT(is_loaded());
if (m_files.file_name(index) == new_filename) return;
copy_on_write(); copy_on_write();
m_files.rename_file(index, new_filename); m_files.rename_file(index, new_filename);
} }

View File

@ -44,18 +44,11 @@ namespace libtorrent
alert_manager::alert_manager(int queue_limit, boost::uint32_t alert_mask) alert_manager::alert_manager(int queue_limit, boost::uint32_t alert_mask)
: m_alert_mask(alert_mask) : m_alert_mask(alert_mask)
, m_queue_size_limit(queue_limit) , m_queue_size_limit(queue_limit)
, m_num_queued_resume(0)
, m_generation(0) , m_generation(0)
{} {}
alert_manager::~alert_manager() {} alert_manager::~alert_manager() {}
int alert_manager::num_queued_resume() const
{
mutex::scoped_lock lock(m_mutex);
return m_num_queued_resume;
}
alert* alert_manager::wait_for_alert(time_duration max_wait) alert* alert_manager::wait_for_alert(time_duration max_wait)
{ {
mutex::scoped_lock lock(m_mutex); mutex::scoped_lock lock(m_mutex);
@ -73,10 +66,6 @@ namespace libtorrent
void alert_manager::maybe_notify(alert* a, mutex::scoped_lock& lock) void alert_manager::maybe_notify(alert* a, mutex::scoped_lock& lock)
{ {
if (a->type() == save_resume_data_failed_alert::alert_type
|| a->type() == save_resume_data_alert::alert_type)
++m_num_queued_resume;
if (m_alerts[m_generation].size() == 1) if (m_alerts[m_generation].size() == 1)
{ {
lock.unlock(); lock.unlock();
@ -102,6 +91,8 @@ namespace libtorrent
{ {
(*i)->on_alert(a); (*i)->on_alert(a);
} }
#else
TORRENT_UNUSED(a);
#endif #endif
} }
@ -168,17 +159,14 @@ namespace libtorrent
} }
#endif #endif
void alert_manager::get_all(std::vector<alert*>& alerts, int& num_resume) void alert_manager::get_all(std::vector<alert*>& alerts)
{ {
mutex::scoped_lock lock(m_mutex); mutex::scoped_lock lock(m_mutex);
TORRENT_ASSERT(m_num_queued_resume <= m_alerts[m_generation].size());
alerts.clear(); alerts.clear();
if (m_alerts[m_generation].empty()) return; if (m_alerts[m_generation].empty()) return;
m_alerts[m_generation].get_pointers(alerts); m_alerts[m_generation].get_pointers(alerts);
num_resume = m_num_queued_resume;
m_num_queued_resume = 0;
// swap buffers // swap buffers
m_generation = (m_generation + 1) & 1; m_generation = (m_generation + 1) & 1;

View File

@ -214,7 +214,6 @@ const char* const job_action_name[] =
"release_files", "release_files",
"delete_files", "delete_files",
"check_fastresume", "check_fastresume",
"save_resume_data",
"rename_file", "rename_file",
"stop_torrent", "stop_torrent",
"cache_piece", "cache_piece",

View File

@ -52,7 +52,6 @@ namespace libtorrent
TORRENT_ASSERT(m_ref.storage == 0 || m_ref.block >= 0); TORRENT_ASSERT(m_ref.storage == 0 || m_ref.block >= 0);
TORRENT_ASSERT(m_ref.storage == 0 || m_ref.piece < static_cast<piece_manager*>(m_ref.storage)->files()->num_pieces()); TORRENT_ASSERT(m_ref.storage == 0 || m_ref.piece < static_cast<piece_manager*>(m_ref.storage)->files()->num_pieces());
TORRENT_ASSERT(m_ref.storage == 0 || m_ref.block <= static_cast<piece_manager*>(m_ref.storage)->files()->piece_length() / 0x4000); TORRENT_ASSERT(m_ref.storage == 0 || m_ref.block <= static_cast<piece_manager*>(m_ref.storage)->files()->piece_length() / 0x4000);
TORRENT_ASSERT(j.action != disk_io_job::save_resume_data);
TORRENT_ASSERT(j.action != disk_io_job::rename_file); TORRENT_ASSERT(j.action != disk_io_job::rename_file);
TORRENT_ASSERT(j.action != disk_io_job::move_storage); TORRENT_ASSERT(j.action != disk_io_job::move_storage);
} }
@ -69,7 +68,6 @@ namespace libtorrent
TORRENT_ASSERT(m_ref.block >= 0); TORRENT_ASSERT(m_ref.block >= 0);
TORRENT_ASSERT(m_ref.piece < static_cast<piece_manager*>(m_ref.storage)->files()->num_pieces()); TORRENT_ASSERT(m_ref.piece < static_cast<piece_manager*>(m_ref.storage)->files()->num_pieces());
TORRENT_ASSERT(m_ref.block <= static_cast<piece_manager*>(m_ref.storage)->files()->piece_length() / 0x4000); TORRENT_ASSERT(m_ref.block <= static_cast<piece_manager*>(m_ref.storage)->files()->piece_length() / 0x4000);
TORRENT_ASSERT(j.action != disk_io_job::save_resume_data);
TORRENT_ASSERT(j.action != disk_io_job::rename_file); TORRENT_ASSERT(j.action != disk_io_job::rename_file);
TORRENT_ASSERT(j.action != disk_io_job::move_storage); TORRENT_ASSERT(j.action != disk_io_job::move_storage);
} }

View File

@ -62,8 +62,6 @@ namespace libtorrent
{ {
if (action == rename_file || action == move_storage) if (action == rename_file || action == move_storage)
free(buffer.string); free(buffer.string);
else if (action == save_resume_data)
delete static_cast<entry*>(buffer.resume_data);
} }
bool disk_io_job::completed(cached_piece_entry const* pe, int block_size) bool disk_io_job::completed(cached_piece_entry const* pe, int block_size)

View File

@ -1038,7 +1038,6 @@ namespace libtorrent
&disk_io_thread::do_release_files, &disk_io_thread::do_release_files,
&disk_io_thread::do_delete_files, &disk_io_thread::do_delete_files,
&disk_io_thread::do_check_fastresume, &disk_io_thread::do_check_fastresume,
&disk_io_thread::do_save_resume_data,
&disk_io_thread::do_rename_file, &disk_io_thread::do_rename_file,
&disk_io_thread::do_stop_torrent, &disk_io_thread::do_stop_torrent,
&disk_io_thread::do_cache_piece, &disk_io_thread::do_cache_piece,
@ -1904,23 +1903,6 @@ namespace libtorrent
add_fence_job(storage, j); add_fence_job(storage, j);
} }
void disk_io_thread::async_save_resume_data(piece_manager* storage
, boost::function<void(disk_io_job const*)> const& handler)
{
#ifdef TORRENT_DEBUG
// the caller must increment the torrent refcount before
// issuing an async disk request
storage->assert_torrent_refcount();
#endif
disk_io_job* j = allocate_job(disk_io_job::save_resume_data);
j->storage = storage->shared_from_this();
j->buffer.resume_data = NULL;
j->callback = handler;
add_fence_job(storage, j);
}
void disk_io_thread::async_rename_file(piece_manager* storage, int index, std::string const& name void disk_io_thread::async_rename_file(piece_manager* storage, int index, std::string const& name
, boost::function<void(disk_io_job const*)> const& handler) , boost::function<void(disk_io_job const*)> const& handler)
{ {
@ -2601,22 +2583,6 @@ namespace libtorrent
return j->storage->check_fastresume(*rd, links.get(), j->error); return j->storage->check_fastresume(*rd, links.get(), j->error);
} }
int disk_io_thread::do_save_resume_data(disk_io_job* j, jobqueue_t& completed_jobs)
{
// if this assert fails, something's wrong with the fence logic
TORRENT_ASSERT(j->storage->num_outstanding_jobs() == 1);
mutex::scoped_lock l(m_cache_mutex);
flush_cache(j->storage.get(), flush_write_cache, completed_jobs, l);
l.unlock();
entry* resume_data = new entry(entry::dictionary_t);
j->storage->get_storage_impl()->write_resume_data(*resume_data, j->error);
TORRENT_ASSERT(j->buffer.resume_data == 0);
j->buffer.resume_data = resume_data;
return j->error ? -1 : 0;
}
int disk_io_thread::do_rename_file(disk_io_job* j, jobqueue_t& /* completed_jobs */ ) int disk_io_thread::do_rename_file(disk_io_job* j, jobqueue_t& /* completed_jobs */ )
{ {
// if this assert fails, something's wrong with the fence logic // if this assert fails, something's wrong with the fence logic

View File

@ -379,7 +379,6 @@ namespace aux {
, *this , *this
#endif #endif
) )
, m_num_save_resume(0)
, m_work(io_service::work(m_io_service)) , m_work(io_service::work(m_io_service))
, m_max_queue_pos(-1) , m_max_queue_pos(-1)
, m_key(0) , m_key(0)
@ -642,58 +641,6 @@ namespace aux {
m_host_resolver.async_resolve(host, flags, h); m_host_resolver.async_resolve(host, flags, h);
} }
void session_impl::queue_async_resume_data(boost::shared_ptr<torrent> const& t)
{
INVARIANT_CHECK;
int loaded_limit = m_settings.get_int(settings_pack::active_loaded_limit);
if (m_num_save_resume + m_alerts.num_queued_resume() >= loaded_limit
&& m_user_load_torrent
&& loaded_limit > 0)
{
TORRENT_ASSERT(t);
// do loaded torrents first, otherwise they'll just be
// evicted and have to be loaded again
if (t->is_loaded())
m_save_resume_queue.push_front(t);
else
m_save_resume_queue.push_back(t);
return;
}
if (t->do_async_save_resume_data())
++m_num_save_resume;
}
// this is called whenever a save_resume_data comes back
// from the disk thread
void session_impl::done_async_resume()
{
TORRENT_ASSERT(m_num_save_resume > 0);
--m_num_save_resume;
}
// this is called when one or all save resume alerts are
// popped off the alert queue
void session_impl::async_resume_dispatched()
{
INVARIANT_CHECK;
int num_queued_resume = m_alerts.num_queued_resume();
int loaded_limit = m_settings.get_int(settings_pack::active_loaded_limit);
while (!m_save_resume_queue.empty()
&& (m_num_save_resume + num_queued_resume < loaded_limit
|| loaded_limit == 0))
{
boost::shared_ptr<torrent> t = m_save_resume_queue.front();
m_save_resume_queue.erase(m_save_resume_queue.begin());
if (t->do_async_save_resume_data())
++m_num_save_resume;
}
}
void session_impl::save_state(entry* eptr, boost::uint32_t flags) const void session_impl::save_state(entry* eptr, boost::uint32_t flags) const
{ {
TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(is_single_thread());
@ -6452,15 +6399,7 @@ retry:
void session_impl::pop_alerts(std::vector<alert*>* alerts) void session_impl::pop_alerts(std::vector<alert*>* alerts)
{ {
int num_resume = 0; m_alerts.get_all(*alerts);
m_alerts.get_all(*alerts, num_resume);
if (num_resume > 0)
{
// we can only issue more resume data jobs from
// the network thread
m_io_service.post(boost::bind(&session_impl::async_resume_dispatched
, this));
}
} }
#ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_NO_DEPRECATE
@ -6892,11 +6831,6 @@ retry:
{ {
TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(is_single_thread());
int loaded_limit = m_settings.get_int(settings_pack::active_loaded_limit);
TORRENT_ASSERT(m_num_save_resume <= loaded_limit);
// if (m_num_save_resume < loaded_limit)
// TORRENT_ASSERT(m_save_resume_queue.empty());
TORRENT_ASSERT(m_torrents.size() >= m_torrent_lru.size()); TORRENT_ASSERT(m_torrents.size() >= m_torrent_lru.size());
if (m_settings.get_int(settings_pack::unchoke_slots_limit) < 0 if (m_settings.get_int(settings_pack::unchoke_slots_limit) < 0

View File

@ -32,19 +32,34 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/stat_cache.hpp" #include "libtorrent/stat_cache.hpp"
#include "libtorrent/assert.hpp" #include "libtorrent/assert.hpp"
#include "libtorrent/error_code.hpp"
#include "libtorrent/file.hpp"
#include <string>
namespace libtorrent namespace libtorrent
{ {
class file_storage;
stat_cache::stat_cache() {} stat_cache::stat_cache() {}
stat_cache::~stat_cache() {} stat_cache::~stat_cache() {}
void stat_cache::set_cache(int i, boost::int64_t size, time_t time) void stat_cache::set_cache(int i, boost::int64_t size)
{ {
TORRENT_ASSERT(i >= 0); TORRENT_ASSERT(i >= 0);
if (i >= int(m_stat_cache.size())) if (i >= int(m_stat_cache.size()))
m_stat_cache.resize(i + 1, not_in_cache); m_stat_cache.resize(i + 1, not_in_cache);
m_stat_cache[i].file_size = size; m_stat_cache[i].file_size = size;
m_stat_cache[i].file_time = time; }
void stat_cache::set_error(int i, error_code const& ec)
{
TORRENT_ASSERT(i >= 0);
if (i >= int(m_stat_cache.size()))
m_stat_cache.resize(i + 1, not_in_cache);
int error_index = add_error(ec);
m_stat_cache[i].file_size = file_error - error_index;
} }
void stat_cache::set_dirty(int i) void stat_cache::set_dirty(int i)
@ -54,36 +69,38 @@ namespace libtorrent
m_stat_cache[i].file_size = not_in_cache; m_stat_cache[i].file_size = not_in_cache;
} }
void stat_cache::set_noexist(int i) boost::int64_t stat_cache::get_filesize(int i, file_storage const& fs
, std::string const& save_path, error_code& ec)
{ {
TORRENT_ASSERT(i >= 0); TORRENT_ASSERT(i < int(fs.num_files()));
if (i >= int(m_stat_cache.size())) if (i >= int(m_stat_cache.size())) m_stat_cache.resize(i + 1, not_in_cache);
m_stat_cache.resize(i + 1, not_in_cache); boost::int64_t sz = m_stat_cache[i].file_size;
m_stat_cache[i].file_size = no_exist; if (sz < not_in_cache)
{
ec = m_errors[-sz + file_error];
return file_error;
}
else if (sz == not_in_cache)
{
// query the filesystem
file_status s;
std::string file_path = fs.file_path(i, save_path);
stat_file(file_path, &s, ec);
if (ec)
{
set_error(i, ec);
sz = file_error;
}
else
{
set_cache(i, s.file_size);
sz = s.file_size;
}
}
return sz;
} }
void stat_cache::set_error(int i) void stat_cache::reserve(int num_files)
{
TORRENT_ASSERT(i >= 0);
if (i >= int(m_stat_cache.size()))
m_stat_cache.resize(i + 1, not_in_cache);
m_stat_cache[i].file_size = cache_error;
}
boost::int64_t stat_cache::get_filesize(int i) const
{
if (i >= int(m_stat_cache.size())) return not_in_cache;
return m_stat_cache[i].file_size;
}
time_t stat_cache::get_filetime(int i) const
{
if (i >= int(m_stat_cache.size())) return not_in_cache;
if (m_stat_cache[i].file_size < 0) return m_stat_cache[i].file_size;
return m_stat_cache[i].file_time;
}
void stat_cache::init(int num_files)
{ {
m_stat_cache.resize(num_files, not_in_cache); m_stat_cache.resize(num_files, not_in_cache);
} }
@ -91,7 +108,15 @@ namespace libtorrent
void stat_cache::clear() void stat_cache::clear()
{ {
std::vector<stat_cache_t>().swap(m_stat_cache); std::vector<stat_cache_t>().swap(m_stat_cache);
std::vector<error_code>().swap(m_errors);
} }
int stat_cache::add_error(error_code const& ec)
{
std::vector<error_code>::iterator i = std::find(m_errors.begin(), m_errors.end(), ec);
if (i != m_errors.end()) return i - m_errors.begin();
m_errors.push_back(ec);
return m_errors.size() - 1;
}
} }

View File

@ -509,7 +509,7 @@ namespace libtorrent
void default_storage::initialize(storage_error& ec) void default_storage::initialize(storage_error& ec)
{ {
m_stat_cache.init(files().num_files()); m_stat_cache.reserve(files().num_files());
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS
// don't do full file allocations on network drives // don't do full file allocations on network drives
@ -540,25 +540,22 @@ namespace libtorrent
// ignore pad files // ignore pad files
if (files().pad_file_at(file_index)) continue; if (files().pad_file_at(file_index)) continue;
if (m_stat_cache.get_filesize(file_index) == stat_cache::not_in_cache) error_code err;
boost::int64_t size = m_stat_cache.get_filesize(file_index, files()
, m_save_path, err);
if (err && err != boost::system::errc::no_such_file_or_directory)
{ {
file_status s; ec.file = file_index;
std::string file_path = files().file_path(file_index, m_save_path); ec.operation = storage_error::stat;
stat_file(file_path, &s, ec.ec); ec.ec = err;
if (ec && ec.ec != boost::system::errc::no_such_file_or_directory) break;
{
m_stat_cache.set_error(file_index);
ec.file = file_index;
ec.operation = storage_error::stat;
break;
}
m_stat_cache.set_cache(file_index, s.file_size, s.mtime);
} }
// if the file already exists, but is larger than what // if the file already exists, but is larger than what
// it's supposed to be, truncate it // it's supposed to be, truncate it
// if the file is empty, just create it either way. // if the file is empty, just create it either way.
if ((!ec && m_stat_cache.get_filesize(file_index) > files().file_size(file_index)) if ((!err && size > files().file_size(file_index))
|| files().file_size(file_index) == 0) || files().file_size(file_index) == 0)
{ {
std::string file_path = files().file_path(file_index, m_save_path); std::string file_path = files().file_path(file_index, m_save_path);
@ -579,9 +576,14 @@ namespace libtorrent
ec.ec.clear(); ec.ec.clear();
file_handle f = open_file(file_index, file::read_write file_handle f = open_file(file_index, file::read_write
| file::random_access, ec); | file::random_access, ec);
if (ec) return; if (ec)
{
ec.file = file_index;
ec.operation = storage_error::fallocate;
return;
}
boost::int64_t size = files().file_size(file_index); size = files().file_size(file_index);
f->set_size(size, ec.ec); f->set_size(size, ec.ec);
if (ec) if (ec)
{ {
@ -589,8 +591,6 @@ namespace libtorrent
ec.operation = storage_error::fallocate; ec.operation = storage_error::fallocate;
break; break;
} }
size_t mtime = m_stat_cache.get_filetime(file_index);
m_stat_cache.set_cache(file_index, size, mtime);
} }
ec.ec.clear(); ec.ec.clear();
} }
@ -609,48 +609,37 @@ namespace libtorrent
bool default_storage::has_any_file(storage_error& ec) bool default_storage::has_any_file(storage_error& ec)
{ {
m_stat_cache.init(files().num_files()); m_stat_cache.reserve(files().num_files());
std::string file_path; std::string file_path;
for (int i = 0; i < files().num_files(); ++i) for (int i = 0; i < files().num_files(); ++i)
{ {
file_status s; boost::int64_t sz = m_stat_cache.get_filesize(
boost::int64_t cache_status = m_stat_cache.get_filesize(i); i, files(), m_save_path, ec.ec);
if (cache_status < 0 && cache_status != stat_cache::no_exist)
if (sz < 0)
{ {
file_path = files().file_path(i, m_save_path); if (ec && ec.ec != boost::system::errc::no_such_file_or_directory)
stat_file(file_path, &s, ec.ec);
boost::int64_t r = s.file_size;
if (ec.ec || !(s.mode & file_status::regular_file)) r = -1;
if (ec && ec.ec == boost::system::errc::no_such_file_or_directory)
{
ec.ec.clear();
r = -3;
}
m_stat_cache.set_cache(i, r, s.mtime);
if (ec)
{ {
ec.file = i; ec.file = i;
ec.operation = storage_error::stat; ec.operation = storage_error::stat;
m_stat_cache.clear(); m_stat_cache.clear();
return false; return false;
} }
// some files not existing is expected and not an error
ec.ec.clear();
} }
// if we didn't find the file, check the next one if (sz > 0) return true;
if (m_stat_cache.get_filesize(i) == stat_cache::no_exist) continue;
if (m_stat_cache.get_filesize(i) > 0)
return true;
} }
file_status s; file_status s;
stat_file(combine_path(m_save_path, m_part_file_name), &s, ec.ec); stat_file(combine_path(m_save_path, m_part_file_name), &s, ec.ec);
if (!ec) return true; if (!ec) return true;
// the part file not existing is expected
if (ec && ec.ec == boost::system::errc::no_such_file_or_directory) if (ec && ec.ec == boost::system::errc::no_such_file_or_directory)
ec.ec.clear(); ec.ec.clear();
if (ec) if (ec)
{ {
ec.file = -1; ec.file = -1;
@ -831,244 +820,12 @@ namespace libtorrent
#endif #endif
} }
void default_storage::write_resume_data(entry& rd, storage_error& ec) const
{
TORRENT_ASSERT(rd.type() == entry::dictionary_t);
entry::list_type& fl = rd["file sizes"].list();
if (m_part_file)
{
error_code ignore;
const_cast<part_file&>(*m_part_file).flush_metadata(ignore);
}
file_storage const& fs = files();
for (int i = 0; i < fs.num_files(); ++i)
{
boost::int64_t file_size = 0;
time_t file_time = 0;
boost::int64_t cache_state = m_stat_cache.get_filesize(i);
if (cache_state != stat_cache::not_in_cache)
{
if (cache_state >= 0)
{
file_size = cache_state;
file_time = m_stat_cache.get_filetime(i);
}
}
else
{
file_status s;
error_code error;
stat_file(fs.file_path(i, m_save_path), &s, error);
if (!error)
{
file_size = s.file_size;
file_time = s.mtime;
}
else if (error == error_code(boost::system::errc::no_such_file_or_directory
, generic_category()))
{
m_stat_cache.set_noexist(i);
}
else
{
ec.ec = error;
ec.file = i;
ec.operation = storage_error::stat;
m_stat_cache.set_error(i);
}
}
fl.push_back(entry(entry::list_t));
entry::list_type& p = fl.back().list();
p.push_back(entry(file_size));
p.push_back(entry(file_time));
}
}
bool default_storage::verify_resume_data(bdecode_node const& rd bool default_storage::verify_resume_data(bdecode_node const& rd
, std::vector<std::string> const* links , std::vector<std::string> const* links
, storage_error& ec) , storage_error& ec)
{ {
// TODO: make this more generic to not just work if files have been
// renamed, but also if they have been merged into a single file for instance
// maybe use the same format as .torrent files and reuse some code from torrent_info
bdecode_node mapped_files = rd.dict_find_list("mapped_files");
if (mapped_files && mapped_files.list_size() == m_files.num_files())
{
m_mapped_files.reset(new file_storage(m_files));
for (int i = 0; i < m_files.num_files(); ++i)
{
std::string new_filename = mapped_files.list_string_value_at(i);
if (new_filename.empty()) continue;
m_mapped_files->rename_file(i, new_filename);
}
}
bdecode_node file_priority = rd.dict_find_list("file_priority");
if (file_priority && file_priority.list_size()
== files().num_files())
{
m_file_priority.resize(file_priority.list_size());
for (int i = 0; i < file_priority.list_size(); ++i)
m_file_priority[i] = boost::uint8_t(file_priority.list_int_value_at(i, 1));
}
bdecode_node file_sizes_ent = rd.dict_find_list("file sizes");
if (file_sizes_ent == 0)
{
ec.ec = errors::missing_file_sizes;
ec.file = -1;
ec.operation = storage_error::check_resume;
return false;
}
if (file_sizes_ent.list_size() == 0)
{
ec.ec = errors::no_files_in_resume_data;
return false;
}
file_storage const& fs = files(); file_storage const& fs = files();
if (file_sizes_ent.list_size() != fs.num_files())
{
ec.ec = errors::mismatching_number_of_files;
ec.file = -1;
ec.operation = storage_error::check_resume;
return false;
}
bool seed = false;
bdecode_node slots = rd.dict_find_list("slots");
if (slots)
{
if (int(slots.list_size()) == m_files.num_pieces())
{
seed = true;
for (int i = 0; i < slots.list_size(); ++i)
{
if (slots.list_int_value_at(i, -1) >= 0) continue;
seed = false;
break;
}
}
}
else if (bdecode_node pieces = rd.dict_find_string("pieces"))
{
if (int(pieces.string_length()) == m_files.num_pieces())
{
seed = true;
char const* p = pieces.string_ptr();
for (int i = 0; i < pieces.string_length(); ++i)
{
if ((p[i] & 1) == 1) continue;
seed = false;
break;
}
}
}
else
{
ec.ec = errors::missing_pieces;
ec.file = -1;
ec.operation = storage_error::check_resume;
return false;
}
for (int i = 0; i < file_sizes_ent.list_size(); ++i)
{
if (fs.pad_file_at(i)) continue;
bdecode_node e = file_sizes_ent.list_at(i);
if (e.type() != bdecode_node::list_t
|| e.list_size() < 2
|| e.list_at(0).type() != bdecode_node::int_t
|| e.list_at(1).type() != bdecode_node::int_t)
{
ec.ec = errors::missing_file_sizes;
ec.file = i;
ec.operation = storage_error::check_resume;
return false;
}
boost::int64_t expected_size = e.list_int_value_at(0);
time_t expected_time = e.list_int_value_at(1);
// if we're a seed, the expected size should match
// the actual full size according to the torrent
if (seed && expected_size < fs.file_size(i))
{
ec.ec = errors::mismatching_file_size;
ec.file = i;
ec.operation = storage_error::check_resume;
return false;
}
boost::int64_t file_size = m_stat_cache.get_filesize(i);
time_t file_time;
if (file_size >= 0)
{
file_time = m_stat_cache.get_filetime(i);
}
else
{
file_status s;
error_code error;
std::string file_path = fs.file_path(i, m_save_path);
stat_file(file_path, &s, error);
if (error)
{
if (error != boost::system::errc::no_such_file_or_directory)
{
m_stat_cache.set_error(i);
ec.ec = error;
ec.file = i;
ec.operation = storage_error::stat;
return false;
}
m_stat_cache.set_noexist(i);
if (expected_size != 0)
{
ec.ec = errors::mismatching_file_size;
ec.file = i;
ec.operation = storage_error::none;
return false;
}
file_size = 0;
file_time = 0;
}
else
{
file_size = s.file_size;
file_time = s.mtime;
}
}
if (expected_size > file_size)
{
ec.ec = errors::mismatching_file_size;
ec.file = i;
ec.operation = storage_error::none;
return false;
}
if (settings().get_bool(settings_pack::ignore_resume_timestamps)) continue;
// allow some slack, because of FAT volumes
if (expected_time != 0 &&
(file_time > expected_time + 5 * 60 || file_time < expected_time - 5))
{
ec.ec = errors::mismatching_file_timestamp;
ec.file = i;
ec.operation = storage_error::stat;
return false;
}
}
// TODO: 2 we probably need to do this unconditionally in this function.
// Even if the resume data file appears stale, we need to create these
// hard links, right?
#ifndef TORRENT_DISABLE_MUTABLE_TORRENTS #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
if (links) if (links)
{ {
@ -1104,6 +861,55 @@ namespace libtorrent
} }
#endif // TORRENT_DISABLE_MUTABLE_TORRENTS #endif // TORRENT_DISABLE_MUTABLE_TORRENTS
if (rd && rd.type() == bdecode_node::dict_t)
{
bdecode_node pieces = rd.dict_find_string("pieces");
if (pieces && pieces.type() == bdecode_node::string_t
&& int(pieces.string_length()) == fs.num_pieces())
{
char const* pieces_str = pieces.string_ptr();
// parse have bitmask. Verify that the files we expect to have
// actually do exist
for (int i = 0; i < fs.num_pieces(); ++i)
{
if ((pieces_str[i] & 1) == 0) continue;
std::vector<file_slice> f = fs.map_block(i, 0, 1);
TORRENT_ASSERT(!f.empty());
const int file_index = f[0].file_index;
error_code error;
boost::int64_t size = m_stat_cache.get_filesize(f[0].file_index
, fs, m_save_path, error);
if (size < 0)
{
if (error != boost::system::errc::no_such_file_or_directory)
{
ec.ec = error;
ec.file = i;
ec.operation = storage_error::stat;
return false;
}
else
{
ec.ec = errors::mismatching_file_size;
ec.file = i;
ec.operation = storage_error::stat;
return false;
}
}
// OK, this file existed, good. Now, skip all remaining pieces in
// this file. We're just sanity-checking whether the files exist
// or not.
peer_request pr = fs.map_file(file_index, 0
, fs.file_size(file_index) + 1);
i = (std::max)(i + 1, pr.piece);
}
}
}
return true; return true;
} }
@ -1597,12 +1403,6 @@ namespace libtorrent
} }
#endif #endif
// used in torrent_handle.cpp
void piece_manager::write_resume_data(entry& rd, storage_error& ec) const
{
m_storage->write_resume_data(rd, ec);
}
int piece_manager::check_no_fastresume(storage_error& ec) int piece_manager::check_no_fastresume(storage_error& ec)
{ {
bool has_files = false; bool has_files = false;
@ -1631,13 +1431,14 @@ namespace libtorrent
int piece_manager::check_init_storage(storage_error& ec) int piece_manager::check_init_storage(storage_error& ec)
{ {
storage_error se; storage_error se;
// initialize may clear the error we pass in and it's important to
// preserve the error code in ec, even when initialize() is successful
m_storage->initialize(se); m_storage->initialize(se);
if (se) if (se)
{ {
ec = se; ec = se;
return fatal_disk_error; return fatal_disk_error;
} }
return no_error; return no_error;
} }

View File

@ -5036,35 +5036,6 @@ namespace libtorrent
} }
} }
void torrent::on_save_resume_data(disk_io_job const* j)
{
TORRENT_ASSERT(is_single_thread());
torrent_ref_holder h(this, "save_resume");
dec_refcount("save_resume");
m_ses.done_async_resume();
if (!j->buffer.resume_data)
{
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle(), j->error.ec);
return;
}
if (!need_loaded())
{
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle()
, m_error);
return;
}
m_need_save_resume_data = false;
m_last_saved_resume = m_ses.session_time();
write_resume_data(*j->buffer.resume_data);
alerts().emplace_alert<save_resume_data_alert>(
boost::shared_ptr<entry>(j->buffer.resume_data), get_handle());
const_cast<disk_io_job*>(j)->buffer.resume_data = 0;
state_updated();
}
void torrent::on_file_renamed(disk_io_job const* j) void torrent::on_file_renamed(disk_io_job const* j)
{ {
TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(is_single_thread());
@ -5521,10 +5492,6 @@ namespace libtorrent
// this call is only valid on torrents with metadata // this call is only valid on torrents with metadata
if (!valid_metadata() || is_seed()) return; if (!valid_metadata() || is_seed()) return;
// the vector need to have exactly one element for every file
// in the torrent
TORRENT_ASSERT(int(files.size()) == m_torrent_file->num_files());
int limit = int(files.size()); int limit = int(files.size());
if (valid_metadata() && limit > m_torrent_file->num_files()) if (valid_metadata() && limit > m_torrent_file->num_files())
limit = m_torrent_file->num_files(); limit = m_torrent_file->num_files();
@ -5545,7 +5512,7 @@ namespace libtorrent
m_file_priority[i] = 0; m_file_priority[i] = 0;
} }
// storage may be NULL during shutdown // storage may be NULL during construction and shutdown
if (m_torrent_file->num_pieces() > 0 && m_storage) if (m_torrent_file->num_pieces() > 0 && m_storage)
{ {
inc_refcount("file_priority"); inc_refcount("file_priority");
@ -6916,9 +6883,6 @@ namespace libtorrent
m_ses.insert_uuid_torrent(m_uuid.empty() ? m_url : m_uuid, me); m_ses.insert_uuid_torrent(m_uuid.empty() ? m_url : m_uuid, me);
} }
// TODO: make this more generic to not just work if files have been
// renamed, but also if they have been merged into a single file for instance
// maybe use the same format as .torrent files and reuse some code from torrent_info
// The mapped_files needs to be read both in the network thread // The mapped_files needs to be read both in the network thread
// and in the disk thread, since they both have their own mapped files structures // and in the disk thread, since they both have their own mapped files structures
// which are kept in sync // which are kept in sync
@ -6947,28 +6911,15 @@ namespace libtorrent
{ {
const int num_files = (std::min)(file_priority.list_size() const int num_files = (std::min)(file_priority.list_size()
, m_torrent_file->num_files()); , m_torrent_file->num_files());
m_file_priority.resize(num_files, 4); std::vector<int> file_prio(num_files);
for (int i = 0; i < num_files; ++i) for (int i = 0; i < num_files; ++i)
{ {
m_file_priority[i] = file_priority.list_int_value_at(i, 1); file_prio[i] = file_priority.list_int_value_at(i, 1);
// this is suspicious, leave seed mode // this is suspicious, leave seed mode
if (m_file_priority[i] == 0) m_seed_mode = false; if (file_prio[i] == 0) m_seed_mode = false;
}
// unallocated slots are assumed to be priority 1, so cut off any
// trailing ones
int end_range = num_files - 1;
for (; end_range >= 0; --end_range) if (m_file_priority[end_range] != 1) break;
m_file_priority.resize(end_range + 1, 4);
// initialize pad files to priority 0
file_storage const& fs = m_torrent_file->files();
for (int i = 0; i < (std::min)(fs.num_files(), end_range + 1); ++i)
{
if (!fs.pad_file_at(i)) continue;
m_file_priority[i] = 0;
} }
update_piece_priorities(); prioritize_files(file_prio);
} }
} }
@ -7275,9 +7226,6 @@ namespace libtorrent
} }
// write renamed files // write renamed files
// TODO: 0 make this more generic to not just work if files have been
// renamed, but also if they have been merged into a single file for instance.
// using file_base
if (&m_torrent_file->files() != &m_torrent_file->orig_files() if (&m_torrent_file->files() != &m_torrent_file->orig_files()
&& m_torrent_file->files().num_files() == m_torrent_file->orig_files().num_files()) && m_torrent_file->files().num_files() == m_torrent_file->orig_files().num_files())
{ {
@ -8766,7 +8714,7 @@ namespace libtorrent
TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index >= 0);
TORRENT_ASSERT(index < m_torrent_file->num_files()); TORRENT_ASSERT(index < m_torrent_file->num_files());
// stoage may be NULL during shutdown // storage may be NULL during shutdown
if (!m_storage.get()) if (!m_storage.get())
{ {
if (alerts().should_post<file_rename_failed_alert>()) if (alerts().should_post<file_rename_failed_alert>())
@ -9558,58 +9506,30 @@ namespace libtorrent
m_save_resume_flags = boost::uint8_t(flags); m_save_resume_flags = boost::uint8_t(flags);
state_updated(); state_updated();
if (m_state == torrent_status::checking_files
|| m_state == torrent_status::checking_resume_data)
{
if (!need_loaded())
{
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle()
, m_error);
return;
}
// storage may be NULL during shutdown
if (!m_storage)
{
TORRENT_ASSERT(m_abort);
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle()
, boost::asio::error::operation_aborted);
return;
}
boost::shared_ptr<entry> rd(new entry);
write_resume_data(*rd);
alerts().emplace_alert<save_resume_data_alert>(rd, get_handle());
return;
}
// TODO: 3 this really needs to be moved to do_async_save_resume_data.
// flags need to be passed on
if ((flags & torrent_handle::flush_disk_cache) && m_storage.get())
m_ses.disk_thread().async_release_files(m_storage.get());
m_ses.queue_async_resume_data(shared_from_this());
}
bool torrent::do_async_save_resume_data()
{
if (!need_loaded()) if (!need_loaded())
{ {
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle(), m_error); alerts().emplace_alert<save_resume_data_failed_alert>(get_handle()
return false; , m_error);
return;
} }
/*
// storage may be NULL during shutdown // storage may be NULL during shutdown
if (!m_storage) if (!m_storage)
{ {
TORRENT_ASSERT(m_abort); TORRENT_ASSERT(m_abort);
alerts().emplace_alert<save_resume_data_failed_alert>(get_handle() alerts().emplace_alert<save_resume_data_failed_alert>(get_handle()
, boost::asio::error::operation_aborted); , boost::asio::error::operation_aborted);
return false; return;
} }
inc_refcount("save_resume"); */
m_ses.disk_thread().async_save_resume_data(m_storage.get() if ((flags & torrent_handle::flush_disk_cache) && m_storage.get())
, boost::bind(&torrent::on_save_resume_data, shared_from_this(), _1)); m_ses.disk_thread().async_release_files(m_storage.get());
return true;
state_updated();
boost::shared_ptr<entry> rd(new entry);
write_resume_data(*rd);
alerts().emplace_alert<save_resume_data_alert>(rd, get_handle());
} }
bool torrent::should_check_files() const bool torrent::should_check_files() const

View File

@ -734,19 +734,6 @@ namespace libtorrent
{ {
entry ret(entry::dictionary_t); entry ret(entry::dictionary_t);
TORRENT_SYNC_CALL1(write_resume_data, boost::ref(ret)); TORRENT_SYNC_CALL1(write_resume_data, boost::ref(ret));
t = m_torrent.lock();
if (t)
{
bool done = false;
session_impl& ses = static_cast<session_impl&>(t->session());
storage_error ec;
ses.get_io_service().dispatch(boost::bind(&aux::fun_wrap, boost::ref(done), boost::ref(ses.cond)
, boost::ref(ses.mut), boost::function<void(void)>(boost::bind(
&piece_manager::write_resume_data, &t->storage(), boost::ref(ret), boost::ref(ec)))));
t.reset();
aux::torrent_wait(done, ses);
}
return ret; return ret;
} }

View File

@ -59,8 +59,7 @@ TORRENT_TEST(limit)
TEST_EQUAL(mgr.pending(), true); TEST_EQUAL(mgr.pending(), true);
std::vector<alert*> alerts; std::vector<alert*> alerts;
int num_resume; mgr.get_all(alerts);
mgr.get_all(alerts, num_resume);
// even though we posted 600, the limit was 500 // even though we posted 600, the limit was 500
TEST_EQUAL(alerts.size(), 500); TEST_EQUAL(alerts.size(), 500);
@ -75,7 +74,7 @@ TORRENT_TEST(limit)
TEST_EQUAL(mgr.pending(), true); TEST_EQUAL(mgr.pending(), true);
mgr.get_all(alerts, num_resume); mgr.get_all(alerts);
// even though we posted 600, the limit was 200 // even though we posted 600, the limit was 200
TEST_EQUAL(alerts.size(), 200); TEST_EQUAL(alerts.size(), 200);
@ -96,8 +95,7 @@ TORRENT_TEST(priority_limit)
mgr.emplace_alert<file_rename_failed_alert>(torrent_handle(), i, error_code()); mgr.emplace_alert<file_rename_failed_alert>(torrent_handle(), i, error_code());
std::vector<alert*> alerts; std::vector<alert*> alerts;
int num_resume; mgr.get_all(alerts);
mgr.get_all(alerts, num_resume);
// even though we posted 400, the limit was 100 for half of them and // even though we posted 400, the limit was 100 for half of them and
// 200 for the other half, meaning we should have 200 alerts now // 200 for the other half, meaning we should have 200 alerts now
@ -173,8 +171,7 @@ TORRENT_TEST(notify_function)
// however, if we pop all the alerts and post new ones, there will be // however, if we pop all the alerts and post new ones, there will be
// and edge triggering the notify call // and edge triggering the notify call
std::vector<alert*> alerts; std::vector<alert*> alerts;
int num_resume; mgr.get_all(alerts);
mgr.get_all(alerts, num_resume);
TEST_EQUAL(mgr.pending(), false); TEST_EQUAL(mgr.pending(), false);
@ -258,8 +255,7 @@ TORRENT_TEST(wait_for_alert)
TEST_CHECK(a->type() == torrent_added_alert::alert_type); TEST_CHECK(a->type() == torrent_added_alert::alert_type);
std::vector<alert*> alerts; std::vector<alert*> alerts;
int num_resume = 0; mgr.get_all(alerts);
mgr.get_all(alerts, num_resume);
start = clock_type::now(); start = clock_type::now();
thread posting_thread(boost::bind(&post_torrent_added, &mgr)); thread posting_thread(boost::bind(&post_torrent_added, &mgr));
@ -274,40 +270,6 @@ TORRENT_TEST(wait_for_alert)
posting_thread.join(); posting_thread.join();
} }
TORRENT_TEST(queued_resume)
{
alert_manager mgr(100, 0xffffffff);
TEST_EQUAL(mgr.num_queued_resume(), 0);
for (int i = 0; i < 17; ++i)
mgr.emplace_alert<torrent_added_alert>(torrent_handle());
TEST_EQUAL(mgr.num_queued_resume(), 0);
std::vector<alert*> alerts;
int num_resume = 0;
mgr.get_all(alerts, num_resume);
TEST_EQUAL(num_resume, 0);
TEST_EQUAL(alerts.size(), 17);
TEST_EQUAL(mgr.num_queued_resume(), 0);
error_code ec(boost::system::errc::no_such_file_or_directory
, generic_category());
for (int i = 0; i < 2; ++i)
mgr.emplace_alert<save_resume_data_failed_alert>(torrent_handle(), ec);
TEST_EQUAL(mgr.num_queued_resume(), 2);
mgr.get_all(alerts, num_resume);
TEST_EQUAL(num_resume, 2);
TEST_EQUAL(alerts.size(), 2);
TEST_EQUAL(mgr.num_queued_resume(), 0);
}
TORRENT_TEST(alert_mask) TORRENT_TEST(alert_mask)
{ {
alert_manager mgr(100, 0xffffffff); alert_manager mgr(100, 0xffffffff);

View File

@ -42,39 +42,46 @@ TORRENT_TEST(stat_cache)
stat_cache sc; stat_cache sc;
sc.init(10); file_storage fs;
for (int i = 0; i < 20; ++i)
for (int i = 0; i < 10; ++i)
{ {
TEST_CHECK(sc.get_filesize(i) == stat_cache::not_in_cache); char buf[50];
TEST_CHECK(sc.get_filetime(i) == stat_cache::not_in_cache); snprintf(buf, sizeof(buf), "test_torrent/test-%d", i);
fs.add_file(buf, (i + 1) * 10);
} }
// out of bound accesses count as not-in-cache std::string save_path = ".";
TEST_CHECK(sc.get_filesize(10) == stat_cache::not_in_cache);
TEST_CHECK(sc.get_filesize(11) == stat_cache::not_in_cache);
sc.set_error(3); sc.reserve(10);
TEST_CHECK(sc.get_filesize(3) == stat_cache::cache_error);
sc.set_noexist(3); sc.set_error(3, error_code(boost::system::errc::permission_denied, generic_category()));
TEST_CHECK(sc.get_filesize(3) == stat_cache::no_exist); ec.clear();
TEST_EQUAL(sc.get_filesize(3, fs, save_path, ec), stat_cache::file_error);
TEST_EQUAL(ec, error_code(boost::system::errc::permission_denied, generic_category()));
sc.set_cache(3, 101, 5555); sc.set_error(3, error_code(boost::system::errc::no_such_file_or_directory, generic_category()));
TEST_CHECK(sc.get_filesize(3) == 101); ec.clear();
TEST_CHECK(sc.get_filetime(3) == 5555); TEST_EQUAL(sc.get_filesize(3, fs, save_path, ec), stat_cache::file_error);
TEST_EQUAL(ec, error_code(boost::system::errc::no_such_file_or_directory, generic_category()));
sc.set_error(11); ec.clear();
TEST_CHECK(sc.get_filesize(10) == stat_cache::not_in_cache); sc.set_cache(3, 101);
TEST_CHECK(sc.get_filesize(11) == stat_cache::cache_error); TEST_EQUAL(sc.get_filesize(3, fs, save_path, ec), 101);
TEST_CHECK(!ec);
sc.set_noexist(13); sc.set_error(11, error_code(boost::system::errc::broken_pipe, generic_category()));
TEST_CHECK(sc.get_filesize(12) == stat_cache::not_in_cache); ec.clear();
TEST_CHECK(sc.get_filesize(13) == stat_cache::no_exist); TEST_EQUAL(sc.get_filesize(11, fs, save_path, ec), stat_cache::file_error);
TEST_EQUAL(ec, error_code(boost::system::errc::broken_pipe, generic_category()));
sc.set_cache(15, 1000, 3000); ec.clear();
TEST_CHECK(sc.get_filesize(14) == stat_cache::not_in_cache); sc.set_error(13, error_code(boost::system::errc::no_such_file_or_directory, generic_category()));
TEST_CHECK(sc.get_filesize(15) == 1000); TEST_EQUAL(sc.get_filesize(13, fs, save_path, ec), stat_cache::file_error);
TEST_CHECK(sc.get_filetime(15) == 3000); TEST_EQUAL(ec, error_code(boost::system::errc::no_such_file_or_directory, generic_category()));
ec.clear();
sc.set_cache(15, 1000);
TEST_CHECK(sc.get_filesize(15, fs, save_path, ec) == 1000);
TEST_CHECK(!ec);
} }

View File

@ -692,7 +692,6 @@ TORRENT_TEST(fastresume)
return; return;
std::cerr << resume.to_string() << "\n"; std::cerr << resume.to_string() << "\n";
TEST_CHECK(resume.dict().find("file sizes") != resume.dict().end());
// make sure the fast resume check fails! since we removed the file // make sure the fast resume check fails! since we removed the file
{ {
@ -795,7 +794,6 @@ TORRENT_TEST(rename_file_fastresume)
TEST_CHECK(!exists(combine_path(test_path, "tmp2/temporary"))); TEST_CHECK(!exists(combine_path(test_path, "tmp2/temporary")));
TEST_CHECK(exists(combine_path(test_path, "tmp2/testing_renamed_files"))); TEST_CHECK(exists(combine_path(test_path, "tmp2/testing_renamed_files")));
TEST_CHECK(resume.dict().find("mapped_files") != resume.dict().end()); TEST_CHECK(resume.dict().find("mapped_files") != resume.dict().end());
TEST_CHECK(resume.dict().find("file sizes") != resume.dict().end());
std::cerr << resume.to_string() << "\n"; std::cerr << resume.to_string() << "\n";