removed checker thread

This commit is contained in:
Arvid Norberg 2008-03-08 06:06:31 +00:00
parent 1a0f8d5cd5
commit f53cfa7eeb
12 changed files with 776 additions and 1375 deletions

View File

@ -94,73 +94,6 @@ namespace libtorrent
{ {
struct session_impl; struct session_impl;
// this data is shared between the main thread and the
// thread that initialize pieces
struct piece_checker_data
{
piece_checker_data()
: processing(false), progress(0.f), abort(false) {}
boost::shared_ptr<torrent> torrent_ptr;
fs::path save_path;
sha1_hash info_hash;
void parse_resume_data(
const entry& rd
, const torrent_info& info
, std::string& error);
std::vector<int> piece_map;
std::vector<piece_picker::downloading_piece> unfinished_pieces;
std::vector<piece_picker::block_info> block_info;
std::vector<tcp::endpoint> peers;
std::vector<tcp::endpoint> banned_peers;
entry resume_data;
// this is true if this torrent is being processed (checked)
// if it is not being processed, then it can be removed from
// the queue without problems, otherwise the abort flag has
// to be set.
bool processing;
// is filled in by storage::initialize_pieces()
// and represents the progress. It should be a
// value in the range [0, 1]
float progress;
// abort defaults to false and is typically
// filled in by torrent_handle when the user
// aborts the torrent
bool abort;
};
struct checker_impl: boost::noncopyable
{
checker_impl(session_impl& s): m_ses(s), m_abort(false) {}
void operator()();
piece_checker_data* find_torrent(const sha1_hash& info_hash);
void remove_torrent(sha1_hash const& info_hash, int options);
#ifndef NDEBUG
void check_invariant() const;
#endif
// when the files has been checked
// the torrent is added to the session
session_impl& m_ses;
mutable boost::mutex m_mutex;
boost::condition m_cond;
// a list of all torrents that are currently in queue
// or checking their files
std::deque<boost::shared_ptr<piece_checker_data> > m_torrents;
std::deque<boost::shared_ptr<piece_checker_data> > m_processing;
bool m_abort;
};
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
struct tracker_logger; struct tracker_logger;
#endif #endif
@ -284,6 +217,9 @@ namespace libtorrent
std::vector<torrent_handle> get_torrents(); std::vector<torrent_handle> get_torrents();
void check_torrent(boost::shared_ptr<torrent> const& t);
void done_checking(boost::shared_ptr<torrent> const& t);
void set_severity_level(alert::severity_t s); void set_severity_level(alert::severity_t s);
std::auto_ptr<alert> pop_alert(); std::auto_ptr<alert> pop_alert();
@ -435,6 +371,7 @@ namespace libtorrent
tracker_manager m_tracker_manager; tracker_manager m_tracker_manager;
torrent_map m_torrents; torrent_map m_torrents;
std::list<boost::shared_ptr<torrent> > m_queued_for_checking;
// this maps sockets to their peer_connection // this maps sockets to their peer_connection
// object. It is the complete list of all connected // object. It is the complete list of all connected
@ -625,16 +562,8 @@ namespace libtorrent
extension_list_t m_extensions; extension_list_t m_extensions;
#endif #endif
// data shared between the main thread
// and the checker thread
checker_impl m_checker_impl;
// the main working thread // the main working thread
boost::scoped_ptr<boost::thread> m_thread; boost::scoped_ptr<boost::thread> m_thread;
// the thread that calls initialize_pieces()
// on all torrents before they start downloading
boost::scoped_ptr<boost::thread> m_checker_thread;
}; };
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING

View File

@ -77,6 +77,8 @@ namespace libtorrent
, move_storage , move_storage
, release_files , release_files
, delete_files , delete_files
, check_fastresume
, check_files
}; };
action_t action; action_t action;
@ -154,10 +156,6 @@ namespace libtorrent
, boost::function<void(int, disk_io_job const&)> const& f , boost::function<void(int, disk_io_job const&)> const& f
= boost::function<void(int, disk_io_job const&)>()); = boost::function<void(int, disk_io_job const&)>());
#ifndef NDEBUG
disk_io_job find_job(boost::intrusive_ptr<piece_manager> s
, int action, int piece) const;
#endif
// keep track of the number of bytes in the job queue // keep track of the number of bytes in the job queue
// at any given time. i.e. the sum of all buffer_size. // at any given time. i.e. the sum of all buffer_size.
// this is used to slow down the download global download // this is used to slow down the download global download
@ -268,9 +266,6 @@ namespace libtorrent
// number of bytes per block. The BitTorrent // number of bytes per block. The BitTorrent
// protocol defines the block size to 16 KiB. // protocol defines the block size to 16 KiB.
int m_block_size; int m_block_size;
#ifndef NDEBUG
disk_io_job m_current;
#endif
#ifdef TORRENT_DISK_STATS #ifdef TORRENT_DISK_STATS
std::ofstream m_log; std::ofstream m_log;

View File

@ -138,10 +138,7 @@ namespace libtorrent
// the vector tells which pieces we already have // the vector tells which pieces we already have
// and which we don't have. // and which we don't have.
void files_checked( void init(std::vector<bool> const& pieces);
std::vector<bool> const& pieces
, std::vector<downloading_piece> const& unfinished
, std::vector<int>& verify_pieces);
// increases the peer count for the given piece // increases the peer count for the given piece
// (is used when a HAVE message is received) // (is used when a HAVE message is received)

View File

@ -119,12 +119,12 @@ namespace libtorrent
// if allocate_files is true. // if allocate_files is true.
// allocate_files is true if allocation mode // allocate_files is true if allocation mode
// is set to full and sparse files are supported // is set to full and sparse files are supported
// false return value indicates an error
virtual bool initialize(bool allocate_files) = 0; virtual bool initialize(bool allocate_files) = 0;
// negative return value indicates an error // negative return value indicates an error
virtual size_type read(char* buf, int slot, int offset, int size) = 0; virtual size_type read(char* buf, int slot, int offset, int size) = 0;
// may throw file_error if storage for slot hasn't been allocated
// negative return value indicates an error // negative return value indicates an error
virtual size_type write(const char* buf, int slot, int offset, int size) = 0; virtual size_type write(const char* buf, int slot, int offset, int size) = 0;
@ -194,47 +194,22 @@ namespace libtorrent
, fs::path const& path , fs::path const& path
, file_pool& fp , file_pool& fp
, disk_io_thread& io , disk_io_thread& io
, storage_constructor_type sc); , storage_constructor_type sc
, storage_mode_t sm);
~piece_manager(); ~piece_manager();
torrent_info const* info() const { return m_info.get(); } torrent_info const* info() const { return m_info.get(); }
bool check_fastresume(aux::piece_checker_data& d
, std::vector<bool>& pieces, int& num_pieces, storage_mode_t storage_mode
, std::string& error_msg);
std::pair<bool, float> check_files(std::vector<bool>& pieces
, int& num_pieces, boost::recursive_mutex& mutex, bool& error);
// frees a buffer that was returned from a read operation // frees a buffer that was returned from a read operation
void free_buffer(char* buf); void free_buffer(char* buf);
void write_resume_data(entry& rd) const void write_resume_data(entry& rd, std::vector<bool> const& have) const;
{ m_storage->write_resume_data(rd); }
bool verify_resume_data(entry const& rd, std::string& error) void async_check_fastresume(entry const* resume_data
{ , boost::function<void(int, disk_io_job const&)> const& handler);
#ifndef NDEBUG
m_resume_data_verified = true;
#endif
return m_storage->verify_resume_data(rd, error);
}
bool is_allocating() const void async_check_files(boost::function<void(int, disk_io_job const&)> const& handler);
{ return m_state == state_expand_pieces; }
void mark_failed(int index);
std::string const& error() const { return m_storage->error(); }
void clear_error() { m_storage->clear_error(); }
unsigned long piece_crc(
int slot_index
, int block_size
, piece_picker::block_info const* bi);
int slot_for(int piece) const;
int piece_for(int slot) const;
void async_read( void async_read(
peer_request const& r peer_request const& r
@ -249,8 +224,6 @@ namespace libtorrent
void async_hash(int piece, boost::function<void(int, disk_io_job const&)> const& f); void async_hash(int piece, boost::function<void(int, disk_io_job const&)> const& f);
fs::path save_path() const;
void async_release_files( void async_release_files(
boost::function<void(int, disk_io_job const&)> const& handler boost::function<void(int, disk_io_job const&)> const& handler
= boost::function<void(int, disk_io_job const&)>()); = boost::function<void(int, disk_io_job const&)>());
@ -262,12 +235,44 @@ namespace libtorrent
void async_move_storage(fs::path const& p void async_move_storage(fs::path const& p
, boost::function<void(int, disk_io_job const&)> const& handler); , boost::function<void(int, disk_io_job const&)> const& handler);
// fills the vector that maps all allocated enum return_t
// slots to the piece that is stored (or {
// partially stored) there. -2 is the index // return values from check_fastresume and check_files
// of unassigned pieces and -1 is unallocated no_error = 0,
void export_piece_map(std::vector<int>& pieces need_full_check = -1,
, std::vector<bool> const& have) const; fatal_disk_error = -2,
};
private:
fs::path save_path() const;
bool verify_resume_data(entry const& rd, std::string& error)
{ return m_storage->verify_resume_data(rd, error); }
bool is_allocating() const
{ return m_state == state_expand_pieces; }
void mark_failed(int index);
std::string const& error() const { return m_storage->error(); }
void clear_error() { m_storage->clear_error(); }
int slot_for(int piece) const;
int piece_for(int slot) const;
// helper functions for check_dastresume
int check_no_fastresume(std::string& error);
int check_init_storage(std::string& error);
// if error is set and return value is 'no_error' or 'need_full_check'
// the error message indicates that the fast resume data was rejected
// if 'fatal_disk_error' is returned, the error message indicates what
// when wrong in the disk access
int check_fastresume(entry const& rd, std::string& error);
// this function returns true if the checking is complete
int check_files(int& current_slot, int& have_piece, std::string& error);
bool compact_allocation() const bool compact_allocation() const
{ return m_storage_mode == storage_mode_compact; } { return m_storage_mode == storage_mode_compact; }
@ -276,18 +281,8 @@ namespace libtorrent
std::string name() const { return m_info->name(); } std::string name() const { return m_info->name(); }
#endif #endif
private:
bool allocate_slots(int num_slots, bool abort_on_disk = false); bool allocate_slots(int num_slots, bool abort_on_disk = false);
int identify_data(
const std::vector<char>& piece_data
, int current_slot
, std::vector<bool>& have_pieces
, int& num_pieces
, const std::multimap<sha1_hash, int>& hash_to_piece
, boost::recursive_mutex& mutex);
size_type read_impl( size_type read_impl(
char* buf char* buf
, int piece_index , int piece_index
@ -300,8 +295,10 @@ namespace libtorrent
, int offset , int offset
, int size); , int size);
bool check_one_piece(std::vector<bool>& pieces, int& num_pieces bool check_one_piece(int& have_piece);
, boost::recursive_mutex& mutex); int identify_data(
const std::vector<char>& piece_data
, int current_slot);
void switch_to_full_mode(); void switch_to_full_mode();
sha1_hash hash_for_piece_impl(int piece); sha1_hash hash_for_piece_impl(int piece);
@ -357,8 +354,6 @@ namespace libtorrent
state_none, state_none,
// the file checking is complete // the file checking is complete
state_finished, state_finished,
// creating the directories
state_create_files,
// checking the files // checking the files
state_full_check, state_full_check,
// move pieces to their final position // move pieces to their final position
@ -403,9 +398,6 @@ namespace libtorrent
// the piece_manager destructs. This is because // the piece_manager destructs. This is because
// the torrent_info object is owned by the torrent. // the torrent_info object is owned by the torrent.
boost::shared_ptr<void> m_torrent; boost::shared_ptr<void> m_torrent;
#ifndef NDEBUG
bool m_resume_data_verified;
#endif
}; };
} }

View File

@ -98,20 +98,19 @@ namespace libtorrent
torrent( torrent(
aux::session_impl& ses aux::session_impl& ses
, aux::checker_impl& checker
, boost::intrusive_ptr<torrent_info> tf , boost::intrusive_ptr<torrent_info> tf
, fs::path const& save_path , fs::path const& save_path
, tcp::endpoint const& net_interface , tcp::endpoint const& net_interface
, storage_mode_t m_storage_mode , storage_mode_t m_storage_mode
, int block_size , int block_size
, storage_constructor_type sc , storage_constructor_type sc
, bool paused); , bool paused
, entry const& resume_data);
// used with metadata-less torrents // used with metadata-less torrents
// (the metadata is downloaded from the peers) // (the metadata is downloaded from the peers)
torrent( torrent(
aux::session_impl& ses aux::session_impl& ses
, aux::checker_impl& checker
, char const* tracker_url , char const* tracker_url
, sha1_hash const& info_hash , sha1_hash const& info_hash
, char const* name , char const* name
@ -120,7 +119,8 @@ namespace libtorrent
, storage_mode_t m_storage_mode , storage_mode_t m_storage_mode
, int block_size , int block_size
, storage_constructor_type sc , storage_constructor_type sc
, bool paused); , bool paused
, entry const& resume_data);
~torrent(); ~torrent();
@ -142,6 +142,12 @@ namespace libtorrent
// it will initialize the storage and the piece-picker // it will initialize the storage and the piece-picker
void init(); void init();
void on_resume_data_checked(int ret, disk_io_job const& j);
void on_piece_checked(int ret, disk_io_job const& j);
void files_checked();
void start_checking();
storage_mode_t storage_mode() const { return m_storage_mode; }
// this will flag the torrent as aborted. The main // this will flag the torrent as aborted. The main
// loop in session_impl will check for this state // loop in session_impl will check for this state
// on all torrents once every second, and take // on all torrents once every second, and take
@ -149,19 +155,12 @@ namespace libtorrent
void abort(); void abort();
bool is_aborted() const { return m_abort; } bool is_aborted() const { return m_abort; }
// returns true if this torrent is being allocated
// by the checker thread.
bool is_allocating() const;
session_settings const& settings() const; session_settings const& settings() const;
aux::session_impl& session() { return m_ses; } aux::session_impl& session() { return m_ses; }
void set_sequential_download(bool sd); void set_sequential_download(bool sd);
bool verify_resume_data(entry const& rd, std::string& error)
{ TORRENT_ASSERT(m_storage); return m_storage->verify_resume_data(rd, error); }
void second_tick(stat& accumulator, float tick_interval); void second_tick(stat& accumulator, float tick_interval);
// debug purpose only // debug purpose only
@ -169,11 +168,6 @@ namespace libtorrent
std::string name() const; std::string name() const;
bool check_fastresume(aux::piece_checker_data&);
std::pair<bool, float> check_files(bool& error);
void files_checked(std::vector<piece_picker::downloading_piece> const&
unfinished_pieces);
stat statistics() const { return m_stat; } stat statistics() const { return m_stat; }
size_type bytes_left() const; size_type bytes_left() const;
boost::tuples::tuple<size_type, size_type> bytes_done() const; boost::tuples::tuple<size_type, size_type> bytes_done() const;
@ -705,7 +699,6 @@ namespace libtorrent
// a back reference to the session // a back reference to the session
// this torrent belongs to. // this torrent belongs to.
aux::session_impl& m_ses; aux::session_impl& m_ses;
aux::checker_impl& m_checker;
boost::scoped_ptr<piece_picker> m_picker; boost::scoped_ptr<piece_picker> m_picker;
@ -768,6 +761,12 @@ namespace libtorrent
// determines the storage state for this torrent. // determines the storage state for this torrent.
storage_mode_t m_storage_mode; storage_mode_t m_storage_mode;
// the state of this torrent (queued, checking, downloading)
torrent_status::state_t m_state;
float m_progress;
entry m_resume_data;
// defaults to 16 kiB, but can be set by the user // defaults to 16 kiB, but can be set by the user
// when creating the torrent // when creating the torrent
const int m_default_block_size; const int m_default_block_size;

View File

@ -278,7 +278,7 @@ namespace libtorrent
friend struct aux::session_impl; friend struct aux::session_impl;
friend class torrent; friend class torrent;
torrent_handle(): m_ses(0), m_chk(0), m_info_hash(0) {} torrent_handle(): m_ses(0), m_info_hash(0) {}
void get_peer_info(std::vector<peer_info>& v) const; void get_peer_info(std::vector<peer_info>& v) const;
bool send_chat_message(tcp::endpoint ip, std::string message) const; bool send_chat_message(tcp::endpoint ip, std::string message) const;
@ -418,15 +418,12 @@ namespace libtorrent
private: private:
torrent_handle(aux::session_impl* s, torrent_handle(aux::session_impl* s
aux::checker_impl* c, , const sha1_hash& h)
const sha1_hash& h)
: m_ses(s) : m_ses(s)
, m_chk(c)
, m_info_hash(h) , m_info_hash(h)
{ {
TORRENT_ASSERT(m_ses != 0); TORRENT_ASSERT(m_ses != 0);
TORRENT_ASSERT(m_chk != 0);
} }
#ifndef NDEBUG #ifndef NDEBUG
@ -434,7 +431,6 @@ namespace libtorrent
#endif #endif
aux::session_impl* m_ses; aux::session_impl* m_ses;
aux::checker_impl* m_chk;
sha1_hash m_info_hash; sha1_hash m_info_hash;
}; };

View File

@ -73,31 +73,6 @@ namespace libtorrent
TORRENT_ASSERT(m_abort == true); TORRENT_ASSERT(m_abort == true);
} }
#ifndef NDEBUG
disk_io_job disk_io_thread::find_job(boost::intrusive_ptr<piece_manager> s
, int action, int piece) const
{
mutex_t::scoped_lock l(m_mutex);
for (std::list<disk_io_job>::const_iterator i = m_jobs.begin();
i != m_jobs.end(); ++i)
{
if (i->storage != s)
continue;
if ((i->action == action || action == -1) && i->piece == piece)
return *i;
}
if ((m_current.action == action || action == -1)
&& m_current.piece == piece)
return m_current;
disk_io_job ret;
ret.action = (disk_io_job::action_t)-1;
ret.piece = -1;
return ret;
}
#endif
void disk_io_thread::join() void disk_io_thread::join()
{ {
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
@ -735,19 +710,14 @@ namespace libtorrent
m_log << log_time() << " idle" << std::endl; m_log << log_time() << " idle" << std::endl;
#endif #endif
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
#ifndef NDEBUG
m_current.action = (disk_io_job::action_t)-1;
m_current.piece = -1;
#endif
while (m_jobs.empty() && !m_abort) while (m_jobs.empty() && !m_abort)
m_signal.wait(l); m_signal.wait(l);
if (m_abort && m_jobs.empty()) return; if (m_abort && m_jobs.empty()) return;
boost::function<void(int, disk_io_job const&)> handler; boost::function<void(int, disk_io_job const&)> handler;
handler.swap(m_jobs.front().callback); handler.swap(m_jobs.front().callback);
#ifndef NDEBUG
m_current = m_jobs.front();
#endif
disk_io_job j = m_jobs.front(); disk_io_job j = m_jobs.front();
m_jobs.pop_front(); m_jobs.pop_front();
m_queue_buffer_size -= j.buffer_size; m_queue_buffer_size -= j.buffer_size;
@ -876,8 +846,8 @@ namespace libtorrent
j.storage->clear_error(); j.storage->clear_error();
break; break;
} }
j.str.resize(20); ret = (j.storage->info()->hash_for_piece(j.piece) == h)?0:-1;
std::memcpy(&j.str[0], &h[0], 20); if (ret == -1) j.storage->mark_failed(j.piece);
break; break;
} }
case disk_io_job::move_storage: case disk_io_job::move_storage:
@ -950,6 +920,47 @@ namespace libtorrent
} }
break; break;
} }
case disk_io_job::check_fastresume:
{
#ifdef TORRENT_DISK_STATS
m_log << log_time() << " check fastresume" << std::endl;
#endif
entry const* rd = (entry const*)j.buffer;
TORRENT_ASSERT(rd != 0);
ret = j.storage->check_fastresume(*rd, j.str);
break;
}
case disk_io_job::check_files:
{
#ifdef TORRENT_DISK_STATS
m_log << log_time() << " check files" << std::endl;
#endif
int piece_size = j.storage->info()->piece_length();
for (int processed = 0; processed < 4 * 1024 * 1024; processed += piece_size)
{
ret = j.storage->check_files(j.piece, j.offset, j.str);
#ifndef BOOST_NO_EXCEPTIONS
try {
#endif
TORRENT_ASSERT(handler);
if (handler && ret == piece_manager::need_full_check)
m_ios.post(bind(handler, ret, j));
#ifndef BOOST_NO_EXCEPTIONS
} catch (std::exception&) {}
#endif
if (ret != piece_manager::need_full_check) break;
}
// if the check is not done, add it at the end of the job queue
if (ret == piece_manager::need_full_check)
{
mutex_t::scoped_lock l(m_mutex);
m_jobs.push_back(j);
m_jobs.back().callback.swap(handler);
continue;
}
break;
}
} }
} }
#ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_NO_EXCEPTIONS
@ -973,12 +984,6 @@ namespace libtorrent
#ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_NO_EXCEPTIONS
} catch (std::exception&) {} } catch (std::exception&) {}
#endif #endif
#ifndef NDEBUG
m_current.storage = 0;
m_current.callback.clear();
#endif
} }
TORRENT_ASSERT(false); TORRENT_ASSERT(false);
} }

View File

@ -122,10 +122,7 @@ namespace libtorrent
} }
// pieces is a bitmask with the pieces we have // pieces is a bitmask with the pieces we have
void piece_picker::files_checked( void piece_picker::init(std::vector<bool> const& pieces)
std::vector<bool> const& pieces
, std::vector<downloading_piece> const& unfinished
, std::vector<int>& verify_pieces)
{ {
TORRENT_PIECE_PICKER_INVARIANT_CHECK; TORRENT_PIECE_PICKER_INVARIANT_CHECK;
#ifndef NDEBUG #ifndef NDEBUG
@ -153,25 +150,6 @@ namespace libtorrent
p.index = 0; p.index = 0;
} }
} }
// if we have fast resume info
// use it
if (!unfinished.empty())
{
for (std::vector<downloading_piece>::const_iterator i
= unfinished.begin(); i != unfinished.end(); ++i)
{
for (int j = 0; j < m_blocks_per_piece; ++j)
{
if (i->info[j].state == block_info::state_finished)
mark_as_finished(piece_block(i->index, j), 0);
}
if (is_piece_finished(i->index))
{
verify_pieces.push_back(i->index);
}
}
}
} }
void piece_picker::piece_info(int index, piece_picker::downloading_piece& st) const void piece_picker::piece_info(int index, piece_picker::downloading_piece& st) const

View File

@ -116,362 +116,8 @@ namespace detail
} }
} namespace aux { }
// This is the checker thread namespace aux {
// it is looping in an infinite loop
// until the session is aborted. It will
// normally just block in a wait() call,
// waiting for a signal from session that
// there's a new torrent to check.
void checker_impl::operator()()
{
eh_initializer();
// if we're currently performing a full file check,
// this is the torrent being processed
boost::shared_ptr<piece_checker_data> processing;
boost::shared_ptr<piece_checker_data> t;
for (;;)
{
// temporary torrent used while checking fastresume data
t.reset();
{
boost::mutex::scoped_lock l(m_mutex);
INVARIANT_CHECK;
// if the job queue is empty and
// we shouldn't abort
// wait for a signal
while (m_torrents.empty() && !m_abort && !processing)
m_cond.wait(l);
if (m_abort)
{
// no lock is needed here, because the main thread
// has already been shut down by now
processing.reset();
t.reset();
std::for_each(m_torrents.begin(), m_torrents.end()
, boost::bind(&torrent::abort
, boost::bind(&shared_ptr<torrent>::get
, boost::bind(&piece_checker_data::torrent_ptr, _1))));
m_torrents.clear();
std::for_each(m_processing.begin(), m_processing.end()
, boost::bind(&torrent::abort
, boost::bind(&shared_ptr<torrent>::get
, boost::bind(&piece_checker_data::torrent_ptr, _1))));
m_processing.clear();
return;
}
if (!m_torrents.empty())
{
t = m_torrents.front();
if (t->abort)
{
// make sure the locking order is
// consistent to avoid dead locks
// we need to lock the session because closing
// torrents assume to have access to it
l.unlock();
session_impl::mutex_t::scoped_lock l2(m_ses.m_mutex);
l.lock();
t->torrent_ptr->abort();
m_torrents.pop_front();
continue;
}
}
}
if (t)
{
std::string error_msg;
t->parse_resume_data(t->resume_data, t->torrent_ptr->torrent_file()
, error_msg);
// lock the session to add the new torrent
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
if (!error_msg.empty() && m_ses.m_alerts.should_post(alert::warning))
{
m_ses.m_alerts.post_alert(fastresume_rejected_alert(
t->torrent_ptr->get_handle()
, error_msg));
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_ses.m_logger) << "fastresume data for "
<< t->torrent_ptr->torrent_file().name() << " rejected: "
<< error_msg << "\n";
#endif
}
mutex::scoped_lock l2(m_mutex);
if (m_torrents.empty() || m_torrents.front() != t)
{
// this means the torrent was removed right after it was
// added. Abort the checking.
t.reset();
continue;
}
// clear the resume data now that it has been used
// (the fast resume data is now parsed and stored in t)
t->resume_data = entry();
bool up_to_date = t->torrent_ptr->check_fastresume(*t);
if (up_to_date)
{
INVARIANT_CHECK;
TORRENT_ASSERT(!m_torrents.empty());
TORRENT_ASSERT(m_torrents.front() == t);
t->torrent_ptr->files_checked(t->unfinished_pieces);
m_torrents.pop_front();
// we cannot add the torrent if the session is aborted.
if (!m_ses.is_aborted())
{
m_ses.m_torrents.insert(std::make_pair(t->info_hash, t->torrent_ptr));
if (m_ses.m_alerts.should_post(alert::info))
{
m_ses.m_alerts.post_alert(torrent_checked_alert(
processing->torrent_ptr->get_handle()
, "torrent finished checking"));
}
if (t->torrent_ptr->is_seed() && m_ses.m_alerts.should_post(alert::info))
{
m_ses.m_alerts.post_alert(torrent_finished_alert(
t->torrent_ptr->get_handle()
, "torrent is complete"));
}
peer_id id;
std::fill(id.begin(), id.end(), 0);
for (std::vector<tcp::endpoint>::const_iterator i = t->peers.begin();
i != t->peers.end(); ++i)
{
t->torrent_ptr->get_policy().peer_from_tracker(*i, id
, peer_info::resume_data, 0);
}
for (std::vector<tcp::endpoint>::const_iterator i = t->banned_peers.begin();
i != t->banned_peers.end(); ++i)
{
policy::peer* p = t->torrent_ptr->get_policy().peer_from_tracker(*i, id
, peer_info::resume_data, 0);
if (p) p->banned = true;
}
}
else
{
t->torrent_ptr->abort();
}
t.reset();
continue;
}
l.unlock();
// move the torrent from
// m_torrents to m_processing
TORRENT_ASSERT(m_torrents.front() == t);
m_torrents.pop_front();
m_processing.push_back(t);
if (!processing)
{
processing = t;
processing->processing = true;
t.reset();
}
}
if (!processing) continue;
TORRENT_ASSERT(processing);
bool finished = false;
bool error = false;
float progress = 0.f;
boost::tie(finished, progress) = processing->torrent_ptr->check_files(error);
if (error)
{
// This will happen if the storage fails to initialize
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
mutex::scoped_lock l2(m_mutex);
if (!m_processing.empty()
&& m_processing.front() == processing)
m_processing.pop_front();
processing.reset();
if (!m_processing.empty())
{
processing = m_processing.front();
processing->processing = true;
}
continue;
}
{
mutex::scoped_lock l2(m_mutex);
INVARIANT_CHECK;
processing->progress = progress;
if (processing->abort)
{
TORRENT_ASSERT(!m_processing.empty());
TORRENT_ASSERT(m_processing.front() == processing);
m_processing.pop_front();
// make sure the lock order is correct
l2.unlock();
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
l2.lock();
processing->torrent_ptr->abort();
processing.reset();
if (!m_processing.empty())
{
processing = m_processing.front();
processing->processing = true;
}
continue;
}
}
if (finished)
{
// lock the session to add the new torrent
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
mutex::scoped_lock l2(m_mutex);
INVARIANT_CHECK;
TORRENT_ASSERT(!m_processing.empty());
TORRENT_ASSERT(m_processing.front() == processing);
// TODO: factor out the adding of torrents to the session
// and to the checker thread to avoid duplicating the
// check for abortion.
if (!m_ses.is_aborted())
{
processing->torrent_ptr->files_checked(processing->unfinished_pieces);
m_ses.m_torrents.insert(std::make_pair(
processing->info_hash, processing->torrent_ptr));
if (m_ses.m_alerts.should_post(alert::info))
{
m_ses.m_alerts.post_alert(torrent_checked_alert(
processing->torrent_ptr->get_handle()
, "torrent finished checking"));
}
if (processing->torrent_ptr->is_seed()
&& m_ses.m_alerts.should_post(alert::info))
{
m_ses.m_alerts.post_alert(torrent_finished_alert(
processing->torrent_ptr->get_handle()
, "torrent is complete"));
}
peer_id id;
std::fill(id.begin(), id.end(), 0);
for (std::vector<tcp::endpoint>::const_iterator i = processing->peers.begin();
i != processing->peers.end(); ++i)
{
processing->torrent_ptr->get_policy().peer_from_tracker(*i, id
, peer_info::resume_data, 0);
}
for (std::vector<tcp::endpoint>::const_iterator i = processing->banned_peers.begin();
i != processing->banned_peers.end(); ++i)
{
policy::peer* p = processing->torrent_ptr->get_policy().peer_from_tracker(*i, id
, peer_info::resume_data, 0);
if (p) p->banned = true;
}
}
else
{
processing->torrent_ptr->abort();
}
processing.reset();
m_processing.pop_front();
if (!m_processing.empty())
{
processing = m_processing.front();
processing->processing = true;
}
}
}
}
aux::piece_checker_data* checker_impl::find_torrent(sha1_hash const& info_hash)
{
INVARIANT_CHECK;
for (std::deque<boost::shared_ptr<piece_checker_data> >::iterator i
= m_torrents.begin(); i != m_torrents.end(); ++i)
{
if ((*i)->info_hash == info_hash) return i->get();
}
for (std::deque<boost::shared_ptr<piece_checker_data> >::iterator i
= m_processing.begin(); i != m_processing.end(); ++i)
{
if ((*i)->info_hash == info_hash) return i->get();
}
return 0;
}
void checker_impl::remove_torrent(sha1_hash const& info_hash, int options)
{
INVARIANT_CHECK;
for (std::deque<boost::shared_ptr<piece_checker_data> >::iterator i
= m_torrents.begin(); i != m_torrents.end(); ++i)
{
if ((*i)->info_hash == info_hash)
{
TORRENT_ASSERT((*i)->processing == false);
if (options & session::delete_files)
(*i)->torrent_ptr->delete_files();
m_torrents.erase(i);
return;
}
}
for (std::deque<boost::shared_ptr<piece_checker_data> >::iterator i
= m_processing.begin(); i != m_processing.end(); ++i)
{
if ((*i)->info_hash == info_hash)
{
TORRENT_ASSERT((*i)->processing == false);
if (options & session::delete_files)
(*i)->torrent_ptr->delete_files();
m_processing.erase(i);
return;
}
}
TORRENT_ASSERT(false);
}
#ifndef NDEBUG
void checker_impl::check_invariant() const
{
for (std::deque<boost::shared_ptr<piece_checker_data> >::const_iterator i
= m_torrents.begin(); i != m_torrents.end(); ++i)
{
TORRENT_ASSERT(*i);
TORRENT_ASSERT((*i)->torrent_ptr);
}
for (std::deque<boost::shared_ptr<piece_checker_data> >::const_iterator i
= m_processing.begin(); i != m_processing.end(); ++i)
{
TORRENT_ASSERT(*i);
TORRENT_ASSERT((*i)->torrent_ptr);
}
}
#endif
struct seed_random_generator struct seed_random_generator
{ {
@ -524,7 +170,6 @@ namespace detail
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
, m_logpath(logpath) , m_logpath(logpath)
#endif #endif
, m_checker_impl(*this)
{ {
#ifdef WIN32 #ifdef WIN32
// windows XP has a limit on the number of // windows XP has a limit on the number of
@ -596,7 +241,6 @@ namespace detail
bind(&session_impl::second_tick, this, _1)); bind(&session_impl::second_tick, this, _1));
m_thread.reset(new boost::thread(boost::ref(*this))); m_thread.reset(new boost::thread(boost::ref(*this)));
m_checker_thread.reset(new boost::thread(boost::ref(m_checker_impl)));
} }
#ifndef TORRENT_DISABLE_EXTENSIONS #ifndef TORRENT_DISABLE_EXTENSIONS
@ -711,10 +355,6 @@ namespace detail
m_download_channel.close(); m_download_channel.close();
m_upload_channel.close(); m_upload_channel.close();
mutex::scoped_lock l2(m_checker_impl.m_mutex);
// abort the checker thread
m_checker_impl.m_abort = true;
} }
void session_impl::set_port_filter(port_filter const& f) void session_impl::set_port_filter(port_filter const& f)
@ -1077,25 +717,6 @@ namespace detail
#endif #endif
}; };
/*
namespace
{
struct compare_peer_ptr
{
bool operator()(peer_connection const* lhs
, intrusive_ptr<peer_connection const> const& rhs)
{
return lhs < rhs.get();
}
bool operator()(intrusive_ptr<peer_connection const> const& lhs
, peer_connection const* rhs)
{
return lhs.get() < rhs;
}
};
}
*/
void session_impl::close_connection(peer_connection const* p void session_impl::close_connection(peer_connection const* p
, char const* message) , char const* message)
{ {
@ -1647,40 +1268,21 @@ namespace detail
std::vector<torrent_handle> session_impl::get_torrents() std::vector<torrent_handle> session_impl::get_torrents()
{ {
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
mutex::scoped_lock l2(m_checker_impl.m_mutex);
std::vector<torrent_handle> ret; std::vector<torrent_handle> ret;
for (std::deque<boost::shared_ptr<aux::piece_checker_data> >::iterator i
= m_checker_impl.m_torrents.begin()
, end(m_checker_impl.m_torrents.end()); i != end; ++i)
{
if ((*i)->abort) continue;
ret.push_back(torrent_handle(this, &m_checker_impl
, (*i)->info_hash));
}
for (std::deque<boost::shared_ptr<aux::piece_checker_data> >::iterator i
= m_checker_impl.m_processing.begin()
, end(m_checker_impl.m_processing.end()); i != end; ++i)
{
if ((*i)->abort) continue;
ret.push_back(torrent_handle(this, &m_checker_impl
, (*i)->info_hash));
}
for (session_impl::torrent_map::iterator i for (session_impl::torrent_map::iterator i
= m_torrents.begin(), end(m_torrents.end()); = m_torrents.begin(), end(m_torrents.end());
i != end; ++i) i != end; ++i)
{ {
if (i->second->is_aborted()) continue; if (i->second->is_aborted()) continue;
ret.push_back(torrent_handle(this, &m_checker_impl ret.push_back(torrent_handle(this, i->first));
, i->first));
} }
return ret; return ret;
} }
torrent_handle session_impl::find_torrent_handle(sha1_hash const& info_hash) torrent_handle session_impl::find_torrent_handle(sha1_hash const& info_hash)
{ {
return torrent_handle(this, &m_checker_impl, info_hash); return torrent_handle(this, info_hash);
} }
torrent_handle session_impl::add_torrent( torrent_handle session_impl::add_torrent(
@ -1699,7 +1301,6 @@ namespace detail
// lock the session and the checker thread (the order is important!) // lock the session and the checker thread (the order is important!)
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
mutex::scoped_lock l2(m_checker_impl.m_mutex);
// INVARIANT_CHECK; // INVARIANT_CHECK;
@ -1710,17 +1311,13 @@ namespace detail
if (!find_torrent(ti->info_hash()).expired()) if (!find_torrent(ti->info_hash()).expired())
throw duplicate_torrent(); throw duplicate_torrent();
// is the torrent currently being checked?
if (m_checker_impl.find_torrent(ti->info_hash()))
throw duplicate_torrent();
// create the torrent and the data associated with // create the torrent and the data associated with
// the checker thread and store it before starting // the checker thread and store it before starting
// the thread // the thread
boost::shared_ptr<torrent> torrent_ptr( boost::shared_ptr<torrent> torrent_ptr(
new torrent(*this, m_checker_impl, ti, save_path new torrent(*this, ti, save_path
, m_listen_interface, storage_mode, 16 * 1024 , m_listen_interface, storage_mode, 16 * 1024
, sc, paused)); , sc, paused, resume_data));
torrent_ptr->start(); torrent_ptr->start();
#ifndef TORRENT_DISABLE_EXTENSIONS #ifndef TORRENT_DISABLE_EXTENSIONS
@ -1732,13 +1329,6 @@ namespace detail
} }
#endif #endif
boost::shared_ptr<aux::piece_checker_data> d(
new aux::piece_checker_data);
d->torrent_ptr = torrent_ptr;
d->save_path = save_path;
d->info_hash = ti->info_hash();
d->resume_data = resume_data;
#ifndef TORRENT_DISABLE_DHT #ifndef TORRENT_DISABLE_DHT
if (m_dht) if (m_dht)
{ {
@ -1750,13 +1340,23 @@ namespace detail
} }
#endif #endif
// add the torrent to the queue to be checked m_torrents.insert(std::make_pair(ti->info_hash(), torrent_ptr));
m_checker_impl.m_torrents.push_back(d);
// and notify the thread that it got another
// job in its queue
m_checker_impl.m_cond.notify_one();
return torrent_handle(this, &m_checker_impl, ti->info_hash()); return torrent_handle(this, ti->info_hash());
}
void session_impl::check_torrent(boost::shared_ptr<torrent> const& t)
{
if (m_queued_for_checking.empty()) t->start_checking();
m_queued_for_checking.push_back(t);
}
void session_impl::done_checking(boost::shared_ptr<torrent> const& t)
{
TORRENT_ASSERT(m_queued_for_checking.front() == t);
m_queued_for_checking.pop_front();
if (!m_queued_for_checking.empty())
m_queued_for_checking.front()->start_checking();
} }
torrent_handle session_impl::add_torrent( torrent_handle session_impl::add_torrent(
@ -1764,7 +1364,7 @@ namespace detail
, sha1_hash const& info_hash , sha1_hash const& info_hash
, char const* name , char const* name
, fs::path const& save_path , fs::path const& save_path
, entry const& , entry const& resume_data
, storage_mode_t storage_mode , storage_mode_t storage_mode
, storage_constructor_type sc , storage_constructor_type sc
, bool paused , bool paused
@ -1773,14 +1373,6 @@ namespace detail
// TODO: support resume data in this case // TODO: support resume data in this case
TORRENT_ASSERT(!save_path.empty()); TORRENT_ASSERT(!save_path.empty());
{
// lock the checker_thread
mutex::scoped_lock l(m_checker_impl.m_mutex);
// is the torrent currently being checked?
if (m_checker_impl.find_torrent(info_hash))
throw duplicate_torrent();
}
// lock the session // lock the session
session_impl::mutex_t::scoped_lock l(m_mutex); session_impl::mutex_t::scoped_lock l(m_mutex);
@ -1789,7 +1381,11 @@ namespace detail
// is the torrent already active? // is the torrent already active?
if (!find_torrent(info_hash).expired()) if (!find_torrent(info_hash).expired())
#ifndef BOOST_NO_EXCEPTIONS
throw duplicate_torrent(); throw duplicate_torrent();
#else
return torrent_handle();
#endif
// you cannot add new torrents to a session that is closing down // you cannot add new torrents to a session that is closing down
TORRENT_ASSERT(!is_aborted()); TORRENT_ASSERT(!is_aborted());
@ -1798,9 +1394,9 @@ namespace detail
// the checker thread and store it before starting // the checker thread and store it before starting
// the thread // the thread
boost::shared_ptr<torrent> torrent_ptr( boost::shared_ptr<torrent> torrent_ptr(
new torrent(*this, m_checker_impl, tracker_url, info_hash, name new torrent(*this, tracker_url, info_hash, name
, save_path, m_listen_interface, storage_mode, 16 * 1024 , save_path, m_listen_interface, storage_mode, 16 * 1024
, sc, paused)); , sc, paused, resume_data));
torrent_ptr->start(); torrent_ptr->start();
#ifndef TORRENT_DISABLE_EXTENSIONS #ifndef TORRENT_DISABLE_EXTENSIONS
@ -1812,16 +1408,14 @@ namespace detail
} }
#endif #endif
m_torrents.insert( m_torrents.insert(std::make_pair(info_hash, torrent_ptr));
std::make_pair(info_hash, torrent_ptr)).first;
return torrent_handle(this, &m_checker_impl, info_hash); return torrent_handle(this, info_hash);
} }
void session_impl::remove_torrent(const torrent_handle& h, int options) void session_impl::remove_torrent(const torrent_handle& h, int options)
{ {
if (h.m_ses != this) return; if (h.m_ses != this) return;
TORRENT_ASSERT(h.m_chk == &m_checker_impl || h.m_chk == 0);
TORRENT_ASSERT(h.m_ses != 0); TORRENT_ASSERT(h.m_ses != 0);
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
@ -1871,19 +1465,6 @@ namespace detail
TORRENT_ASSERT(m_torrents.find(i_hash) == m_torrents.end()); TORRENT_ASSERT(m_torrents.find(i_hash) == m_torrents.end());
return; return;
} }
if (h.m_chk)
{
mutex::scoped_lock l(m_checker_impl.m_mutex);
aux::piece_checker_data* d = m_checker_impl.find_torrent(h.m_info_hash);
if (d != 0)
{
if (d->processing) d->abort = true;
else m_checker_impl.remove_torrent(h.m_info_hash, options);
return;
}
}
} }
bool session_impl::listen_on( bool session_impl::listen_on(
@ -2182,31 +1763,6 @@ namespace detail
TORRENT_ASSERT(m_torrents.empty()); TORRENT_ASSERT(m_torrents.empty());
// it's important that the main thread is closed completely before
// the checker thread is terminated. Because all the connections
// have to be closed and removed from the torrents before they
// can be destructed. (because the weak pointers in the
// peer_connections will be invalidated when the torrents are
// destructed and then the invariant will be broken).
{
mutex::scoped_lock l(m_checker_impl.m_mutex);
// abort the checker thread
m_checker_impl.m_abort = true;
// abort the currently checking torrent
if (!m_checker_impl.m_torrents.empty())
{
m_checker_impl.m_torrents.front()->abort = true;
}
m_checker_impl.m_cond.notify_one();
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " waiting for checker thread\n";
#endif
m_checker_thread->join();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << time_now_string() << " waiting for disk io thread\n"; (*m_logger) << time_now_string() << " waiting for disk io thread\n";
#endif #endif
@ -2476,210 +2032,5 @@ namespace detail
} }
#endif #endif
void piece_checker_data::parse_resume_data(
const entry& resume_data
, const torrent_info& info
, std::string& error)
{
// if we don't have any resume data, return
if (resume_data.type() == entry::undefined_t) return;
entry rd = resume_data;
try
{
if (rd["file-format"].string() != "libtorrent resume file")
{
error = "missing file format tag";
return;
}
if (rd["file-version"].integer() > 1)
{
error = "incompatible file version "
+ boost::lexical_cast<std::string>(rd["file-version"].integer());
return;
}
// verify info_hash
sha1_hash hash = rd["info-hash"].string();
if (hash != info.info_hash())
{
error = "mismatching info-hash: " + boost::lexical_cast<std::string>(hash);
return;
}
// the peers
if (entry* peers_entry = rd.find_key("peers"))
{
entry::list_type& peer_list = peers_entry->list();
std::vector<tcp::endpoint> tmp_peers;
tmp_peers.reserve(peer_list.size());
for (entry::list_type::iterator i = peer_list.begin();
i != peer_list.end(); ++i)
{
tcp::endpoint a(
address::from_string((*i)["ip"].string())
, (unsigned short)(*i)["port"].integer());
tmp_peers.push_back(a);
}
peers.swap(tmp_peers);
}
if (entry* banned_peers_entry = rd.find_key("banned_peers"))
{
entry::list_type& peer_list = banned_peers_entry->list();
std::vector<tcp::endpoint> tmp_peers;
tmp_peers.reserve(peer_list.size());
for (entry::list_type::iterator i = peer_list.begin();
i != peer_list.end(); ++i)
{
tcp::endpoint a(
address::from_string((*i)["ip"].string())
, (unsigned short)(*i)["port"].integer());
tmp_peers.push_back(a);
}
banned_peers.swap(tmp_peers);
}
// read piece map
const entry::list_type& slots = rd["slots"].list();
if ((int)slots.size() > info.num_pieces())
{
error = "file has more slots than torrent (slots: "
+ boost::lexical_cast<std::string>(slots.size()) + " size: "
+ boost::lexical_cast<std::string>(info.num_pieces()) + " )";
return;
}
std::vector<int> tmp_pieces;
tmp_pieces.reserve(slots.size());
for (entry::list_type::const_iterator i = slots.begin();
i != slots.end(); ++i)
{
int index = (int)i->integer();
if (index >= info.num_pieces() || index < -2)
{
error = "too high index number in slot map (index: "
+ boost::lexical_cast<std::string>(index) + " size: "
+ boost::lexical_cast<std::string>(info.num_pieces()) + ")";
return;
}
tmp_pieces.push_back(index);
}
// only bother to check the partial pieces if we have the same block size
// as in the fast resume data. If the blocksize has changed, then throw
// away all partial pieces.
std::vector<piece_picker::downloading_piece> tmp_unfinished;
int num_blocks_per_piece = (int)rd["blocks per piece"].integer();
if (num_blocks_per_piece == info.piece_length() / torrent_ptr->block_size())
{
// the unfinished pieces
entry::list_type& unfinished = rd["unfinished"].list();
int unfinished_size = int(unfinished.size());
block_info.resize(num_blocks_per_piece * unfinished_size);
tmp_unfinished.reserve(unfinished_size);
int index = 0;
for (entry::list_type::iterator i = unfinished.begin();
i != unfinished.end(); ++i, ++index)
{
piece_picker::downloading_piece p;
p.info = &block_info[index * num_blocks_per_piece];
p.index = (int)(*i)["piece"].integer();
if (p.index < 0 || p.index >= info.num_pieces())
{
error = "invalid piece index in unfinished piece list (index: "
+ boost::lexical_cast<std::string>(p.index) + " size: "
+ boost::lexical_cast<std::string>(info.num_pieces()) + ")";
return;
}
const std::string& bitmask = (*i)["bitmask"].string();
const int num_bitmask_bytes = (std::max)(num_blocks_per_piece / 8, 1);
if ((int)bitmask.size() != num_bitmask_bytes)
{
error = "invalid size of bitmask (" + boost::lexical_cast<std::string>(bitmask.size()) + ")";
return;
}
for (int j = 0; j < num_bitmask_bytes; ++j)
{
unsigned char bits = bitmask[j];
int num_bits = (std::min)(num_blocks_per_piece - j*8, 8);
for (int k = 0; k < num_bits; ++k)
{
const int bit = j * 8 + k;
if (bits & (1 << k))
{
p.info[bit].state = piece_picker::block_info::state_finished;
++p.finished;
}
}
}
if (p.finished == 0) continue;
std::vector<int>::iterator slot_iter
= std::find(tmp_pieces.begin(), tmp_pieces.end(), p.index);
if (slot_iter == tmp_pieces.end())
{
// this piece is marked as unfinished
// but doesn't have any storage
error = "piece " + boost::lexical_cast<std::string>(p.index) + " is "
"marked as unfinished, but doesn't have any storage";
return;
}
TORRENT_ASSERT(*slot_iter == p.index);
int slot_index = static_cast<int>(slot_iter - tmp_pieces.begin());
const entry* ad = i->find_key("adler32");
if (ad && ad->type() == entry::int_t)
{
unsigned long adler
= torrent_ptr->filesystem().piece_crc(
slot_index
, torrent_ptr->block_size()
, p.info);
// crc's didn't match, don't use the resume data
if (ad->integer() != entry::integer_type(adler))
{
error = "checksum mismatch on piece "
+ boost::lexical_cast<std::string>(p.index);
return;
}
}
tmp_unfinished.push_back(p);
}
}
if (!torrent_ptr->verify_resume_data(rd, error))
return;
piece_map.swap(tmp_pieces);
unfinished_pieces.swap(tmp_unfinished);
}
catch (invalid_encoding&)
{
return;
}
catch (type_error&)
{
return;
}
catch (file_error&)
{
return;
}
}
}} }}

View File

@ -462,7 +462,12 @@ namespace libtorrent
#endif #endif
file(m_save_path / file_iter->path, file::out); file(m_save_path / file_iter->path, file::out);
#ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_NO_EXCEPTIONS
} catch (std::exception&) {} }
catch (std::exception& e)
{
m_error = e.what();
return true;
}
#endif #endif
continue; continue;
} }
@ -480,7 +485,12 @@ namespace libtorrent
f->set_size(file_iter->size); f->set_size(file_iter->size);
} }
#ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_NO_EXCEPTIONS
} catch (std::exception&) {} }
catch (std::exception& e)
{
m_error = e.what();
return true;
}
#endif #endif
} }
// close files that were opened in write mode // close files that were opened in write mode
@ -1094,9 +1104,10 @@ namespace libtorrent
, fs::path const& save_path , fs::path const& save_path
, file_pool& fp , file_pool& fp
, disk_io_thread& io , disk_io_thread& io
, storage_constructor_type sc) , storage_constructor_type sc
, storage_mode_t sm)
: m_storage(sc(ti, save_path, fp)) : m_storage(sc(ti, save_path, fp))
, m_storage_mode(storage_mode_sparse) , m_storage_mode(sm)
, m_info(ti) , m_info(ti)
, m_save_path(complete(save_path)) , m_save_path(complete(save_path))
, m_state(state_none) , m_state(state_none)
@ -1107,9 +1118,6 @@ namespace libtorrent
, m_io_thread(io) , m_io_thread(io)
, m_torrent(torrent) , m_torrent(torrent)
{ {
#ifndef NDEBUG
m_resume_data_verified = false;
#endif
} }
piece_manager::~piece_manager() piece_manager::~piece_manager()
@ -1149,6 +1157,26 @@ namespace libtorrent
m_io_thread.add_job(j, handler); m_io_thread.add_job(j, handler);
} }
void piece_manager::async_check_fastresume(entry const* resume_data
, boost::function<void(int, disk_io_job const&)> const& handler)
{
TORRENT_ASSERT(resume_data != 0);
disk_io_job j;
j.storage = this;
j.action = disk_io_job::check_fastresume;
j.buffer = (char*)resume_data;
m_io_thread.add_job(j, handler);
}
void piece_manager::async_check_files(
boost::function<void(int, disk_io_job const&)> const& handler)
{
disk_io_job j;
j.storage = this;
j.action = disk_io_job::check_files;
m_io_thread.add_job(j, handler);
}
void piece_manager::async_read( void piece_manager::async_read(
peer_request const& r peer_request const& r
, boost::function<void(int, disk_io_job const&)> const& handler , boost::function<void(int, disk_io_job const&)> const& handler
@ -1238,45 +1266,44 @@ namespace libtorrent
return false; return false;
} }
void piece_manager::export_piece_map( void piece_manager::write_resume_data(entry& rd
std::vector<int>& p, std::vector<bool> const& have) const , std::vector<bool> const& have) const
{ {
boost::recursive_mutex::scoped_lock lock(m_mutex); boost::recursive_mutex::scoped_lock lock(m_mutex);
INVARIANT_CHECK; INVARIANT_CHECK;
m_storage->write_resume_data(rd);
entry::list_type& slots = rd["slots"].list();
if (m_storage_mode == storage_mode_compact) if (m_storage_mode == storage_mode_compact)
{ {
p.clear(); slots.clear();
p.reserve(m_info->num_pieces());
std::vector<int>::const_reverse_iterator last; std::vector<int>::const_reverse_iterator last;
for (last = m_slot_to_piece.rbegin(); for (last = m_slot_to_piece.rbegin();
last != m_slot_to_piece.rend(); ++last) last != m_slot_to_piece.rend(); ++last)
{ {
if (*last != unallocated && have[*last]) break; if (*last != unallocated) break;
} }
for (std::vector<int>::const_iterator i = for (std::vector<int>::const_iterator i =
m_slot_to_piece.begin(); m_slot_to_piece.begin();
i != last.base(); ++i) i != last.base(); ++i)
{ {
p.push_back((*i >= 0 && have[*i]) ? *i : unassigned); slots.push_back((*i >= 0) ? *i : unassigned);
} }
} }
else else
{ {
p.reserve(m_info->num_pieces());
for (int i = 0; i < m_info->num_pieces(); ++i) for (int i = 0; i < m_info->num_pieces(); ++i)
{ {
p.push_back(have[i] ? i : unassigned); slots.push_back(have[i] ? i : unassigned);
} }
} }
} }
void piece_manager::mark_failed(int piece_index) void piece_manager::mark_failed(int piece_index)
{ {
boost::recursive_mutex::scoped_lock lock(m_mutex);
INVARIANT_CHECK; INVARIANT_CHECK;
if (m_storage_mode != storage_mode_compact) return; if (m_storage_mode != storage_mode_compact) return;
@ -1290,43 +1317,6 @@ namespace libtorrent
m_free_slots.push_back(slot_index); m_free_slots.push_back(slot_index);
} }
unsigned long piece_manager::piece_crc(
int slot_index
, int block_size
, piece_picker::block_info const* bi)
{
TORRENT_ASSERT(slot_index >= 0);
TORRENT_ASSERT(slot_index < m_info->num_pieces());
TORRENT_ASSERT(block_size > 0);
adler32_crc crc;
std::vector<char> buf(block_size);
int num_blocks = static_cast<int>(m_info->piece_size(slot_index)) / block_size;
int last_block_size = static_cast<int>(m_info->piece_size(slot_index)) % block_size;
if (last_block_size == 0) last_block_size = block_size;
for (int i = 0; i < num_blocks-1; ++i)
{
if (bi[i].state != piece_picker::block_info::state_finished) continue;
m_storage->read(
&buf[0]
, slot_index
, i * block_size
, block_size);
crc.update(&buf[0], block_size);
}
if (num_blocks > 0 && bi[num_blocks - 1].state == piece_picker::block_info::state_finished)
{
m_storage->read(
&buf[0]
, slot_index
, block_size * (num_blocks - 1)
, last_block_size);
crc.update(&buf[0], last_block_size);
}
return crc.final();
}
size_type piece_manager::read_impl( size_type piece_manager::read_impl(
char* buf char* buf
, int piece_index , int piece_index
@ -1382,16 +1372,10 @@ namespace libtorrent
int piece_manager::identify_data( int piece_manager::identify_data(
const std::vector<char>& piece_data const std::vector<char>& piece_data
, int current_slot , int current_slot)
, std::vector<bool>& have_pieces
, int& num_pieces
, const std::multimap<sha1_hash, int>& hash_to_piece
, boost::recursive_mutex& mutex)
{ {
// INVARIANT_CHECK; // INVARIANT_CHECK;
TORRENT_ASSERT((int)have_pieces.size() == m_info->num_pieces());
const int piece_size = static_cast<int>(m_info->piece_length()); const int piece_size = static_cast<int>(m_info->piece_length());
const int last_piece_size = static_cast<int>(m_info->piece_size( const int last_piece_size = static_cast<int>(m_info->piece_size(
m_info->num_pieces() - 1)); m_info->num_pieces() - 1));
@ -1421,8 +1405,8 @@ namespace libtorrent
map_iter end2; map_iter end2;
// makes the lookups for the small digest and the large digest // makes the lookups for the small digest and the large digest
boost::tie(begin1, end1) = hash_to_piece.equal_range(small_hash); boost::tie(begin1, end1) = m_hash_to_piece.equal_range(small_hash);
boost::tie(begin2, end2) = hash_to_piece.equal_range(large_hash); boost::tie(begin2, end2) = m_hash_to_piece.equal_range(large_hash);
// copy all potential piece indices into this vector // copy all potential piece indices into this vector
std::vector<int> matching_pieces; std::vector<int> matching_pieces;
@ -1448,15 +1432,11 @@ namespace libtorrent
// we will assume that the piece is in the right place // we will assume that the piece is in the right place
const int piece_index = current_slot; const int piece_index = current_slot;
// lock because we're writing to have_pieces int other_slot = m_piece_to_slot[piece_index];
boost::recursive_mutex::scoped_lock l(mutex); if (other_slot >= 0)
if (have_pieces[piece_index])
{ {
// we have already found a piece with // we have already found a piece with
// this index. // this index.
int other_slot = m_piece_to_slot[piece_index];
TORRENT_ASSERT(other_slot >= 0);
// take one of the other matching pieces // take one of the other matching pieces
// that hasn't already been assigned // that hasn't already been assigned
@ -1464,18 +1444,15 @@ namespace libtorrent
for (std::vector<int>::iterator i = matching_pieces.begin(); for (std::vector<int>::iterator i = matching_pieces.begin();
i != matching_pieces.end(); ++i) i != matching_pieces.end(); ++i)
{ {
if (have_pieces[*i] || *i == piece_index) continue; if (m_piece_to_slot[*i] >= 0 || *i == piece_index) continue;
other_piece = *i; other_piece = *i;
break; break;
} }
if (other_piece >= 0) if (other_piece >= 0)
{ {
// replace the old slot with 'other_piece' // replace the old slot with 'other_piece'
TORRENT_ASSERT(have_pieces[other_piece] == false);
have_pieces[other_piece] = true;
m_slot_to_piece[other_slot] = other_piece; m_slot_to_piece[other_slot] = other_piece;
m_piece_to_slot[other_piece] = other_slot; m_piece_to_slot[other_piece] = other_slot;
++num_pieces;
} }
else else
{ {
@ -1491,19 +1468,9 @@ namespace libtorrent
TORRENT_ASSERT(m_piece_to_slot[piece_index] != current_slot); TORRENT_ASSERT(m_piece_to_slot[piece_index] != current_slot);
TORRENT_ASSERT(m_piece_to_slot[piece_index] >= 0); TORRENT_ASSERT(m_piece_to_slot[piece_index] >= 0);
m_piece_to_slot[piece_index] = has_no_slot; m_piece_to_slot[piece_index] = has_no_slot;
#ifndef NDEBUG
// to make the assert happy, a few lines down
have_pieces[piece_index] = false;
#endif
}
else
{
++num_pieces;
} }
TORRENT_ASSERT(have_pieces[piece_index] == false);
TORRENT_ASSERT(m_piece_to_slot[piece_index] == has_no_slot); TORRENT_ASSERT(m_piece_to_slot[piece_index] == has_no_slot);
have_pieces[piece_index] = true;
return piece_index; return piece_index;
} }
@ -1514,21 +1481,14 @@ namespace libtorrent
for (std::vector<int>::iterator i = matching_pieces.begin(); for (std::vector<int>::iterator i = matching_pieces.begin();
i != matching_pieces.end(); ++i) i != matching_pieces.end(); ++i)
{ {
if (have_pieces[*i]) continue; if (m_piece_to_slot[*i] >= 0) continue;
free_piece = *i; free_piece = *i;
break; break;
} }
if (free_piece >= 0) if (free_piece >= 0)
{ {
// lock because we're writing to have_pieces
boost::recursive_mutex::scoped_lock l(mutex);
TORRENT_ASSERT(have_pieces[free_piece] == false);
TORRENT_ASSERT(m_piece_to_slot[free_piece] == has_no_slot); TORRENT_ASSERT(m_piece_to_slot[free_piece] == has_no_slot);
have_pieces[free_piece] = true;
++num_pieces;
return free_piece; return free_piece;
} }
else else
@ -1538,15 +1498,92 @@ namespace libtorrent
} }
} }
int piece_manager::check_no_fastresume(std::string& error)
{
torrent_info::file_iterator i = m_info->begin_files(true);
torrent_info::file_iterator end = m_info->end_files(true);
for (; i != end; ++i)
{
bool file_exists = false;
fs::path f = m_save_path / i->path;
#ifndef BOOST_NO_EXCEPTIONS
try
{
#endif
#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
file_exists = exists_win(f);
#elif defined(_WIN32) && defined(UNICODE)
fs::wpath wf = safe_convert(f.string());
file_exists = exists(wf);
#else
file_exists = exists(f);
#endif
#ifndef BOOST_NO_EXCEPTIONS
}
catch (std::exception& e)
{
error = f.string();
error += ": ";
error += e.what();
return fatal_disk_error;
}
#endif
if (file_exists && i->size > 0)
{
m_state = state_full_check;
m_piece_to_slot.clear();
m_piece_to_slot.resize(m_info->num_pieces(), has_no_slot);
m_slot_to_piece.clear();
m_slot_to_piece.resize(m_info->num_pieces(), unallocated);
return need_full_check;
}
}
if (m_storage_mode == storage_mode_compact)
{
// in compact mode without checking, we need to
// populate the unallocated list
TORRENT_ASSERT(m_unallocated_slots.empty());
for (int i = 0, end(m_info->num_pieces()); i < end; ++i)
m_unallocated_slots.push_back(i);
m_piece_to_slot.resize(m_info->num_pieces(), has_no_slot);
m_slot_to_piece.resize(m_info->num_pieces(), unallocated);
}
return check_init_storage(error);
}
int piece_manager::check_init_storage(std::string& error)
{
if (m_storage->initialize(m_storage_mode == storage_mode_allocate))
{
error = m_storage->error();
m_storage->clear_error();
return fatal_disk_error;
}
m_state = state_finished;
buffer().swap(m_scratch_buffer);
buffer().swap(m_scratch_buffer2);
if (m_storage_mode != storage_mode_compact)
{
// if no piece is out of place
// since we're in full allocation mode, we can
// forget the piece allocation tables
std::vector<int>().swap(m_piece_to_slot);
std::vector<int>().swap(m_slot_to_piece);
std::vector<int>().swap(m_free_slots);
std::vector<int>().swap(m_unallocated_slots);
}
return no_error;
}
// check if the fastresume data is up to date // check if the fastresume data is up to date
// if it is, use it and return true. If it // if it is, use it and return true. If it
// isn't return false and the full check // isn't return false and the full check
// will be run // will be run
bool piece_manager::check_fastresume( int piece_manager::check_fastresume(
aux::piece_checker_data& data entry const& rd, std::string& error)
, std::vector<bool>& pieces
, int& num_pieces, storage_mode_t storage_mode
, std::string& error_msg)
{ {
boost::recursive_mutex::scoped_lock lock(m_mutex); boost::recursive_mutex::scoped_lock lock(m_mutex);
@ -1554,7 +1591,144 @@ namespace libtorrent
TORRENT_ASSERT(m_info->piece_length() > 0); TORRENT_ASSERT(m_info->piece_length() > 0);
m_storage_mode = storage_mode; // if we don't have any resume data, return
if (rd.type() == entry::undefined_t) return check_no_fastresume(error);
if (rd.type() != entry::dictionary_t)
{
error = "invalid fastresume data (not a bencoded dictionary)";
return check_no_fastresume(error);
}
entry const* file_format = rd.find_key("file-format");
if (file_format == 0 || file_format->type() != entry::string_t)
{
error = "missing file format tag";
return check_no_fastresume(error);
}
if (file_format->string() != "libtorrent resume file")
{
error = "invalid file format tag";
return check_no_fastresume(error);
}
entry const* info_hash = rd.find_key("info-hash");
if (info_hash == 0 || info_hash->type() != entry::string_t)
{
error = "missing info-hash";
return check_no_fastresume(error);
}
if (sha1_hash(info_hash->string()) != m_info->info_hash())
{
error = "mismatching info-hash";
return check_no_fastresume(error);
}
int block_size = (std::min)(16 * 1024, m_info->piece_length());
entry const* blocks_per_piece_ent = rd.find_key("blocks per piece");
if (blocks_per_piece_ent != 0
&& blocks_per_piece_ent->type() == entry::int_t
&& blocks_per_piece_ent->integer() != m_info->piece_length() / block_size)
{
error = "invalid 'blocks per piece' entry";
return check_no_fastresume(error);
}
storage_mode_t storage_mode = storage_mode_compact;
entry const* allocation = rd.find_key("allocation");
if (allocation != 0
&& allocation->type() == entry::string_t
&& allocation->string() != "compact")
storage_mode = storage_mode_sparse;
// read piece map
entry const* slots = rd.find_key("slots");
if (slots == 0 || slots->type() != entry::list_t)
{
error = "missing slot list";
return check_no_fastresume(error);
}
if ((int)slots->list().size() > m_info->num_pieces())
{
error = "file has more slots than torrent (slots: "
+ boost::lexical_cast<std::string>(slots->list().size()) + " size: "
+ boost::lexical_cast<std::string>(m_info->num_pieces()) + " )";
return check_no_fastresume(error);
}
// assume no piece is out of place (i.e. in a slot
// other than the one it should be in)
bool out_of_place = false;
if (storage_mode == storage_mode_compact)
{
int num_pieces = int(m_info->num_pieces());
m_slot_to_piece.resize(num_pieces, unallocated);
m_piece_to_slot.resize(num_pieces, has_no_slot);
int slot = 0;
for (entry::list_type::const_iterator i = slots->list().begin();
i != slots->list().end(); ++i, ++slot)
{
if (i->type() != entry::int_t)
{
error = "invalid entry type in slot list";
return check_no_fastresume(error);
}
int index = int(i->integer());
if (index >= num_pieces || index < -2)
{
error = "too high index number in slot map (index: "
+ boost::lexical_cast<std::string>(index) + " size: "
+ boost::lexical_cast<std::string>(num_pieces) + ")";
return check_no_fastresume(error);
}
if (index >= 0)
{
m_slot_to_piece[slot] = index;
m_piece_to_slot[index] = slot;
if (slot != index) out_of_place = true;
}
else if (index == unassigned)
{
if (m_storage_mode == storage_mode_compact)
m_free_slots.push_back(slot);
}
else
{
TORRENT_ASSERT(index == unallocated);
if (m_storage_mode == storage_mode_compact)
m_unallocated_slots.push_back(slot);
}
}
}
else
{
int slot = 0;
for (entry::list_type::const_iterator i = slots->list().begin();
i != slots->list().end(); ++i, ++slot)
{
if (i->type() != entry::int_t)
{
error = "invalid entry type in slot list";
return check_no_fastresume(error);
}
int index = int(i->integer());
if (index != slot)
{
error = "invalid slot index";
return check_no_fastresume(error);
}
}
}
if (!m_storage->verify_resume_data(rd, error))
return check_no_fastresume(error);
// This will corrupt the storage // This will corrupt the storage
// use while debugging to find // use while debugging to find
@ -1562,123 +1736,44 @@ namespace libtorrent
// by check_pieces. // by check_pieces.
// m_storage->shuffle(); // m_storage->shuffle();
m_piece_to_slot.resize(m_info->num_pieces(), has_no_slot); if (m_storage_mode == storage_mode_compact)
m_slot_to_piece.resize(m_info->num_pieces(), unallocated);
TORRENT_ASSERT(m_free_slots.empty());
TORRENT_ASSERT(m_unallocated_slots.empty());
// assume no piece is out of place (i.e. in a slot
// other than the one it should be in)
bool out_of_place = false;
pieces.clear();
pieces.resize(m_info->num_pieces(), false);
num_pieces = 0;
// if we have fast-resume info
// use it instead of doing the actual checking
if (!data.piece_map.empty()
&& int(data.piece_map.size()) <= m_info->num_pieces())
{ {
TORRENT_ASSERT(m_resume_data_verified); if (m_unallocated_slots.empty()) switch_to_full_mode();
for (int i = 0; i < (int)data.piece_map.size(); ++i) }
else
{
TORRENT_ASSERT(m_free_slots.empty());
TORRENT_ASSERT(m_unallocated_slots.empty());
if (out_of_place)
{ {
m_slot_to_piece[i] = data.piece_map[i]; // in this case we're in full allocation mode, but
if (data.piece_map[i] >= 0) // we're resuming a compact allocated storage
{ m_state = state_expand_pieces;
if (data.piece_map[i] != i) out_of_place = true; m_current_slot = 0;
m_piece_to_slot[data.piece_map[i]] = i; error = "pieces needs to be reordered";
int found_piece = data.piece_map[i]; return need_full_check;
// if the piece is not in the unfinished list
// we have all of it
if (std::find_if(
data.unfinished_pieces.begin()
, data.unfinished_pieces.end()
, piece_picker::has_index(found_piece))
== data.unfinished_pieces.end())
{
++num_pieces;
pieces[found_piece] = true;
}
}
else if (data.piece_map[i] == unassigned)
{
if (m_storage_mode == storage_mode_compact)
m_free_slots.push_back(i);
}
else
{
TORRENT_ASSERT(data.piece_map[i] == unallocated);
if (m_storage_mode == storage_mode_compact)
m_unallocated_slots.push_back(i);
}
} }
if (m_storage_mode == storage_mode_compact)
{
m_unallocated_slots.reserve(int(m_info->num_pieces() - data.piece_map.size()));
for (int i = (int)data.piece_map.size(); i < (int)m_info->num_pieces(); ++i)
{
m_unallocated_slots.push_back(i);
}
if (m_unallocated_slots.empty())
{
switch_to_full_mode();
}
}
else
{
if (!out_of_place)
{
// if no piece is out of place
// since we're in full allocation mode, we can
// forget the piece allocation tables
std::vector<int>().swap(m_piece_to_slot);
std::vector<int>().swap(m_slot_to_piece);
m_state = state_create_files;
return false;
}
else
{
// in this case we're in full allocation mode, but
// we're resuming a compact allocated storage
m_state = state_expand_pieces;
m_current_slot = 0;
error_msg = "pieces needs to be reordered";
return false;
}
}
m_state = state_create_files;
return false;
} }
m_state = state_full_check; return check_init_storage(error);
return false;
} }
/* /*
state chart: state chart:
check_fastresume() check_fastresume() ----------+
|
| | | | |
| v | v v
| +------------+ +---------------+ | +------------+ +---------------+
| | full_check |-->| expand_pieses | | | full_check |-->| expand_pieses |
| +------------+ +---------------+ | +------------+ +---------------+
| | | | | |
| v | | v |
| +--------------+ | | +--------------+ |
+->| create_files | <------+ +->| finished | <------+
+--------------+ +--------------+
|
v
+----------+
| finished |
+----------+
*/ */
@ -1688,23 +1783,12 @@ namespace libtorrent
// the second return value is the progress the // the second return value is the progress the
// file check is at. 0 is nothing done, and 1 // file check is at. 0 is nothing done, and 1
// is finished // is finished
std::pair<bool, float> piece_manager::check_files( int piece_manager::check_files(int& current_slot, int& have_piece, std::string& error)
std::vector<bool>& pieces, int& num_pieces, boost::recursive_mutex& mutex
, bool& error)
{ {
#ifndef NDEBUG TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_info->num_pieces());
boost::recursive_mutex::scoped_lock l_(mutex);
TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true));
l_.unlock();
#endif
if (m_state == state_create_files)
{
m_storage->initialize(m_storage_mode == storage_mode_allocate);
m_state = state_finished;
return std::make_pair(true, 1.f);
}
current_slot = m_current_slot;
have_piece = -1;
if (m_state == state_expand_pieces) if (m_state == state_expand_pieces)
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
@ -1724,8 +1808,9 @@ namespace libtorrent
if (m_storage->read(&m_scratch_buffer2[0], piece, 0, piece_size) if (m_storage->read(&m_scratch_buffer2[0], piece, 0, piece_size)
!= piece_size) != piece_size)
{ {
error = true; error = m_storage->error();
return std::make_pair(true, (float)m_current_slot / m_info->num_pieces()); m_storage->clear_error();
return fatal_disk_error;
} }
m_scratch_piece = other_piece; m_scratch_piece = other_piece;
m_piece_to_slot[other_piece] = unassigned; m_piece_to_slot[other_piece] = unassigned;
@ -1736,8 +1821,9 @@ namespace libtorrent
int piece_size = m_info->piece_size(piece); int piece_size = m_info->piece_size(piece);
if (m_storage->write(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size) if (m_storage->write(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size)
{ {
error = true; error = m_storage->error();
return std::make_pair(true, (float)m_current_slot / m_info->num_pieces()); m_storage->clear_error();
return fatal_disk_error;
} }
m_piece_to_slot[piece] = piece; m_piece_to_slot[piece] = piece;
m_slot_to_piece[piece] = piece; m_slot_to_piece[piece] = piece;
@ -1745,7 +1831,7 @@ namespace libtorrent
if (other_piece >= 0) if (other_piece >= 0)
m_scratch_buffer.swap(m_scratch_buffer2); m_scratch_buffer.swap(m_scratch_buffer2);
return std::make_pair(false, (float)m_current_slot / m_info->num_pieces()); return need_full_check;
} }
while (m_current_slot < m_info->num_pieces() while (m_current_slot < m_info->num_pieces()
@ -1757,15 +1843,7 @@ namespace libtorrent
if (m_current_slot == m_info->num_pieces()) if (m_current_slot == m_info->num_pieces())
{ {
m_state = state_create_files; return check_init_storage(error);
buffer().swap(m_scratch_buffer);
buffer().swap(m_scratch_buffer2);
if (m_storage_mode != storage_mode_compact)
{
std::vector<int>().swap(m_piece_to_slot);
std::vector<int>().swap(m_slot_to_piece);
}
return std::make_pair(false, 1.f);
} }
int piece = m_slot_to_piece[m_current_slot]; int piece = m_slot_to_piece[m_current_slot];
@ -1782,8 +1860,9 @@ namespace libtorrent
int piece_size = m_info->piece_size(other_piece); int piece_size = m_info->piece_size(other_piece);
if (m_storage->read(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size) if (m_storage->read(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size)
{ {
error = true; error = m_storage->error();
return std::make_pair(false, (float)m_current_slot / m_info->num_pieces()); m_storage->clear_error();
return fatal_disk_error;
} }
m_scratch_piece = other_piece; m_scratch_piece = other_piece;
m_piece_to_slot[other_piece] = unassigned; m_piece_to_slot[other_piece] = unassigned;
@ -1796,12 +1875,12 @@ namespace libtorrent
m_slot_to_piece[m_current_slot] = unassigned; m_slot_to_piece[m_current_slot] = unassigned;
m_slot_to_piece[piece] = piece; m_slot_to_piece[piece] = piece;
return std::make_pair(false, (float)m_current_slot / m_info->num_pieces()); return need_full_check;
} }
TORRENT_ASSERT(m_state == state_full_check); TORRENT_ASSERT(m_state == state_full_check);
bool skip = check_one_piece(pieces, num_pieces, mutex); bool skip = check_one_piece(have_piece);
if (skip) if (skip)
{ {
@ -1837,6 +1916,7 @@ namespace libtorrent
} }
++m_current_slot; ++m_current_slot;
current_slot = m_current_slot;
if (m_current_slot >= m_info->num_pieces()) if (m_current_slot >= m_info->num_pieces())
{ {
@ -1856,8 +1936,7 @@ namespace libtorrent
std::vector<int>().swap(m_piece_to_slot); std::vector<int>().swap(m_piece_to_slot);
std::vector<int>().swap(m_slot_to_piece); std::vector<int>().swap(m_slot_to_piece);
m_state = state_create_files; return check_init_storage(error);
return std::make_pair(false, 1.f);
} }
else else
{ {
@ -1865,47 +1944,37 @@ namespace libtorrent
// we're resuming a compact allocated storage // we're resuming a compact allocated storage
m_state = state_expand_pieces; m_state = state_expand_pieces;
m_current_slot = 0; m_current_slot = 0;
return std::make_pair(false, 0.f); current_slot = m_current_slot;
return need_full_check;
} }
} }
else if (m_unallocated_slots.empty()) else if (m_unallocated_slots.empty())
{ {
switch_to_full_mode(); switch_to_full_mode();
} }
m_state = state_create_files; return check_init_storage(error);
#ifndef NDEBUG
boost::recursive_mutex::scoped_lock l(mutex);
TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true));
#endif
return std::make_pair(false, 1.f);
} }
return need_full_check;
TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true));
return std::make_pair(false, (float)m_current_slot / m_info->num_pieces());
} }
bool piece_manager::check_one_piece(std::vector<bool>& pieces, int& num_pieces bool piece_manager::check_one_piece(int& have_piece)
, boost::recursive_mutex& mutex)
{ {
// ------------------------ // ------------------------
// DO THE FULL CHECK // DO THE FULL CHECK
// ------------------------ // ------------------------
TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_info->num_pieces());
TORRENT_ASSERT(int(m_slot_to_piece.size()) == m_info->num_pieces());
TORRENT_ASSERT(have_piece == -1);
// initialization for the full check // initialization for the full check
if (m_hash_to_piece.empty()) if (m_hash_to_piece.empty())
{ {
for (int i = 0; i < m_info->num_pieces(); ++i) for (int i = 0; i < m_info->num_pieces(); ++i)
{
m_hash_to_piece.insert(std::make_pair(m_info->hash_for_piece(i), i)); m_hash_to_piece.insert(std::make_pair(m_info->hash_for_piece(i), i));
}
boost::recursive_mutex::scoped_lock l(mutex);
std::fill(pieces.begin(), pieces.end(), false);
num_pieces = 0;
} }
m_piece_data.resize(m_info->piece_length()); m_piece_data.resize(int(m_info->piece_length()));
int piece_size = m_info->piece_size(m_current_slot); int piece_size = m_info->piece_size(m_current_slot);
int num_read = m_storage->read(&m_piece_data[0] int num_read = m_storage->read(&m_piece_data[0]
, m_current_slot, 0, piece_size); , m_current_slot, 0, piece_size);
@ -1914,14 +1983,14 @@ namespace libtorrent
if (num_read != piece_size) if (num_read != piece_size)
return true; return true;
int piece_index = identify_data(m_piece_data, m_current_slot int piece_index = identify_data(m_piece_data, m_current_slot);
, pieces, num_pieces, m_hash_to_piece, mutex);
if (piece_index >= 0) have_piece = piece_index;
if (piece_index != m_current_slot if (piece_index != m_current_slot
&& piece_index >= 0) && piece_index >= 0)
m_out_of_place = true; m_out_of_place = true;
TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true));
TORRENT_ASSERT(piece_index == unassigned || piece_index >= 0); TORRENT_ASSERT(piece_index == unassigned || piece_index >= 0);
const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated; const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated;
@ -2148,7 +2217,7 @@ namespace libtorrent
if (m_storage_mode != storage_mode_compact) return piece_index; if (m_storage_mode != storage_mode_compact) return piece_index;
// INVARIANT_CHECK; INVARIANT_CHECK;
TORRENT_ASSERT(piece_index >= 0); TORRENT_ASSERT(piece_index >= 0);
TORRENT_ASSERT(piece_index < (int)m_piece_to_slot.size()); TORRENT_ASSERT(piece_index < (int)m_piece_to_slot.size());
@ -2263,7 +2332,7 @@ namespace libtorrent
boost::recursive_mutex::scoped_lock lock(m_mutex); boost::recursive_mutex::scoped_lock lock(m_mutex);
TORRENT_ASSERT(num_slots > 0); TORRENT_ASSERT(num_slots > 0);
// INVARIANT_CHECK; INVARIANT_CHECK;
TORRENT_ASSERT(!m_unallocated_slots.empty()); TORRENT_ASSERT(!m_unallocated_slots.empty());
TORRENT_ASSERT(m_storage_mode == storage_mode_compact); TORRENT_ASSERT(m_storage_mode == storage_mode_compact);

View File

@ -150,14 +150,14 @@ namespace libtorrent
torrent::torrent( torrent::torrent(
session_impl& ses session_impl& ses
, aux::checker_impl& checker
, boost::intrusive_ptr<torrent_info> tf , boost::intrusive_ptr<torrent_info> tf
, fs::path const& save_path , fs::path const& save_path
, tcp::endpoint const& net_interface , tcp::endpoint const& net_interface
, storage_mode_t storage_mode , storage_mode_t storage_mode
, int block_size , int block_size
, storage_constructor_type sc , storage_constructor_type sc
, bool paused) , bool paused
, entry const& resume_data)
: m_torrent_file(tf) : m_torrent_file(tf)
, m_abort(false) , m_abort(false)
, m_paused(paused) , m_paused(paused)
@ -179,7 +179,6 @@ namespace libtorrent
, m_last_dht_announce(time_now() - minutes(15)) , m_last_dht_announce(time_now() - minutes(15))
#endif #endif
, m_ses(ses) , m_ses(ses)
, m_checker(checker)
, m_picker(0) , m_picker(0)
, m_trackers(m_torrent_file->trackers()) , m_trackers(m_torrent_file->trackers())
, m_last_working_tracker(-1) , m_last_working_tracker(-1)
@ -195,6 +194,9 @@ namespace libtorrent
, m_net_interface(net_interface.address(), 0) , m_net_interface(net_interface.address(), 0)
, m_save_path(complete(save_path)) , m_save_path(complete(save_path))
, m_storage_mode(storage_mode) , m_storage_mode(storage_mode)
, m_state(torrent_status::queued_for_checking)
, m_progress(0.f)
, m_resume_data(resume_data)
, m_default_block_size(block_size) , m_default_block_size(block_size)
, m_connections_initialized(true) , m_connections_initialized(true)
, m_settings(ses.settings()) , m_settings(ses.settings())
@ -211,7 +213,6 @@ namespace libtorrent
torrent::torrent( torrent::torrent(
session_impl& ses session_impl& ses
, aux::checker_impl& checker
, char const* tracker_url , char const* tracker_url
, sha1_hash const& info_hash , sha1_hash const& info_hash
, char const* name , char const* name
@ -220,7 +221,8 @@ namespace libtorrent
, storage_mode_t storage_mode , storage_mode_t storage_mode
, int block_size , int block_size
, storage_constructor_type sc , storage_constructor_type sc
, bool paused) , bool paused
, entry const& resume_data)
: m_torrent_file(new torrent_info(info_hash)) : m_torrent_file(new torrent_info(info_hash))
, m_abort(false) , m_abort(false)
, m_paused(paused) , m_paused(paused)
@ -242,7 +244,6 @@ namespace libtorrent
, m_last_dht_announce(time_now() - minutes(15)) , m_last_dht_announce(time_now() - minutes(15))
#endif #endif
, m_ses(ses) , m_ses(ses)
, m_checker(checker)
, m_picker(0) , m_picker(0)
, m_last_working_tracker(-1) , m_last_working_tracker(-1)
, m_currently_trying_tracker(0) , m_currently_trying_tracker(0)
@ -257,6 +258,9 @@ namespace libtorrent
, m_net_interface(net_interface.address(), 0) , m_net_interface(net_interface.address(), 0)
, m_save_path(complete(save_path)) , m_save_path(complete(save_path))
, m_storage_mode(storage_mode) , m_storage_mode(storage_mode)
, m_state(torrent_status::queued_for_checking)
, m_progress(0.f)
, m_resume_data(resume_data)
, m_default_block_size(block_size) , m_default_block_size(block_size)
, m_connections_initialized(false) , m_connections_initialized(false)
, m_settings(ses.settings()) , m_settings(ses.settings())
@ -399,7 +403,8 @@ namespace libtorrent
// the shared_from_this() will create an intentional // the shared_from_this() will create an intentional
// cycle of ownership, se the hpp file for description. // cycle of ownership, se the hpp file for description.
m_owning_storage = new piece_manager(shared_from_this(), m_torrent_file m_owning_storage = new piece_manager(shared_from_this(), m_torrent_file
, m_save_path, m_ses.m_files, m_ses.m_disk_thread, m_storage_constructor); , m_save_path, m_ses.m_files, m_ses.m_disk_thread, m_storage_constructor
, m_storage_mode);
m_storage = m_owning_storage.get(); m_storage = m_owning_storage.get();
m_block_size = calculate_block_size(*m_torrent_file, m_default_block_size); m_block_size = calculate_block_size(*m_torrent_file, m_default_block_size);
m_picker.reset(new piece_picker( m_picker.reset(new piece_picker(
@ -409,6 +414,233 @@ namespace libtorrent
std::vector<std::string> const& url_seeds = m_torrent_file->url_seeds(); std::vector<std::string> const& url_seeds = m_torrent_file->url_seeds();
std::copy(url_seeds.begin(), url_seeds.end(), std::inserter(m_web_seeds std::copy(url_seeds.begin(), url_seeds.end(), std::inserter(m_web_seeds
, m_web_seeds.begin())); , m_web_seeds.begin()));
m_state = torrent_status::queued_for_checking;
m_storage->async_check_fastresume(&m_resume_data
, bind(&torrent::on_resume_data_checked
, shared_from_this(), _1, _2));
}
void torrent::on_resume_data_checked(int ret, disk_io_job const& j)
{
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
if (ret == piece_manager::fatal_disk_error)
{
if (m_ses.m_alerts.should_post(alert::fatal))
{
m_ses.m_alerts.post_alert(file_error_alert(get_handle(), j.str));
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
(*m_ses.m_logger) << time_now_string() << ": fatal disk error ["
" error: " << j.str <<
" torrent: " << torrent_file().name() <<
" ]\n";
#endif
}
std::fill(m_have_pieces.begin(), m_have_pieces.end(), false);
m_num_pieces = 0;
pause();
return;
}
// parse out "peers" from the resume data and add them to the peer list
entry const* peers_entry = m_resume_data.find_key("peers");
if (peers_entry && peers_entry->type() == entry::list_t)
{
peer_id id;
std::fill(id.begin(), id.end(), 0);
entry::list_type const& peer_list = peers_entry->list();
for (entry::list_type::const_iterator i = peer_list.begin();
i != peer_list.end(); ++i)
{
if (i->type() != entry::dictionary_t) continue;
entry const* ip = i->find_key("ip");
entry const* port = i->find_key("port");
if (ip == 0 || port == 0
|| ip->type() != entry::string_t
|| port->type() != entry::int_t)
continue;
tcp::endpoint a(
address::from_string(ip->string())
, (unsigned short)port->integer());
m_policy.peer_from_tracker(a, id, peer_info::resume_data, 0);
}
}
// parse out "banned_peers" and add them as banned
entry const* banned_peers_entry = m_resume_data.find_key("banned_peers");
if (banned_peers_entry != 0 && banned_peers_entry->type() == entry::list_t)
{
peer_id id;
std::fill(id.begin(), id.end(), 0);
entry::list_type const& peer_list = banned_peers_entry->list();
for (entry::list_type::const_iterator i = peer_list.begin();
i != peer_list.end(); ++i)
{
if (i->type() != entry::dictionary_t) continue;
entry const* ip = i->find_key("ip");
entry const* port = i->find_key("port");
if (ip == 0 || port == 0
|| ip->type() != entry::string_t
|| port->type() != entry::int_t)
continue;
tcp::endpoint a(
address::from_string(ip->string())
, (unsigned short)port->integer());
policy::peer* p = m_policy.peer_from_tracker(a, id, peer_info::resume_data, 0);
if (p) p->banned = true;
}
}
bool fastresume_rejected = !j.str.empty();
if (fastresume_rejected && m_ses.m_alerts.should_post(alert::warning))
{
m_ses.m_alerts.post_alert(fastresume_rejected_alert(get_handle(), j.str));
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
(*m_ses.m_logger) << "fastresume data for "
<< torrent_file().name() << " rejected: "
<< j.str << "\n";
#endif
}
if (ret == 0)
{
// there are either no files for this torrent
// or the resume_data was accepted
m_num_pieces = 0;
std::fill(m_have_pieces.begin(), m_have_pieces.end(), false);
if (!fastresume_rejected)
{
// parse slots
entry const* slots_ent = m_resume_data.find_key("slots");
if (slots_ent != 0 && slots_ent->type() == entry::list_t)
{
entry::list_type const& slots = slots_ent->list();
for (entry::list_type::const_iterator i = slots.begin();
i != slots.end(); ++i)
{
if (i->type() != entry::int_t) continue;
int piece_index = int(i->integer());
if (piece_index < 0 || piece_index >= torrent_file().num_pieces())
continue;
m_have_pieces[piece_index] = true;
++m_num_pieces;
}
}
// parse unfinished pieces
int num_blocks_per_piece =
static_cast<int>(torrent_file().piece_length()) / block_size();
entry const* unfinished_ent = m_resume_data.find_key("unfinished");
if (unfinished_ent != 0 && unfinished_ent->type() == entry::list_t)
{
entry::list_type const& unfinished = unfinished_ent->list();
int index = 0;
for (entry::list_type::const_iterator i = unfinished.begin();
i != unfinished.end(); ++i, ++index)
{
if (i->type() != entry::dictionary_t) continue;
entry const* piece = i->find_key("piece");
if (piece == 0 || piece->type() != entry::int_t) continue;
int piece_index = int(piece->integer());
if (piece_index < 0 || piece_index >= torrent_file().num_pieces())
continue;
// if this assert is hit, the resume data file was corrupt
TORRENT_ASSERT(m_have_pieces[piece_index]);
m_have_pieces[piece_index] = false;
--m_num_pieces;
entry const* bitmask_ent = i->find_key("bitmask");
if (bitmask_ent == 0 || bitmask_ent->type() != entry::string_t) break;
std::string const& bitmask = bitmask_ent->string();
const int num_bitmask_bytes = (std::max)(num_blocks_per_piece / 8, 1);
if ((int)bitmask.size() != num_bitmask_bytes) continue;
for (int j = 0; j < num_bitmask_bytes; ++j)
{
unsigned char bits = bitmask[j];
int num_bits = (std::min)(num_blocks_per_piece - j*8, 8);
for (int k = 0; k < num_bits; ++k)
{
const int bit = j * 8 + k;
if (bits & (1 << k))
{
m_picker->mark_as_finished(piece_block(piece_index, bit), 0);
if (m_picker->is_piece_finished(piece_index))
async_verify_piece(piece_index, bind(&torrent::piece_finished
, shared_from_this(), piece_index, _1));
}
}
}
}
}
}
files_checked();
}
else
{
// either the fastresume data was rejected or there are
// some files
m_ses.check_torrent(shared_from_this());
}
}
void torrent::start_checking()
{
m_state = torrent_status::checking_files;
m_storage->async_check_files(bind(
&torrent::on_piece_checked
, shared_from_this(), _1, _2));
}
void torrent::on_piece_checked(int ret, disk_io_job const& j)
{
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
if (ret == piece_manager::fatal_disk_error)
{
if (m_ses.m_alerts.should_post(alert::fatal))
{
m_ses.m_alerts.post_alert(file_error_alert(get_handle(), j.str));
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
(*m_ses.m_logger) << time_now_string() << ": fatal disk error ["
" error: " << j.str <<
" torrent: " << torrent_file().name() <<
" ]\n";
#endif
}
std::fill(m_have_pieces.begin(), m_have_pieces.end(), false);
m_num_pieces = 0;
pause();
m_ses.done_checking(shared_from_this());
return;
}
m_progress = j.piece / float(torrent_file().num_pieces());
if (j.offset >= 0 && !m_have_pieces[j.offset])
{
m_have_pieces[j.offset] = true;
++m_num_pieces;
}
// we're not done checking yet
// this handler will be called repeatedly until
// we're done, or encounter a failure
if (ret == piece_manager::need_full_check) return;
m_ses.done_checking(shared_from_this());
files_checked();
} }
void torrent::use_interface(const char* net_interface) void torrent::use_interface(const char* net_interface)
@ -1102,7 +1334,6 @@ namespace libtorrent
// that has sent the least number of pieces // that has sent the least number of pieces
m_picker->restore_piece(index); m_picker->restore_piece(index);
TORRENT_ASSERT(m_storage); TORRENT_ASSERT(m_storage);
m_storage->mark_failed(index);
TORRENT_ASSERT(m_have_pieces[index] == false); TORRENT_ASSERT(m_have_pieces[index] == false);
@ -2151,31 +2382,14 @@ namespace libtorrent
return false; return false;
} }
init();
boost::mutex::scoped_lock(m_checker.m_mutex);
boost::shared_ptr<aux::piece_checker_data> d(
new aux::piece_checker_data);
d->torrent_ptr = shared_from_this();
d->save_path = m_save_path;
d->info_hash = m_torrent_file->info_hash();
// add the torrent to the queue to be checked
m_checker.m_torrents.push_back(d);
typedef session_impl::torrent_map torrent_map;
torrent_map::iterator i = m_ses.m_torrents.find(
m_torrent_file->info_hash());
TORRENT_ASSERT(i != m_ses.m_torrents.end());
m_ses.m_torrents.erase(i);
// and notify the thread that it got another
// job in its queue
m_checker.m_cond.notify_one();
if (m_ses.m_alerts.should_post(alert::info)) if (m_ses.m_alerts.should_post(alert::info))
{ {
m_ses.m_alerts.post_alert(metadata_received_alert( m_ses.m_alerts.post_alert(metadata_received_alert(
get_handle(), "metadata successfully received from swarm")); get_handle(), "metadata successfully received from swarm"));
} }
init();
return true; return true;
} }
@ -2186,6 +2400,13 @@ namespace libtorrent
TORRENT_ASSERT(p != 0); TORRENT_ASSERT(p != 0);
TORRENT_ASSERT(!p->is_local()); TORRENT_ASSERT(!p->is_local());
if (m_state == torrent_status::queued_for_checking
|| m_state == torrent_status::checking_files)
{
p->disconnect("torrent is not ready to accept peers");
return;
}
if (m_ses.m_connections.find(p) == m_ses.m_connections.end()) if (m_ses.m_connections.find(p) == m_ses.m_connections.end())
{ {
p->disconnect("peer is not properly constructed"); p->disconnect("peer is not properly constructed");
@ -2246,7 +2467,9 @@ namespace libtorrent
{ {
return int(m_connections.size()) < m_max_connections return int(m_connections.size()) < m_max_connections
&& m_ses.m_half_open.free_slots() && m_ses.m_half_open.free_slots()
&& !m_paused; && !m_paused
&& m_state != torrent_status::checking_files
&& m_state != torrent_status::queued_for_checking;
} }
void torrent::disconnect_all() void torrent::disconnect_all()
@ -2380,6 +2603,8 @@ namespace libtorrent
, "torrent has finished downloading")); , "torrent has finished downloading"));
} }
m_state = torrent_status::finished;
// disconnect all seeds // disconnect all seeds
// TODO: should disconnect all peers that have the pieces we have // TODO: should disconnect all peers that have the pieces we have
// not just seeds // not just seeds
@ -2414,6 +2639,7 @@ namespace libtorrent
// make the next tracker request // make the next tracker request
// be a completed-event // be a completed-event
m_event = tracker_request::completed; m_event = tracker_request::completed;
m_state = torrent_status::seeding;
force_tracker_request(); force_tracker_request();
} }
@ -2478,10 +2704,10 @@ namespace libtorrent
} }
bool torrent::check_fastresume(aux::piece_checker_data& data) /*
void torrent::check_fastresume()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
TORRENT_ASSERT(valid_metadata()); TORRENT_ASSERT(valid_metadata());
bool done = true; bool done = true;
try try
@ -2492,16 +2718,6 @@ namespace libtorrent
done = m_storage->check_fastresume(data, m_have_pieces, m_num_pieces done = m_storage->check_fastresume(data, m_have_pieces, m_num_pieces
, m_storage_mode, error_msg); , m_storage_mode, error_msg);
if (!error_msg.empty() && m_ses.m_alerts.should_post(alert::warning))
{
m_ses.m_alerts.post_alert(fastresume_rejected_alert(
get_handle(), error_msg));
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
(*m_ses.m_logger) << "fastresume data for "
<< torrent_file().name() << " rejected: "
<< error_msg << "\n";
#endif
}
} }
catch (std::exception& e) catch (std::exception& e)
{ {
@ -2551,30 +2767,21 @@ namespace libtorrent
return progress; return progress;
} }
*/
void torrent::files_checked(std::vector<piece_picker::downloading_piece> const& void torrent::files_checked()
unfinished_pieces)
{ {
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
TORRENT_ASSERT(m_torrent_file->is_valid()); TORRENT_ASSERT(m_torrent_file->is_valid());
INVARIANT_CHECK; INVARIANT_CHECK;
m_state = torrent_status::connecting_to_tracker;
if (!is_seed()) if (!is_seed())
{ {
// this is filled in with pieces that needs to be checked m_picker->init(m_have_pieces);
// against its hashes.
std::vector<int> verify_pieces;
m_picker->files_checked(m_have_pieces, unfinished_pieces, verify_pieces);
if (m_sequential_download) if (m_sequential_download)
picker().sequential_download(m_sequential_download); picker().sequential_download(m_sequential_download);
while (!verify_pieces.empty())
{
int piece = verify_pieces.back();
verify_pieces.pop_back();
async_verify_piece(piece, bind(&torrent::piece_finished
, shared_from_this(), piece, _1));
}
} }
#ifndef TORRENT_DISABLE_EXTENSIONS #ifndef TORRENT_DISABLE_EXTENSIONS
@ -2593,6 +2800,7 @@ namespace libtorrent
if (is_seed()) if (is_seed())
{ {
m_state = torrent_status::seeding;
m_picker.reset(); m_picker.reset();
if (m_ses.settings().free_torrent_hashes) if (m_ses.settings().free_torrent_hashes)
m_torrent_file->seed_free(); m_torrent_file->seed_free();
@ -2634,10 +2842,7 @@ namespace libtorrent
fs::path torrent::save_path() const fs::path torrent::save_path() const
{ {
if (m_owning_storage.get()) return m_save_path;
return m_owning_storage->save_path();
else
return m_save_path;
} }
void torrent::move_storage(fs::path const& save_path) void torrent::move_storage(fs::path const& save_path)
@ -2674,7 +2879,7 @@ namespace libtorrent
torrent_handle torrent::get_handle() const torrent_handle torrent::get_handle() const
{ {
return torrent_handle(&m_ses, &m_checker, m_torrent_file->info_hash()); return torrent_handle(&m_ses, m_torrent_file->info_hash());
} }
session_settings const& torrent::settings() const session_settings const& torrent::settings() const
@ -2779,17 +2984,6 @@ namespace libtorrent
complete = false; complete = false;
break; break;
} }
// this is no longer valid since the completion event
// may be queued in the io service
/*
if (complete && m_files_checked)
{
disk_io_job ret = m_ses.m_disk_thread.find_job(
m_owning_storage, -1, i->index);
TORRENT_ASSERT(ret.action == disk_io_job::hash || ret.action == disk_io_job::write);
TORRENT_ASSERT(ret.piece == i->index);
}
*/
} }
} }
@ -3100,9 +3294,8 @@ namespace libtorrent
void torrent::on_piece_verified(int ret, disk_io_job const& j void torrent::on_piece_verified(int ret, disk_io_job const& j
, boost::function<void(bool)> f) , boost::function<void(bool)> f)
{ {
sha1_hash h(j.str);
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
f(m_torrent_file->hash_for_piece(j.piece) == h); f(ret == 0);
} }
const tcp::endpoint& torrent::current_tracker() const const tcp::endpoint& torrent::current_tracker() const
@ -3110,9 +3303,6 @@ namespace libtorrent
return m_tracker_address; return m_tracker_address;
} }
bool torrent::is_allocating() const
{ return m_owning_storage.get() && m_owning_storage->is_allocating(); }
void torrent::file_progress(std::vector<float>& fp) const void torrent::file_progress(std::vector<float>& fp) const
{ {
TORRENT_ASSERT(valid_metadata()); TORRENT_ASSERT(valid_metadata());
@ -3217,6 +3407,8 @@ namespace libtorrent
st.connections_limit = m_max_connections; st.connections_limit = m_max_connections;
// if we don't have any metadata, stop here // if we don't have any metadata, stop here
st.state = m_state;
if (!valid_metadata()) if (!valid_metadata())
{ {
if (m_got_tracker_response == false) if (m_got_tracker_response == false)
@ -3224,14 +3416,8 @@ namespace libtorrent
else else
st.state = torrent_status::downloading_metadata; st.state = torrent_status::downloading_metadata;
// TODO: add a progress member to the torrent that will be used in this case st.progress = m_progress;
// and that may be set by a plugin
// if (m_metadata_size == 0) st.progress = 0.f;
// else st.progress = (std::min)(1.f, m_metadata_progress / (float)m_metadata_size);
st.progress = 0.f;
st.block_size = 0; st.block_size = 0;
return st; return st;
} }
@ -3258,31 +3444,14 @@ namespace libtorrent
TORRENT_ASSERT(st.total_wanted >= st.total_wanted_done); TORRENT_ASSERT(st.total_wanted >= st.total_wanted_done);
if (st.total_wanted == 0) st.progress = 1.f; if (m_state == torrent_status::checking_files)
st.progress = m_progress;
else if (st.total_wanted == 0) st.progress = 1.f;
else st.progress = st.total_wanted_done else st.progress = st.total_wanted_done
/ static_cast<double>(st.total_wanted); / static_cast<double>(st.total_wanted);
st.pieces = &m_have_pieces; st.pieces = &m_have_pieces;
st.num_pieces = m_num_pieces; st.num_pieces = m_num_pieces;
if (m_got_tracker_response == false)
{
st.state = torrent_status::connecting_to_tracker;
}
else if (is_seed())
{
TORRENT_ASSERT(st.total_done == m_torrent_file->total_size());
st.state = torrent_status::seeding;
}
else if (st.total_wanted_done == st.total_wanted)
{
st.state = torrent_status::finished;
}
else
{
st.state = torrent_status::downloading;
}
st.num_seeds = num_seeds(); st.num_seeds = num_seeds();
if (m_picker.get()) if (m_picker.get())
st.distributed_copies = m_picker->distributed_copies(); st.distributed_copies = m_picker->distributed_copies();

View File

@ -82,58 +82,46 @@ using libtorrent::aux::session_impl;
#define TORRENT_FORWARD(call) \ #define TORRENT_FORWARD(call) \
if (m_ses == 0) return; \ if (m_ses == 0) return; \
TORRENT_ASSERT(m_chk); \
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \ session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \
mutex::scoped_lock l2(m_chk->m_mutex); \ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock(); \
torrent* t = find_torrent(m_ses, m_chk, m_info_hash); \ if (!t) return; \
if (t == 0) return; \
t->call t->call
#define TORRENT_FORWARD_RETURN(call, def) \ #define TORRENT_FORWARD_RETURN(call, def) \
if (m_ses == 0) return def; \ if (m_ses == 0) return def; \
TORRENT_ASSERT(m_chk); \
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \ session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \
mutex::scoped_lock l2(m_chk->m_mutex); \ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock(); \
torrent* t = find_torrent(m_ses, m_chk, m_info_hash); \ if (!t) return def; \
if (t == 0) return def; \
return t->call return t->call
#define TORRENT_FORWARD_RETURN2(call, def) \ #define TORRENT_FORWARD_RETURN2(call, def) \
if (m_ses == 0) return def; \ if (m_ses == 0) return def; \
TORRENT_ASSERT(m_chk); \
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \ session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \
mutex::scoped_lock l2(m_chk->m_mutex); \ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock(); \
torrent* t = find_torrent(m_ses, m_chk, m_info_hash); \ if (!t) return def; \
if (t == 0) return def; \
t->call t->call
#else #else
#define TORRENT_FORWARD(call) \ #define TORRENT_FORWARD(call) \
if (m_ses == 0) throw_invalid_handle(); \ if (m_ses == 0) throw_invalid_handle(); \
TORRENT_ASSERT(m_chk); \
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \ session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \
mutex::scoped_lock l2(m_chk->m_mutex); \ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock(); \
torrent* t = find_torrent(m_ses, m_chk, m_info_hash); \ if (!t) throw_invalid_handle(); \
if (t == 0) throw_invalid_handle(); \
t->call t->call
#define TORRENT_FORWARD_RETURN(call, def) \ #define TORRENT_FORWARD_RETURN(call, def) \
if (m_ses == 0) throw_invalid_handle(); \ if (m_ses == 0) throw_invalid_handle(); \
TORRENT_ASSERT(m_chk); \
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \ session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \
mutex::scoped_lock l2(m_chk->m_mutex); \ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock(); \
torrent* t = find_torrent(m_ses, m_chk, m_info_hash); \ if (!t) return def; \
if (t == 0) return def; \
return t->call return t->call
#define TORRENT_FORWARD_RETURN2(call, def) \ #define TORRENT_FORWARD_RETURN2(call, def) \
if (m_ses == 0) throw_invalid_handle(); \ if (m_ses == 0) throw_invalid_handle(); \
TORRENT_ASSERT(m_chk); \
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \ session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); \
mutex::scoped_lock l2(m_chk->m_mutex); \ boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock(); \
torrent* t = find_torrent(m_ses, m_chk, m_info_hash); \ if (!t) return def; \
if (t == 0) return def; \
t->call t->call
#endif #endif
@ -150,26 +138,12 @@ namespace libtorrent
throw invalid_handle(); throw invalid_handle();
} }
#endif #endif
torrent* find_torrent(
session_impl* ses
, aux::checker_impl* chk
, sha1_hash const& hash)
{
aux::piece_checker_data* d = chk->find_torrent(hash);
if (d != 0) return d->torrent_ptr.get();
boost::shared_ptr<torrent> t = ses->find_torrent(hash).lock();
if (t) return t.get();
return 0;
}
} }
#ifndef NDEBUG #ifndef NDEBUG
void torrent_handle::check_invariant() const void torrent_handle::check_invariant() const
{ {
TORRENT_ASSERT((m_ses == 0 && m_chk == 0) || (m_ses != 0 && m_chk != 0));
} }
#endif #endif
@ -302,30 +276,8 @@ namespace libtorrent
#else #else
throw_invalid_handle(); throw_invalid_handle();
#endif #endif
TORRENT_ASSERT(m_chk);
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
mutex::scoped_lock l2(m_chk->m_mutex);
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
if (d != 0)
{
torrent_status st = d->torrent_ptr->status();
if (d->processing)
{
if (d->torrent_ptr->is_allocating())
st.state = torrent_status::allocating;
else
st.state = torrent_status::checking_files;
}
else
st.state = torrent_status::queued_for_checking;
st.progress = d->progress;
st.paused = d->torrent_ptr->is_paused();
return st;
}
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock(); boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (t) return t->status(); if (t) return t->status();
@ -464,11 +416,9 @@ namespace libtorrent
#else #else
if (m_ses == 0) throw_invalid_handle(); if (m_ses == 0) throw_invalid_handle();
#endif #endif
TORRENT_ASSERT(m_chk);
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex);
mutex::scoped_lock l2(m_chk->m_mutex); boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
torrent* t = find_torrent(m_ses, m_chk, m_info_hash); if (!t || !t->valid_metadata())
if (t == 0 || !t->valid_metadata())
#ifdef BOOST_NO_EXCEPTIONS #ifdef BOOST_NO_EXCEPTIONS
return empty; return empty;
#else #else
@ -481,10 +431,8 @@ namespace libtorrent
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
if (m_ses == 0) return false; if (m_ses == 0) return false;
TORRENT_ASSERT(m_chk);
session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex); session_impl::mutex_t::scoped_lock l1(m_ses->m_mutex);
mutex::scoped_lock l2(m_chk->m_mutex); boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
torrent* t = find_torrent(m_ses, m_chk, m_info_hash);
return t; return t;
} }
@ -498,12 +446,8 @@ namespace libtorrent
#else #else
throw_invalid_handle(); throw_invalid_handle();
#endif #endif
TORRENT_ASSERT(m_chk);
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
mutex::scoped_lock l2(m_chk->m_mutex); boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
torrent* t = find_torrent(m_ses, m_chk, m_info_hash);
if (!t || !t->valid_metadata()) if (!t || !t->valid_metadata())
#ifdef BOOST_NO_EXCEPTIONS #ifdef BOOST_NO_EXCEPTIONS
return entry(); return entry();
@ -518,7 +462,9 @@ namespace libtorrent
ret["file-format"] = "libtorrent resume file"; ret["file-format"] = "libtorrent resume file";
ret["file-version"] = 1; ret["file-version"] = 1;
ret["allocation"] = t->filesystem().compact_allocation()?"compact":"full"; storage_mode_t sm = t->storage_mode();
ret["allocation"] = sm == storage_mode_sparse?"sparse"
:sm == storage_mode_allocate?"full":"compact";
const sha1_hash& info_hash = t->torrent_file().info_hash(); const sha1_hash& info_hash = t->torrent_file().info_hash();
ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end()); ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end());
@ -569,25 +515,13 @@ namespace libtorrent
TORRENT_ASSERT(bits == 8 || j == num_bitmask_bytes - 1); TORRENT_ASSERT(bits == 8 || j == num_bitmask_bytes - 1);
} }
piece_struct["bitmask"] = bitmask; piece_struct["bitmask"] = bitmask;
/*
TORRENT_ASSERT(t->filesystem().slot_for(i->index) >= 0);
unsigned long adler
= t->filesystem().piece_crc(
t->filesystem().slot_for(i->index)
, t->block_size()
, i->info);
piece_struct["adler32"] = adler;
*/
// push the struct onto the unfinished-piece list // push the struct onto the unfinished-piece list
up.push_back(piece_struct); up.push_back(piece_struct);
} }
} }
std::vector<int> piece_index; std::vector<int> piece_index;
t->filesystem().export_piece_map(piece_index, have_pieces); t->filesystem().write_resume_data(ret, have_pieces);
entry::list_type& slots = ret["slots"].list();
std::copy(piece_index.begin(), piece_index.end(), std::back_inserter(slots));
// write local peers // write local peers
@ -632,8 +566,6 @@ namespace libtorrent
peer_list.push_back(peer); peer_list.push_back(peer);
} }
t->filesystem().write_resume_data(ret);
return ret; return ret;
} }
@ -654,27 +586,16 @@ namespace libtorrent
#else #else
throw_invalid_handle(); throw_invalid_handle();
#endif #endif
TORRENT_ASSERT(m_chk);
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock(); boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) if (!t)
{ {
// the torrent is being checked. Add the peer to its
// peer list. The entries in there will be connected
// once the checking is complete.
mutex::scoped_lock l2(m_chk->m_mutex);
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
if (d == 0)
#ifdef BOOST_NO_EXCEPTIONS #ifdef BOOST_NO_EXCEPTIONS
return;
#else
throw_invalid_handle();
#endif
d->peers.push_back(adr);
return; return;
#else
throw_invalid_handle();
#endif
} }
peer_id id; peer_id id;