Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix partially done downloads and choke groups #147

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/download/download_wrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ DownloadWrapper::receive_hash_done(ChunkHandle handle, const char* hash) {
priority_queue_erase(&taskScheduler, &m_main->delay_partially_done());
priority_queue_erase(&taskScheduler, &m_main->delay_partially_restarted());
priority_queue_insert(&taskScheduler, &m_main->delay_partially_done(), cachedTime);
finished_download();
}

if (!m_main->have_queue()->empty() && m_main->have_queue()->front().first >= cachedTime)
Expand Down Expand Up @@ -325,8 +326,10 @@ DownloadWrapper::receive_tick(uint32_t ticks) {

void
DownloadWrapper::receive_update_priorities() {
if (m_main->chunk_selector()->empty())
if (m_main->chunk_selector()->empty()) {
file_list()->set_selected_size_bytes();
return;
}

data()->mutable_high_priority()->clear();
data()->mutable_normal_priority()->clear();
Expand Down Expand Up @@ -373,11 +376,15 @@ DownloadWrapper::receive_update_priorities() {
priority_queue_erase(&taskScheduler, &m_main->delay_partially_done());
priority_queue_erase(&taskScheduler, &m_main->delay_partially_restarted());

if (was_partial)
if (was_partial) {
priority_queue_insert(&taskScheduler, &m_main->delay_partially_done(), cachedTime);
else
finished_download();
} else {
priority_queue_insert(&taskScheduler, &m_main->delay_partially_restarted(), cachedTime);
}
}

file_list()->set_selected_size_bytes();
}

void
Expand Down
2 changes: 1 addition & 1 deletion src/manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Manager::Manager() :
m_connectionManager->listen()->slot_accepted() =
std::bind(&HandshakeManager::add_incoming, m_handshakeManager, std::placeholders::_1, std::placeholders::_2);

m_resourceManager->push_group("default");
m_resourceManager->push_group("default_leech");
m_resourceManager->group_back()->up_queue()->set_heuristics(choke_queue::HEURISTICS_UPLOAD_LEECH);
m_resourceManager->group_back()->down_queue()->set_heuristics(choke_queue::HEURISTICS_DOWNLOAD_LEECH);
}
Expand Down
2 changes: 1 addition & 1 deletion src/protocol/handshake_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ HandshakeManager::receive_succeeded(Handshake* handshake) {

if (!download->info()->is_active())
reason = e_handshake_inactive_download;
else if (download->file_list()->is_done() && handshake->bitfield()->is_all_set())
else if (download->file_list()->data()->is_partially_done() && handshake->bitfield()->is_all_set())
reason = e_handshake_unwanted_connection;
else
reason = e_handshake_duplicate;
Expand Down
2 changes: 1 addition & 1 deletion src/protocol/peer_connection_base.cc
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ PeerConnectionBase::initialize(DownloadMain* download, PeerInfo* peerInfo, Socke

m_peerChunks.download_cache()->clear();

if (!m_download->file_list()->is_done()) {
if (!m_download->file_list()->data()->is_partially_done()) {
m_sendInterested = true;
m_downInterested = true;
}
Expand Down
2 changes: 1 addition & 1 deletion src/protocol/peer_connection_leech.cc
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ PeerConnection<type>::read_have_chunk(uint32_t index) {
m_download->choke_group()->up_queue()->set_not_queued(this, &m_upChoke);
}

if (type != Download::CONNECTION_LEECH || m_download->file_list()->is_done())
if (type != Download::CONNECTION_LEECH || m_download->file_list()->data()->is_partially_done())
return;

if (is_down_interested()) {
Expand Down
63 changes: 63 additions & 0 deletions src/torrent/data/file_list.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ FileList::FileList() :
m_isOpen(false),

m_torrentSize(0),
m_selectedSize(0),
m_chunkSize(0),
m_maxFileSize(~uint64_t()) {
}
Expand All @@ -98,6 +99,7 @@ FileList::~FileList() {

base_type::clear();
m_torrentSize = 0;
m_selectedSize = 0;
}

bool
Expand Down Expand Up @@ -193,6 +195,65 @@ FileList::set_max_file_size(uint64_t size) {
m_maxFileSize = size;
}

// This function should be called from receive_update_priorities().
// It offloads continous calculation by updating the m_selectedSize property.
// Its purpose is to replace size_bytes() with selected_size_bytes()
// by taking into account partial downloads.
void
FileList::set_selected_size_bytes(uint64_t bytes) {
if (bytes > 0) {
m_selectedSize = bytes;
return;
}

if (is_done()) {
m_selectedSize = m_torrentSize;
return;
}

uint64_t completedBytes = completed_bytes();

if (data()->is_partially_done()) {
m_selectedSize = completedBytes;
return;
}

uint32_t selectedSizeChunks = 0;
uint32_t prevChunk = -1;
bool areAllFilesSelected = true;
bool isLastFileSelected = false;

for (FileList::const_iterator itr = begin(), last = end(); itr != last; itr++) {

if ((*itr)->priority() != PRIORITY_OFF) {
selectedSizeChunks += (*itr)->size_chunks() - ((*itr)->range_first() == prevChunk ? 1 : 0);
prevChunk = (*itr)->range_second() - 1;

if (itr == end() - 1)
isLastFileSelected = true;
} else {
areAllFilesSelected = false;
}

}

if (areAllFilesSelected) {
m_selectedSize = m_torrentSize;
return;
}

uint64_t selectedSizeBytes = (uint64_t)selectedSizeChunks * (uint64_t)m_chunkSize;

// Dealing with size of last chunk as it's usually smaller than the rest.
uint64_t remainder = m_torrentSize % (uint64_t)m_chunkSize;

if (isLastFileSelected && remainder != 0)
selectedSizeBytes = selectedSizeBytes - (uint64_t)m_chunkSize + remainder;

// Set completed bytes if some files (e.g. all of them) were set to Off later.
m_selectedSize = (selectedSizeBytes < completedBytes ? completedBytes : selectedSizeBytes);
}

// This function should really ensure that we arn't dealing files
// spread over multiple mount-points.
uint64_t
Expand Down Expand Up @@ -376,6 +437,7 @@ FileList::initialize(uint64_t torrentSize, uint32_t chunkSize) {

m_chunkSize = chunkSize;
m_torrentSize = torrentSize;
m_selectedSize = torrentSize;
m_rootDir = ".";

m_data.mutable_completed_bitfield()->set_size_bits((size_bytes() + chunk_size() - 1) / chunk_size());
Expand Down Expand Up @@ -725,6 +787,7 @@ FileList::reset_filesize(int64_t size) {
close();
m_chunkSize = size;
m_torrentSize = size;
m_selectedSize = size;
(*begin())->set_size_bytes(size);
(*begin())->set_range(m_chunkSize);

Expand Down
4 changes: 4 additions & 0 deletions src/torrent/data/file_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ class LIBTORRENT_EXPORT FileList : private std::vector<File*> {
uint64_t size_bytes() const { return m_torrentSize; }
uint32_t size_chunks() const { return bitfield()->size_bits(); }

uint64_t selected_size_bytes() const { return m_selectedSize; }
void set_selected_size_bytes(uint64_t bytes = 0);

uint32_t completed_chunks() const { return bitfield()->size_set(); }
uint64_t completed_bytes() const;
uint64_t left_bytes() const;
Expand Down Expand Up @@ -187,6 +190,7 @@ class LIBTORRENT_EXPORT FileList : private std::vector<File*> {
uint64_t m_torrentSize;
uint32_t m_chunkSize;
uint64_t m_maxFileSize;
uint64_t m_selectedSize;

std::string m_rootDir;

Expand Down
26 changes: 0 additions & 26 deletions src/torrent/download.cc
Original file line number Diff line number Diff line change
Expand Up @@ -565,32 +565,6 @@ Download::set_connection_type(ConnectionType t) {
m_ptr->set_connection_type(t);
}

Download::HeuristicType
Download::upload_choke_heuristic() const {
return (Download::HeuristicType)m_ptr->main()->choke_group()->up_queue()->heuristics();
}

void
Download::set_upload_choke_heuristic(HeuristicType t) {
if ((choke_queue::heuristics_enum)t >= choke_queue::HEURISTICS_MAX_SIZE)
throw input_error("Invalid heuristics value.");

m_ptr->main()->choke_group()->up_queue()->set_heuristics((choke_queue::heuristics_enum)t);
}

Download::HeuristicType
Download::download_choke_heuristic() const {
return (Download::HeuristicType)m_ptr->main()->choke_group()->down_queue()->heuristics();
}

void
Download::set_download_choke_heuristic(HeuristicType t) {
if ((choke_queue::heuristics_enum)t >= choke_queue::HEURISTICS_MAX_SIZE)
throw input_error("Invalid heuristics value.");

m_ptr->main()->choke_group()->down_queue()->set_heuristics((choke_queue::heuristics_enum)t);
}

void
Download::update_priorities() {
m_ptr->receive_update_priorities();
Expand Down
9 changes: 0 additions & 9 deletions src/torrent/download.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,15 +183,6 @@ class LIBTORRENT_EXPORT Download {
ConnectionType connection_type() const;
void set_connection_type(ConnectionType t);

typedef enum {
} HeuristicType;

HeuristicType upload_choke_heuristic() const;
void set_upload_choke_heuristic(HeuristicType t);

HeuristicType download_choke_heuristic() const;
void set_download_choke_heuristic(HeuristicType t);

// Call this when you want the modifications of the download priorities
// in the entries to take effect. It is slightly expensive as it rechecks
// all the peer bitfields to see if we are still interested.
Expand Down
2 changes: 1 addition & 1 deletion src/torrent/peer/connection_list.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ ConnectionList::clear() {

bool
ConnectionList::want_connection(PeerInfo* p, Bitfield* bitfield) {
if (m_download->file_list()->is_done() || m_download->initial_seeding() != NULL)
if (m_download->file_list()->data()->is_partially_done() || m_download->initial_seeding() != NULL)
return !bitfield->is_all_set();

if (!m_download->info()->is_accepting_seeders())
Expand Down
5 changes: 1 addition & 4 deletions src/torrent/utils/resume.cc
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,7 @@ resume_save_progress(Download download, Object& object) {

// } else if ((*listItr)->completed_chunks() == (*listItr)->size_chunks()) {

} else if (fileList->bitfield()->is_all_set()) {
// Currently only checking if we're finished. This needs to be
// smarter when it comes to downloading partial torrents, etc.

} else if (download.data()->is_partially_done()) {
// This assumes the syncs are properly called before
// resume_save_progress gets called after finishing a torrent.
filesItr->insert_key("mtime", (int64_t)fs.modified_time());
Expand Down