mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-08-15 14:42:30 +00:00
* Removed core::HashQueue, using the "hashing" view and a function in
core::Manager instead. * Added a new :rtorrent:hashing state so we can distinguish between initial hashing and manual rehashes. The former is cleared if the download is paused. * Added a hash failed flag to core::Download. This is used to skip entries in the hashing view that either failed to open or are in the process of being erased from DownloadList. * torrent::Bitfield::unset was incrementing, rather than decrementing, the m_set counter. * Replaced all torrent::internal_error's in rtorrent with torrent::client_error. * ^D now checks :rtorrent:state to decide whetever to stop or erase a download. Downloads queued for hashing will now stop rather than be deleted. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@688 e378c898-3ddf-0310-93e7-cc216c733640
This commit is contained in:
@@ -85,10 +85,10 @@ CommandScheduler::erase(iterator itr) {
|
||||
void
|
||||
CommandScheduler::call_item(value_type item) {
|
||||
if (item->is_queued())
|
||||
throw torrent::internal_error("CommandScheduler::call_item(...) called but item is still queued.");
|
||||
throw torrent::client_error("CommandScheduler::call_item(...) called but item is still queued.");
|
||||
|
||||
if (std::find(begin(), end(), item) == end())
|
||||
throw torrent::internal_error("CommandScheduler::call_item(...) called but the item isn't in the scheduler.");
|
||||
throw torrent::client_error("CommandScheduler::call_item(...) called but the item isn't in the scheduler.");
|
||||
|
||||
// Remove the item before calling the command if it should be
|
||||
// removed.
|
||||
@@ -110,7 +110,7 @@ CommandScheduler::call_item(value_type item) {
|
||||
}
|
||||
|
||||
if (next <= cachedTime)
|
||||
throw torrent::internal_error("CommandScheduler::call_item(...) tried to schedule a zero interval item.");
|
||||
throw torrent::client_error("CommandScheduler::call_item(...) tried to schedule a zero interval item.");
|
||||
|
||||
item->enable(next);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ CommandSchedulerItem::~CommandSchedulerItem() {
|
||||
void
|
||||
CommandSchedulerItem::enable(rak::timer t) {
|
||||
if (t == rak::timer())
|
||||
throw torrent::internal_error("CommandSchedulerItem::enable() t == rak::timer().");
|
||||
throw torrent::client_error("CommandSchedulerItem::enable() t == rak::timer().");
|
||||
|
||||
if (is_queued())
|
||||
disable();
|
||||
@@ -71,7 +71,7 @@ CommandSchedulerItem::next_time_scheduled() const {
|
||||
return rak::timer();
|
||||
|
||||
if (m_timeScheduled == rak::timer())
|
||||
throw torrent::internal_error("CommandSchedulerItem::next_time_scheduled() m_timeScheduled == rak::timer().");
|
||||
throw torrent::client_error("CommandSchedulerItem::next_time_scheduled() m_timeScheduled == rak::timer().");
|
||||
|
||||
rak::timer next = m_timeScheduled;
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ Control::initialize() {
|
||||
m_core->listen_open();
|
||||
m_core->download_store()->enable(m_variables->get_value("session_lock"));
|
||||
|
||||
m_core->set_hashing_view(*m_viewManager->find_throw("hashing"));
|
||||
m_scheduler->set_view(*m_viewManager->find_throw("scheduler"));
|
||||
|
||||
m_ui->init(this);
|
||||
|
||||
@@ -14,8 +14,6 @@ libsub_core_a_SOURCES = \
|
||||
download_slot_map.h \
|
||||
download_store.cc \
|
||||
download_store.h \
|
||||
hash_queue.cc \
|
||||
hash_queue.h \
|
||||
http_queue.cc \
|
||||
http_queue.h \
|
||||
log.cc \
|
||||
|
||||
@@ -57,6 +57,8 @@ Download::Download(download_type d) :
|
||||
m_fileList(d.file_list()),
|
||||
m_trackerList(d.tracker_list()),
|
||||
|
||||
m_hashFailed(false),
|
||||
|
||||
m_chunksFailed(0) {
|
||||
|
||||
m_connTrackerSucceded = m_download.signal_tracker_succeded(sigc::bind(sigc::mem_fun(*this, &Download::receive_tracker_msg), ""));
|
||||
|
||||
+14
-2
@@ -56,15 +56,25 @@ public:
|
||||
typedef utils::VariableMap variable_map_type;
|
||||
|
||||
static const int64_t variable_hashing_stopped = 0;
|
||||
static const int64_t variable_hashing_started = 1;
|
||||
static const int64_t variable_hashing_initial = 1;
|
||||
static const int64_t variable_hashing_last = 2;
|
||||
static const int64_t variable_hashing_rehash = 3;
|
||||
|
||||
Download(download_type d);
|
||||
~Download();
|
||||
|
||||
bool is_open() const { return m_download.is_open(); }
|
||||
bool is_active() const { return m_download.is_active(); }
|
||||
inline bool is_done() const { return m_download.chunks_done() == m_download.chunks_total(); }
|
||||
bool is_done() const { return m_download.chunks_done() == m_download.chunks_total(); }
|
||||
|
||||
// FIXME: Fixed a bug in libtorrent that caused is_hash_checked to
|
||||
// return true when the torrent is closed. Remove this redundant
|
||||
// test in the next milestone.
|
||||
bool is_hash_checked() const { return m_download.is_open() && m_download.is_hash_checked(); }
|
||||
bool is_hash_checking() const { return m_download.is_hash_checking(); }
|
||||
|
||||
bool is_hash_failed() const { return m_hashFailed; }
|
||||
void set_hash_failed(bool v) { m_hashFailed = v; }
|
||||
|
||||
variable_map_type* variable() { return &m_variables; }
|
||||
std::string variable_string(const std::string& key) { return m_variables.get_string(key); }
|
||||
@@ -127,6 +137,8 @@ private:
|
||||
file_list_type m_fileList;
|
||||
tracker_list_type m_trackerList;
|
||||
|
||||
bool m_hashFailed;
|
||||
|
||||
std::string m_message;
|
||||
uint32_t m_chunksFailed;
|
||||
|
||||
|
||||
+66
-56
@@ -47,7 +47,6 @@
|
||||
#include "rak/functional.h"
|
||||
|
||||
#include "globals.h"
|
||||
#include "hash_queue.h"
|
||||
#include "manager.h"
|
||||
|
||||
#include "download.h"
|
||||
@@ -60,7 +59,7 @@ inline void
|
||||
DownloadList::check_contains(Download* d) {
|
||||
#ifdef USE_EXTRA_DEBUG
|
||||
if (std::find(begin(), end(), d) == end())
|
||||
throw torrent::internal_error("DownloadList::check_contains(...) failed.");
|
||||
throw torrent::client_error("DownloadList::check_contains(...) failed.");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -121,12 +120,14 @@ DownloadList::insert(Download* download) {
|
||||
|
||||
try {
|
||||
(*itr)->download()->signal_download_done(sigc::bind(sigc::mem_fun(*this, &DownloadList::received_finished), download));
|
||||
(*itr)->download()->signal_hash_done(sigc::bind(sigc::mem_fun(*this, &DownloadList::hash_done), download));
|
||||
|
||||
std::for_each(slot_map_insert().begin(), slot_map_insert().end(), download_list_call(*itr));
|
||||
|
||||
} catch (torrent::local_error& e) {
|
||||
// Should perhaps relax this, just print an error and remove the
|
||||
// downloads?
|
||||
throw torrent::internal_error("Caught during DownloadList::insert(...): " + std::string(e.what()));
|
||||
throw torrent::client_error("Caught during DownloadList::insert(...): " + std::string(e.what()));
|
||||
}
|
||||
|
||||
return itr;
|
||||
@@ -142,7 +143,10 @@ DownloadList::erase(Download* download) {
|
||||
DownloadList::iterator
|
||||
DownloadList::erase(iterator itr) {
|
||||
if (itr == end())
|
||||
throw torrent::internal_error("DownloadList::erase(...) could not find download.");
|
||||
throw torrent::client_error("DownloadList::erase(...) could not find download.");
|
||||
|
||||
// Makes sure close doesn't restart hashing of this download.
|
||||
(*itr)->set_hash_failed(true);
|
||||
|
||||
close(*itr);
|
||||
|
||||
@@ -199,10 +203,12 @@ void
|
||||
DownloadList::close_throw(Download* download) {
|
||||
check_contains(download);
|
||||
|
||||
if (!download->download()->is_open())
|
||||
if (!download->is_open())
|
||||
return;
|
||||
|
||||
if (download->download()->is_active())
|
||||
// When pause gets called it will clear the initial hash checking.
|
||||
|
||||
if (download->is_active())
|
||||
pause(download);
|
||||
|
||||
// Save the torrent on close, this covers shutdown and if a torrent
|
||||
@@ -214,18 +220,32 @@ DownloadList::close_throw(Download* download) {
|
||||
// Reconsider this save. Should be done explicitly when shutting down.
|
||||
//control->core()->download_store()->save(download);
|
||||
|
||||
if (control->core()->hash_queue()->is_queued(download)) {
|
||||
control->core()->hash_queue()->remove(download);
|
||||
download->download()->close();
|
||||
// FIXME: Urgh, need to do something sane when closing a download
|
||||
// that has been queued for hashing. ATM just close the torrent and
|
||||
// call the hash_removed slots. This might open and restart the
|
||||
// hashing of this torrent.
|
||||
|
||||
// Hash removed slot must be called after close as we can't atm
|
||||
// stop already started hash checks except through close.
|
||||
std::for_each(slot_map_hash_removed().begin(), slot_map_hash_removed().end(), download_list_call(download));
|
||||
// if (download->is_hash_checking()) {
|
||||
// download->download()->close();
|
||||
|
||||
} else {
|
||||
download->download()->close();
|
||||
}
|
||||
// // Hash removed slot must be called after close as we can't atm
|
||||
// // stop already started hash checks except through close.
|
||||
// std::for_each(slot_map_hash_removed().begin(), slot_map_hash_removed().end(), download_list_call(download));
|
||||
|
||||
// } else {
|
||||
// download->download()->close();
|
||||
// }
|
||||
|
||||
// But this isn't correct either, i think... ATM do the borking
|
||||
// thing, the hash queue won't be updated. Need to properly handle
|
||||
// erasing a download.
|
||||
|
||||
// Propably should do its own thingie in erase rather than calling
|
||||
// close.
|
||||
|
||||
download->download()->close();
|
||||
|
||||
std::for_each(slot_map_hash_removed().begin(), slot_map_hash_removed().end(), download_list_call(download));
|
||||
std::for_each(slot_map_close().begin(), slot_map_close().end(), download_list_call(download));
|
||||
}
|
||||
|
||||
@@ -256,34 +276,27 @@ DownloadList::resume(Download* download) {
|
||||
if (download->download()->is_active())
|
||||
return;
|
||||
|
||||
// Properly escape when resume get's called during hashing. The
|
||||
// 'state' is changed by the call to DownloadList::start so it
|
||||
// will automagically start afterwards.
|
||||
if (control->core()->hash_queue()->is_queued(download))
|
||||
return;
|
||||
|
||||
download->variable()->set("state_changed", cachedTime.seconds());
|
||||
|
||||
open_throw(download);
|
||||
|
||||
// Manual or end-of-download rehashing clears the resume data so
|
||||
// we can just start the hashing again without clearing it again.
|
||||
//
|
||||
// It is also assumed the is_hash_checked flag gets cleared when
|
||||
// 'hashing' was set.
|
||||
if (!download->download()->is_hash_checked()) {
|
||||
if (!download->is_hash_checked()) {
|
||||
download->set_hash_failed(false);
|
||||
|
||||
// Set 'hashing' to started if hashing wasn't started, else keep
|
||||
// the old value.
|
||||
if (download->variable()->get_value("hashing") == Download::variable_hashing_stopped)
|
||||
download->variable()->set("hashing", Download::variable_hashing_started);
|
||||
download->variable()->set("hashing", Download::variable_hashing_initial);
|
||||
|
||||
control->core()->hash_queue()->insert(download);
|
||||
std::for_each(slot_map_hash_queued().begin(), slot_map_hash_queued().end(), download_list_call(download));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
download->variable()->set("state_changed", cachedTime.seconds());
|
||||
|
||||
open_throw(download);
|
||||
|
||||
if (download->is_done()) {
|
||||
download->set_connection_type(download->variable()->get_string("connection_seed"));
|
||||
} else {
|
||||
@@ -314,11 +327,10 @@ DownloadList::pause(Download* download) {
|
||||
|
||||
try {
|
||||
|
||||
if (control->core()->hash_queue()->is_queued(download)) {
|
||||
control->core()->hash_queue()->remove(download);
|
||||
// Don't stop if we're doing the final hashing.
|
||||
if (download->variable()->get_value("hashing") == Download::variable_hashing_initial) {
|
||||
download->variable()->set("hashing", Download::variable_hashing_stopped);
|
||||
|
||||
// Hash removed slot must be called after close as we can't atm
|
||||
// stop already started hash checks except through close.
|
||||
std::for_each(slot_map_hash_removed().begin(), slot_map_hash_removed().end(), download_list_call(download));
|
||||
}
|
||||
|
||||
@@ -347,37 +359,20 @@ DownloadList::check_hash(Download* download) {
|
||||
|
||||
try {
|
||||
|
||||
download->variable()->set("hashing", Download::variable_hashing_started);
|
||||
check_hash_throw(download);
|
||||
download->variable()->set("hashing", Download::variable_hashing_rehash);
|
||||
hash_clear(download);
|
||||
|
||||
} catch (torrent::local_error& e) {
|
||||
control->core()->push_log(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// Throw in addition to not setting 'hashing'.
|
||||
void
|
||||
DownloadList::check_hash_throw(Download* download) {
|
||||
check_contains(download);
|
||||
|
||||
close_throw(download);
|
||||
download->download()->hash_resume_clear();
|
||||
open_throw(download);
|
||||
|
||||
// If any more stuff is added here, make sure resume etc are still
|
||||
// correct.
|
||||
control->core()->hash_queue()->insert(download);
|
||||
std::for_each(slot_map_hash_queued().begin(), slot_map_hash_queued().end(), download_list_call(download));
|
||||
}
|
||||
|
||||
void
|
||||
DownloadList::hash_done(Download* download) {
|
||||
check_contains(download);
|
||||
|
||||
if (!download->download()->is_hash_checked() ||
|
||||
download->download()->is_hash_checking() ||
|
||||
download->download()->is_active())
|
||||
throw torrent::internal_error("DownloadList::hash_done(...) download in invalid state.");
|
||||
if (!download->is_hash_checked() || download->is_hash_checking() || download->is_active())
|
||||
throw torrent::client_error("DownloadList::hash_done(...) download in invalid state.");
|
||||
|
||||
// Need to find some sane conditional here. Can we check the total
|
||||
// downloaded to ensure something was transferred, thus we didn't
|
||||
@@ -392,7 +387,8 @@ DownloadList::hash_done(Download* download) {
|
||||
download->variable()->set("hashing", Download::variable_hashing_stopped);
|
||||
|
||||
switch (hashing) {
|
||||
case Download::variable_hashing_started:
|
||||
case Download::variable_hashing_initial:
|
||||
case Download::variable_hashing_rehash:
|
||||
// Normal re/hashing.
|
||||
|
||||
if (download->is_done())
|
||||
@@ -432,6 +428,20 @@ DownloadList::hash_done(Download* download) {
|
||||
std::for_each(slot_map_hash_done().begin(), slot_map_hash_done().end(), download_list_call(download));
|
||||
}
|
||||
|
||||
void
|
||||
DownloadList::hash_clear(Download* download) {
|
||||
check_contains(download);
|
||||
|
||||
close_throw(download);
|
||||
download->download()->hash_resume_clear();
|
||||
|
||||
download->set_hash_failed(false);
|
||||
|
||||
// If any more stuff is added here, make sure resume etc are still
|
||||
// correct.
|
||||
std::for_each(slot_map_hash_queued().begin(), slot_map_hash_queued().end(), download_list_call(download));
|
||||
}
|
||||
|
||||
void
|
||||
DownloadList::received_finished(Download* download) {
|
||||
check_contains(download);
|
||||
@@ -441,7 +451,7 @@ DownloadList::received_finished(Download* download) {
|
||||
// trigger correctly, also so it can bork on missing data.
|
||||
|
||||
download->variable()->set("hashing", Download::variable_hashing_last);
|
||||
check_hash_throw(download);
|
||||
hash_clear(download);
|
||||
|
||||
} else {
|
||||
confirm_finished(download);
|
||||
|
||||
@@ -100,9 +100,6 @@ public:
|
||||
void pause(Download* d);
|
||||
|
||||
void check_hash(Download* d);
|
||||
void check_hash_throw(Download* d);
|
||||
|
||||
void hash_done(Download* d);
|
||||
|
||||
enum {
|
||||
SLOTS_INSERT,
|
||||
@@ -171,6 +168,9 @@ public:
|
||||
static void erase_key(slot_map& sm, const std::string& key) { sm.erase(key); }
|
||||
|
||||
private:
|
||||
void hash_done(Download* d);
|
||||
void hash_clear(Download* d);
|
||||
|
||||
inline void check_contains(Download* d);
|
||||
|
||||
void received_finished(Download* d);
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
// rTorrent - BitTorrent client
|
||||
// Copyright (C) 2005-2006, Jari Sundell
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for
|
||||
// all of the code used other than OpenSSL. If you modify file(s)
|
||||
// with this exception, you may extend this exception to your version
|
||||
// of the file(s), but you are not obligated to do so. If you do not
|
||||
// wish to do so, delete this exception statement from your version.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <sigc++/bind.h>
|
||||
|
||||
#include "download.h"
|
||||
#include "download_list.h"
|
||||
#include "rak/functional.h"
|
||||
#include "hash_queue.h"
|
||||
|
||||
namespace core {
|
||||
|
||||
bool
|
||||
HashQueue::is_queued(Download* download) const {
|
||||
return
|
||||
download->download()->is_hash_checking() ||
|
||||
std::find_if(begin(), end(), rak::equal(download, std::mem_fun(&HashQueueNode::download))) != end();
|
||||
}
|
||||
|
||||
void
|
||||
HashQueue::insert(Download* download) {
|
||||
if (download->download()->is_hash_checking() || find(download) != end())
|
||||
return;
|
||||
|
||||
if (download->download()->is_hash_checked()) {
|
||||
m_downloadList->hash_done(download);
|
||||
return;
|
||||
}
|
||||
|
||||
if (find(download) != end())
|
||||
throw torrent::internal_error("HashQueue::insert(...) download already in queue.");
|
||||
|
||||
iterator itr = Base::insert(end(), new HashQueueNode(download));
|
||||
|
||||
(*itr)->set_connection(download->download()->signal_hash_done(sigc::bind(sigc::mem_fun(*this, &HashQueue::receive_hash_done), download)));
|
||||
|
||||
fill_queue();
|
||||
}
|
||||
|
||||
void
|
||||
HashQueue::remove(Download* d) {
|
||||
iterator itr = find(d);
|
||||
|
||||
if (itr == end())
|
||||
return;
|
||||
|
||||
// We don't do anything if we're already checking, just disconnect.
|
||||
// if ((*itr)->download()->download()->is_hash_checking()) {
|
||||
// // What do we do if we're already checking?
|
||||
// }
|
||||
|
||||
delete *itr;
|
||||
Base::erase(itr);
|
||||
|
||||
fill_queue();
|
||||
}
|
||||
|
||||
HashQueue::iterator
|
||||
HashQueue::find(Download* d) {
|
||||
return std::find_if(begin(), end(), rak::equal(d, std::mem_fun(&HashQueueNode::download)));
|
||||
}
|
||||
|
||||
void
|
||||
HashQueue::receive_hash_done(Download* d) {
|
||||
iterator itr = find(d);
|
||||
|
||||
if (itr == end())
|
||||
return;
|
||||
|
||||
delete *itr;
|
||||
Base::erase(itr);
|
||||
|
||||
m_downloadList->hash_done(d);
|
||||
fill_queue();
|
||||
}
|
||||
|
||||
void
|
||||
HashQueue::fill_queue() {
|
||||
if (empty() || front()->download()->download()->is_hash_checking())
|
||||
return;
|
||||
|
||||
if (front()->download()->download()->is_hash_checked())
|
||||
throw std::logic_error("core::HashQueue::fill_queue() encountered a checked hash");
|
||||
|
||||
front()->download()->download()->hash_check();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
// rTorrent - BitTorrent client
|
||||
// Copyright (C) 2005-2006, Jari Sundell
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations
|
||||
// including the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for
|
||||
// all of the code used other than OpenSSL. If you modify file(s)
|
||||
// with this exception, you may extend this exception to your version
|
||||
// of the file(s), but you are not obligated to do so. If you do not
|
||||
// wish to do so, delete this exception statement from your version.
|
||||
// If you delete this exception statement from all source files in the
|
||||
// program, then also delete it here.
|
||||
//
|
||||
// Contact: Jari Sundell <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#ifndef RTORRENT_CORE_HASH_QUEUE_H
|
||||
#define RTORRENT_CORE_HASH_QUEUE_H
|
||||
|
||||
#include <list>
|
||||
#include <sigc++/slot.h>
|
||||
#include <sigc++/connection.h>
|
||||
|
||||
namespace core {
|
||||
|
||||
class Download;
|
||||
class DownloadList;
|
||||
|
||||
class HashQueueNode;
|
||||
|
||||
class HashQueue : private std::list<HashQueueNode*> {
|
||||
public:
|
||||
typedef std::list<HashQueueNode*> Base;
|
||||
|
||||
using Base::iterator;
|
||||
using Base::const_iterator;
|
||||
using Base::reverse_iterator;
|
||||
using Base::const_reverse_iterator;
|
||||
|
||||
using Base::begin;
|
||||
using Base::end;
|
||||
using Base::rbegin;
|
||||
using Base::rend;
|
||||
|
||||
using Base::front;
|
||||
using Base::back;
|
||||
|
||||
using Base::empty;
|
||||
using Base::size;
|
||||
|
||||
// Replace 'dl' with a slot.
|
||||
HashQueue(DownloadList* dl) : m_downloadList(dl) {}
|
||||
|
||||
bool is_queued(Download* download) const;
|
||||
|
||||
void insert(Download* d);
|
||||
|
||||
// It's safe to try to remove downloads not in the queue. The hash
|
||||
// checking is not stopped if it has already started.
|
||||
void remove(Download* d);
|
||||
|
||||
iterator find(Download* d);
|
||||
|
||||
private:
|
||||
void receive_hash_done(Download* d);
|
||||
|
||||
void fill_queue();
|
||||
|
||||
DownloadList* m_downloadList;
|
||||
};
|
||||
|
||||
class HashQueueNode {
|
||||
public:
|
||||
HashQueueNode(Download* d) : m_download(d) {}
|
||||
~HashQueueNode() { disconnect(); }
|
||||
|
||||
void disconnect() { m_connection.disconnect(); }
|
||||
Download* download() { return m_download; }
|
||||
|
||||
void set_connection(sigc::connection c) { m_connection = c; }
|
||||
|
||||
private:
|
||||
Download* m_download;
|
||||
sigc::connection m_connection;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+40
-3
@@ -60,11 +60,11 @@
|
||||
#include "download.h"
|
||||
#include "download_factory.h"
|
||||
#include "download_store.h"
|
||||
#include "hash_queue.h"
|
||||
#include "http_queue.h"
|
||||
#include "manager.h"
|
||||
#include "poll_manager_epoll.h"
|
||||
#include "poll_manager_select.h"
|
||||
#include "view.h"
|
||||
|
||||
namespace core {
|
||||
|
||||
@@ -114,6 +114,8 @@ delete_tied(Download* d) {
|
||||
}
|
||||
|
||||
Manager::Manager() :
|
||||
m_hashingView(NULL),
|
||||
|
||||
m_pollManager(NULL),
|
||||
m_portFirst(6890),
|
||||
m_portLast(6999) {
|
||||
@@ -122,17 +124,24 @@ Manager::Manager() :
|
||||
m_downloadList = new DownloadList();
|
||||
|
||||
m_httpQueue = new HttpQueue();
|
||||
m_hashQueue = new HashQueue(m_downloadList);
|
||||
}
|
||||
|
||||
Manager::~Manager() {
|
||||
delete m_downloadList;
|
||||
delete m_hashQueue;
|
||||
|
||||
delete m_downloadStore;
|
||||
delete m_httpQueue;
|
||||
}
|
||||
|
||||
void
|
||||
Manager::set_hashing_view(View* v) {
|
||||
if (v == NULL || m_hashingView != NULL)
|
||||
throw torrent::client_error("Manager::set_hashing_view(...) received NULL or is already set.");
|
||||
|
||||
m_hashingView = v;
|
||||
v->signal_changed().connect(sigc::mem_fun(this, &Manager::receive_hashing_changed));
|
||||
}
|
||||
|
||||
void
|
||||
Manager::initialize_first() {
|
||||
if ((m_pollManager = PollManagerEPoll::create(sysconf(_SC_OPEN_MAX))) != NULL)
|
||||
@@ -360,4 +369,32 @@ Manager::try_create_download_expand(const std::string& uri, bool start, bool pri
|
||||
try_create_download(uri, start, printLog, tied);
|
||||
}
|
||||
|
||||
// DownloadList's hashing related functions don't actually start the
|
||||
// hashing, it only reacts to events. This functions checks the
|
||||
// hashing view and starts hashing if nessesary.
|
||||
void
|
||||
Manager::receive_hashing_changed() {
|
||||
if (m_hashingView->empty_visible() ||
|
||||
std::find_if(m_hashingView->begin_visible(), m_hashingView->end_visible(), std::mem_fun(&Download::is_hash_checking)) != m_hashingView->end_visible())
|
||||
return;
|
||||
|
||||
for (View::iterator itr = m_hashingView->begin_visible(), last = m_hashingView->end_visible(); itr != last; ++itr) {
|
||||
|
||||
try {
|
||||
|
||||
if ((*itr)->is_hash_checked())
|
||||
throw torrent::client_error("core::Manager::receive_hashing_changed() hash already checked.");
|
||||
|
||||
m_downloadList->open_throw(*itr);
|
||||
(*itr)->download()->hash_check();
|
||||
|
||||
return;
|
||||
|
||||
} catch (torrent::local_error& e) {
|
||||
(*itr)->set_hash_failed(true);
|
||||
push_log(e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-3
@@ -50,8 +50,8 @@ namespace torrent {
|
||||
namespace core {
|
||||
|
||||
class DownloadStore;
|
||||
class HashQueue;
|
||||
class HttpQueue;
|
||||
class View;
|
||||
|
||||
class Manager {
|
||||
public:
|
||||
@@ -65,9 +65,11 @@ public:
|
||||
DownloadList* download_list() { return m_downloadList; }
|
||||
DownloadStore* download_store() { return m_downloadStore; }
|
||||
|
||||
HashQueue* hash_queue() { return m_hashQueue; }
|
||||
HttpQueue* http_queue() { return m_httpQueue; }
|
||||
|
||||
View* hashing_view() { return m_hashingView; }
|
||||
void set_hashing_view(View* v);
|
||||
|
||||
PollManager* get_poll_manager() { return m_pollManager; }
|
||||
Log& get_log_important() { return m_logImportant; }
|
||||
Log& get_log_complete() { return m_logComplete; }
|
||||
@@ -103,11 +105,14 @@ private:
|
||||
|
||||
void receive_http_failed(std::string msg);
|
||||
|
||||
void receive_hashing_changed();
|
||||
|
||||
DownloadList* m_downloadList;
|
||||
DownloadStore* m_downloadStore;
|
||||
HashQueue* m_hashQueue;
|
||||
HttpQueue* m_httpQueue;
|
||||
|
||||
View* m_hashingView;
|
||||
|
||||
PollManager* m_pollManager;
|
||||
Log m_logImportant;
|
||||
Log m_logComplete;
|
||||
|
||||
@@ -82,7 +82,7 @@ Scheduler::update() {
|
||||
|
||||
for (View::iterator itr = m_view->begin_visible(), last = m_view->end_visible(); curActive > target; ++itr) {
|
||||
if (itr == last)
|
||||
throw torrent::internal_error("Scheduler::update() loop bork.");
|
||||
throw torrent::client_error("Scheduler::update() loop bork.");
|
||||
|
||||
if ((*itr)->is_active()) {
|
||||
m_downloadList->pause(*itr);
|
||||
@@ -94,7 +94,7 @@ Scheduler::update() {
|
||||
|
||||
for (View::iterator itr = m_view->begin_visible(), last = m_view->end_visible(); curActive < m_maxActive; ++itr) {
|
||||
if (itr == last)
|
||||
throw torrent::internal_error("Scheduler::update() loop bork.");
|
||||
throw torrent::client_error("Scheduler::update() loop bork.");
|
||||
|
||||
if (!(*itr)->is_active()) {
|
||||
m_downloadList->resume(*itr);
|
||||
|
||||
+8
-8
@@ -60,15 +60,15 @@ View::~View() {
|
||||
void
|
||||
View::initialize(const std::string& name, core::DownloadList* dlist) {
|
||||
if (!m_name.empty())
|
||||
throw torrent::internal_error("View::initialize(...) called on an already initialized view.");
|
||||
throw torrent::client_error("View::initialize(...) called on an already initialized view.");
|
||||
|
||||
if (name.empty())
|
||||
throw torrent::internal_error("View::initialize(...) called with an empty name.");
|
||||
throw torrent::client_error("View::initialize(...) called with an empty name.");
|
||||
|
||||
std::string key = "0_view_" + name;
|
||||
|
||||
if (dlist->has_slot_insert(key) || dlist->has_slot_erase(key))
|
||||
throw torrent::internal_error("View::initialize(...) duplicate key name found in DownloadList.");
|
||||
throw torrent::client_error("View::initialize(...) duplicate key name found in DownloadList.");
|
||||
|
||||
m_name = name;
|
||||
m_list = dlist;
|
||||
@@ -167,7 +167,7 @@ View::filter() {
|
||||
void
|
||||
View::set_filter_on(int event) {
|
||||
if (event == DownloadList::SLOTS_INSERT || event == DownloadList::SLOTS_ERASE || event >= DownloadList::SLOTS_MAX_SIZE)
|
||||
throw torrent::internal_error("View::filter_on(...) invalid event.");
|
||||
throw torrent::client_error("View::filter_on(...) invalid event.");
|
||||
|
||||
m_list->slots(event)["0_view_" + m_name] = sigc::bind(sigc::mem_fun(this, &View::received), event);
|
||||
}
|
||||
@@ -193,7 +193,7 @@ View::insert_visible(Download* d) {
|
||||
inline void
|
||||
View::erase(iterator itr) {
|
||||
if (itr == end_filtered())
|
||||
throw torrent::internal_error("View::erase_visible(...) iterator out of range.");
|
||||
throw torrent::client_error("View::erase_visible(...) iterator out of range.");
|
||||
|
||||
m_size -= (itr < end_visible());
|
||||
m_focus -= (m_focus > position(itr));
|
||||
@@ -209,7 +209,7 @@ View::received(core::Download* download, int event) {
|
||||
case DownloadList::SLOTS_INSERT:
|
||||
|
||||
if (itr != base_type::end())
|
||||
throw torrent::internal_error("View::received(..., SLOTS_INSERT) already inserted.");
|
||||
throw torrent::client_error("View::received(..., SLOTS_INSERT) already inserted.");
|
||||
|
||||
if (view_downloads_filter(m_filter)(download))
|
||||
insert_visible(download);
|
||||
@@ -217,7 +217,7 @@ View::received(core::Download* download, int event) {
|
||||
base_type::insert(end_filtered(), download);
|
||||
|
||||
if (m_focus > m_size)
|
||||
throw torrent::internal_error("View::received(...) m_focus > m_size.");
|
||||
throw torrent::client_error("View::received(...) m_focus > m_size.");
|
||||
|
||||
break;
|
||||
|
||||
@@ -227,7 +227,7 @@ View::received(core::Download* download, int event) {
|
||||
|
||||
default:
|
||||
if (itr == end_filtered())
|
||||
throw torrent::internal_error("View::received(..., SLOTS_*) could not find download.");
|
||||
throw torrent::client_error("View::received(..., SLOTS_*) could not find download.");
|
||||
|
||||
if (view_downloads_filter(m_filter)(download)) {
|
||||
|
||||
|
||||
+2
-1
@@ -85,7 +85,8 @@ public:
|
||||
|
||||
const std::string& name() const { return m_name; }
|
||||
|
||||
bool empty() const { return m_size == 0; }
|
||||
bool empty_visible() const { return m_size == 0; }
|
||||
|
||||
size_type size() const { return m_size; }
|
||||
|
||||
// Perhaps this should be renamed?
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
|
||||
#include "download.h"
|
||||
#include "download_list.h"
|
||||
#include "hash_queue.h"
|
||||
#include "manager.h"
|
||||
#include "view.h"
|
||||
#include "view_manager.h"
|
||||
@@ -162,7 +161,7 @@ ViewManager::clear() {
|
||||
ViewManager::iterator
|
||||
ViewManager::insert(const std::string& name) {
|
||||
if (find(name) != end())
|
||||
throw torrent::internal_error("ViewManager::insert(...) name already inserted.");
|
||||
throw torrent::client_error("ViewManager::insert(...) name already inserted.");
|
||||
|
||||
View* view = new View();
|
||||
view->initialize(name, m_list);
|
||||
|
||||
@@ -79,7 +79,7 @@ print_hhmmss_local(char* first, char* last, time_t t) {
|
||||
|
||||
if (u == NULL)
|
||||
//return "inv_time";
|
||||
throw torrent::internal_error("print_hhmmss_local(...) failed.");
|
||||
throw torrent::client_error("print_hhmmss_local(...) failed.");
|
||||
|
||||
return print_buffer(first, last, "%2u:%02u:%02u", u->tm_hour, u->tm_min, u->tm_sec);
|
||||
}
|
||||
@@ -98,7 +98,7 @@ print_ddmmyyyy(char* first, char* last, time_t t) {
|
||||
|
||||
if (u == NULL)
|
||||
//return "inv_time";
|
||||
throw torrent::internal_error("print_ddmmyyyy(...) failed.");
|
||||
throw torrent::client_error("print_ddmmyyyy(...) failed.");
|
||||
|
||||
return print_buffer(first, last, "%02u/%02u/%04u", u->tm_mday, (u->tm_mon + 1), (1900 + u->tm_year));
|
||||
}
|
||||
@@ -154,17 +154,17 @@ print_download_info(char* first, char* last, core::Download* d) {
|
||||
first = print_buffer(first, last, " [%s]", core::Download::priority_to_string(d->priority()));
|
||||
|
||||
if (first > last)
|
||||
throw torrent::internal_error("print_download_info(...) wrote past end of the buffer.");
|
||||
throw torrent::client_error("print_download_info(...) wrote past end of the buffer.");
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
char*
|
||||
print_download_status(char* first, char* last, core::Download* d) {
|
||||
if (!d->download()->is_active())
|
||||
if (!d->is_active())
|
||||
first = print_buffer(first, last, "Inactive: ");
|
||||
|
||||
if (d->download()->is_hash_checking()) {
|
||||
if (d->is_hash_checking()) {
|
||||
first = print_buffer(first, last, "Checking hash [%2i%%]",
|
||||
(d->download()->chunks_hashed() * 100) / d->download()->chunks_total());
|
||||
|
||||
@@ -182,7 +182,7 @@ print_download_status(char* first, char* last, core::Download* d) {
|
||||
}
|
||||
|
||||
if (first > last)
|
||||
throw torrent::internal_error("print_download_status(...) wrote past end of the buffer.");
|
||||
throw torrent::client_error("print_download_status(...) wrote past end of the buffer.");
|
||||
|
||||
return first;
|
||||
}
|
||||
@@ -233,7 +233,7 @@ print_status_info(char* first, char* last) {
|
||||
}
|
||||
|
||||
if (first > last)
|
||||
throw torrent::internal_error("print_status_info(...) wrote past end of the buffer.");
|
||||
throw torrent::client_error("print_status_info(...) wrote past end of the buffer.");
|
||||
|
||||
if (!rak::socket_address::cast_from(torrent::connection_manager()->bind_address())->is_address_any()) {
|
||||
first = print_buffer(first, last, " [Bind ");
|
||||
|
||||
@@ -73,7 +73,7 @@ WindowDownloadList::redraw() {
|
||||
|
||||
m_canvas->print(0, 0, "%s", ("[View: " + m_view->name() + "]").c_str());
|
||||
|
||||
if (m_view->empty() || m_canvas->get_width() < 5 || m_canvas->get_height() < 2)
|
||||
if (m_view->empty_visible() || m_canvas->get_width() < 5 || m_canvas->get_height() < 2)
|
||||
return;
|
||||
|
||||
typedef std::pair<core::View::iterator, core::View::iterator> Range;
|
||||
|
||||
@@ -90,7 +90,7 @@ DownloadList::DownloadList(Control* c) :
|
||||
receive_change_view("main");
|
||||
|
||||
if (m_view == NULL)
|
||||
throw torrent::internal_error("View \"main\" must be present to initialize the main display.");
|
||||
throw torrent::client_error("View \"main\" must be present to initialize the main display.");
|
||||
|
||||
m_taskUpdate.set_slot(rak::mem_fn(this, &DownloadList::task_update)),
|
||||
|
||||
@@ -209,7 +209,7 @@ DownloadList::receive_stop_download() {
|
||||
if (m_view->focus() == m_view->end_visible())
|
||||
return;
|
||||
|
||||
if ((*m_view->focus())->download()->is_active())
|
||||
if ((*m_view->focus())->variable()->get_value("state") == 1)
|
||||
m_control->core()->download_list()->stop(*m_view->focus());
|
||||
else
|
||||
m_control->core()->download_list()->erase(*m_view->focus());
|
||||
@@ -391,7 +391,7 @@ DownloadList::receive_change_view(const std::string& name) {
|
||||
ElementDownloadList* ui = dynamic_cast<ElementDownloadList*>(m_uiArray[DISPLAY_DOWNLOAD_LIST]);
|
||||
|
||||
if (ui == NULL)
|
||||
throw torrent::internal_error("DownloadList::receive_change_view(...) could not cast ui.");
|
||||
throw torrent::client_error("DownloadList::receive_change_view(...) could not cast ui.");
|
||||
|
||||
ui->set_view(m_view);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ ElementFileList::ElementFileList(core::Download* d) :
|
||||
void
|
||||
ElementFileList::activate(Control* c, MItr mItr) {
|
||||
if (m_window != NULL)
|
||||
throw torrent::internal_error("ui::ElementFileList::activate(...) called on an object in the wrong state");
|
||||
throw torrent::client_error("ui::ElementFileList::activate(...) called on an object in the wrong state");
|
||||
|
||||
c->input()->push_front(&m_bindings);
|
||||
|
||||
@@ -71,7 +71,7 @@ ElementFileList::activate(Control* c, MItr mItr) {
|
||||
void
|
||||
ElementFileList::disable(Control* c) {
|
||||
if (m_window == NULL)
|
||||
throw torrent::internal_error("ui::ElementFileList::disable(...) called on an object in the wrong state");
|
||||
throw torrent::client_error("ui::ElementFileList::disable(...) called on an object in the wrong state");
|
||||
|
||||
c->input()->erase(&m_bindings);
|
||||
|
||||
@@ -82,7 +82,7 @@ ElementFileList::disable(Control* c) {
|
||||
void
|
||||
ElementFileList::receive_next() {
|
||||
if (m_window == NULL)
|
||||
throw torrent::internal_error("ui::ElementFileList::receive_next(...) called on a disabled object");
|
||||
throw torrent::client_error("ui::ElementFileList::receive_next(...) called on a disabled object");
|
||||
|
||||
if (++m_focus >= m_download->download()->file_list().size())
|
||||
m_focus = 0;
|
||||
@@ -93,7 +93,7 @@ ElementFileList::receive_next() {
|
||||
void
|
||||
ElementFileList::receive_prev() {
|
||||
if (m_window == NULL)
|
||||
throw torrent::internal_error("ui::ElementFileList::receive_prev(...) called on a disabled object");
|
||||
throw torrent::client_error("ui::ElementFileList::receive_prev(...) called on a disabled object");
|
||||
|
||||
torrent::FileList fl = m_download->download()->file_list();
|
||||
|
||||
@@ -111,7 +111,7 @@ ElementFileList::receive_prev() {
|
||||
void
|
||||
ElementFileList::receive_priority() {
|
||||
if (m_window == NULL)
|
||||
throw torrent::internal_error("ui::ElementFileList::receive_prev(...) called on a disabled object");
|
||||
throw torrent::client_error("ui::ElementFileList::receive_prev(...) called on a disabled object");
|
||||
|
||||
torrent::FileList fl = m_download->download()->file_list();
|
||||
|
||||
@@ -129,7 +129,7 @@ ElementFileList::receive_priority() {
|
||||
void
|
||||
ElementFileList::receive_change_all() {
|
||||
if (m_window == NULL)
|
||||
throw torrent::internal_error("ui::ElementFileList::receive_prev(...) called on a disabled object");
|
||||
throw torrent::client_error("ui::ElementFileList::receive_prev(...) called on a disabled object");
|
||||
|
||||
torrent::FileList fl = m_download->download()->file_list();
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ ElementTrackerList::ElementTrackerList(core::Download* d) :
|
||||
void
|
||||
ElementTrackerList::activate(Control* c, MItr mItr) {
|
||||
if (m_window != NULL)
|
||||
throw torrent::internal_error("ui::ElementTrackerList::activate(...) called on an object in the wrong state");
|
||||
throw torrent::client_error("ui::ElementTrackerList::activate(...) called on an object in the wrong state");
|
||||
|
||||
c->input()->push_front(&m_bindings);
|
||||
|
||||
@@ -72,7 +72,7 @@ ElementTrackerList::activate(Control* c, MItr mItr) {
|
||||
void
|
||||
ElementTrackerList::disable(Control* c) {
|
||||
if (m_window == NULL)
|
||||
throw torrent::internal_error("ui::ElementTrackerList::disable(...) called on an object in the wrong state");
|
||||
throw torrent::client_error("ui::ElementTrackerList::disable(...) called on an object in the wrong state");
|
||||
|
||||
c->input()->erase(&m_bindings);
|
||||
|
||||
@@ -83,7 +83,7 @@ ElementTrackerList::disable(Control* c) {
|
||||
void
|
||||
ElementTrackerList::receive_disable() {
|
||||
if (m_window == NULL)
|
||||
throw torrent::internal_error("ui::ElementTrackerList::receive_disable(...) called on a disabled object");
|
||||
throw torrent::client_error("ui::ElementTrackerList::receive_disable(...) called on a disabled object");
|
||||
|
||||
torrent::Tracker t = m_download->download()->tracker_list().get(m_focus);
|
||||
|
||||
@@ -98,7 +98,7 @@ ElementTrackerList::receive_disable() {
|
||||
void
|
||||
ElementTrackerList::receive_next() {
|
||||
if (m_window == NULL)
|
||||
throw torrent::internal_error("ui::ElementTrackerList::receive_next(...) called on a disabled object");
|
||||
throw torrent::client_error("ui::ElementTrackerList::receive_next(...) called on a disabled object");
|
||||
|
||||
if (++m_focus >= m_download->download()->tracker_list().size())
|
||||
m_focus = 0;
|
||||
@@ -109,7 +109,7 @@ ElementTrackerList::receive_next() {
|
||||
void
|
||||
ElementTrackerList::receive_prev() {
|
||||
if (m_window == NULL)
|
||||
throw torrent::internal_error("ui::ElementTrackerList::receive_prev(...) called on a disabled object");
|
||||
throw torrent::client_error("ui::ElementTrackerList::receive_prev(...) called on a disabled object");
|
||||
|
||||
if (m_download->download()->tracker_list().size() == 0)
|
||||
return;
|
||||
@@ -125,12 +125,12 @@ ElementTrackerList::receive_prev() {
|
||||
void
|
||||
ElementTrackerList::receive_cycle_group() {
|
||||
if (m_window == NULL)
|
||||
throw torrent::internal_error("ui::ElementTrackerList::receive_group_cycle(...) called on a disabled object");
|
||||
throw torrent::client_error("ui::ElementTrackerList::receive_group_cycle(...) called on a disabled object");
|
||||
|
||||
torrent::TrackerList tl = m_download->download()->tracker_list();
|
||||
|
||||
if (m_focus >= tl.size())
|
||||
throw torrent::internal_error("ui::ElementTrackerList::receive_group_cycle(...) called with an invalid focus");
|
||||
throw torrent::client_error("ui::ElementTrackerList::receive_group_cycle(...) called with an invalid focus");
|
||||
|
||||
tl.cycle_group(tl.get(m_focus).group());
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ VariableMap::insert(const std::string& key, Variable* v) {
|
||||
iterator itr = base_type::find(key);
|
||||
|
||||
if (itr != base_type::end())
|
||||
throw torrent::internal_error("VariableMap::insert(...) tried to insert an already existing key.");
|
||||
throw torrent::client_error("VariableMap::insert(...) tried to insert an already existing key.");
|
||||
|
||||
base_type::insert(itr, value_type(key, v));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user