diff --git a/include/libtorrent/disk_io_thread.hpp b/include/libtorrent/disk_io_thread.hpp index f542cd390..c8f10b032 100644 --- a/include/libtorrent/disk_io_thread.hpp +++ b/include/libtorrent/disk_io_thread.hpp @@ -73,6 +73,8 @@ namespace libtorrent , piece(0) , offset(0) , priority(0) + , error_piece(-1) + , error_op(-1) {} enum action_t @@ -123,6 +125,9 @@ namespace libtorrent // the piece the error occurred on int error_piece; + // the operation that failed (only read or write) + int error_op; + // this is called when operation completes boost::function callback; }; diff --git a/include/libtorrent/piece_picker.hpp b/include/libtorrent/piece_picker.hpp index b216d0c8d..ece78544a 100644 --- a/include/libtorrent/piece_picker.hpp +++ b/include/libtorrent/piece_picker.hpp @@ -275,7 +275,7 @@ namespace libtorrent , piece_state_t s); void mark_as_writing(piece_block block, void* peer); void mark_as_finished(piece_block block, void* peer); - void write_failed(piece_block block); + void write_failed(int piece); int num_peers(piece_block block) const; // returns information about the given piece diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index ef4f7c255..b71242c1a 100644 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -290,6 +290,7 @@ namespace libtorrent error_code const& error() const { return m_storage->error(); } std::string const& error_file() const { return m_storage->error_file(); } int last_piece() const { return m_last_piece; } + int last_operation() const { return m_last_op; } void clear_error() { m_storage->clear_error(); } int slot_for(int piece) const; @@ -423,6 +424,9 @@ namespace libtorrent // the last piece we wrote to or read from int m_last_piece; + // the last operation we did (read or write) + int m_last_op; + // this is saved in case we need to instantiate a new // storage (osed when remapping files) storage_constructor_type m_storage_constructor; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index d8c19c220..ef1fc2dd2 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -216,6 +216,7 @@ namespace libtorrent void ip_filter_updated() { m_policy.ip_filter_updated(); } + void handle_disk_error(disk_io_job const& j, peer_connection* c = 0); void clear_error(); void set_error(error_code const& ec, std::string const& file); bool has_error() const { return m_error; } diff --git a/src/disk_io_thread.cpp b/src/disk_io_thread.cpp index 78c7a278c..073543497 100644 --- a/src/disk_io_thread.cpp +++ b/src/disk_io_thread.cpp @@ -1249,9 +1249,12 @@ namespace libtorrent j.error = ec; j.error_file = j.storage->error_file(); j.error_piece = j.storage->last_piece(); + j.error_op = j.storage->last_operation(); j.storage->clear_error(); #ifdef TORRENT_DEBUG - std::cout << "ERROR: '" << j.str << "' " << j.error_file << std::endl; + std::cout << "ERROR: '" << j.str << "' while " + << (j.error_op == disk_io_job::read?"reading ":"writing ") + << j.error_file << std::endl; #endif return true; } @@ -1406,11 +1409,6 @@ namespace libtorrent } case disk_io_job::read_and_hash: { - if (test_error(j)) - { - ret = -1; - break; - } #ifdef TORRENT_DISK_STATS m_log << log_time() << " read_and_hash " << j.buffer_size << std::endl; #endif @@ -1421,8 +1419,10 @@ namespace libtorrent if (j.buffer == 0) { ret = -1; - j.error = error_code(ENOMEM, get_posix_category()); - j.error_piece = -1; + j.error = error_code(boost::system::errc::not_enough_memory + , get_posix_category()); + j.error_piece = j.piece; + j.error_op = disk_io_job::read; j.str = j.error.message(); break; } @@ -1450,6 +1450,7 @@ namespace libtorrent j.error = error_code(errors::failed_hash_check, libtorrent_category); j.str = j.error.message(); j.error_piece = j.storage->last_piece(); + j.error_op = disk_io_job::read; j.buffer = 0; break; } @@ -1478,8 +1479,10 @@ namespace libtorrent if (j.buffer == 0) { ret = -1; - j.error = error_code(ENOMEM, get_posix_category()); - j.error_piece = -1; + j.error = error_code(boost::system::errc::not_enough_memory + , get_posix_category()); + j.error_piece = j.piece; + j.error_op = disk_io_job::read; j.str = j.error.message(); break; } @@ -1513,6 +1516,7 @@ namespace libtorrent j.error_file.clear(); j.str = j.error.message(); j.error_piece = j.storage->last_piece(); + j.error_op = disk_io_job::read; ret = -1; break; } diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 651c7f0f7..c59326242 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -2078,18 +2078,14 @@ namespace libtorrent if (ret == -1 || !t) { - if (t->has_picker()) t->picker().write_failed(block_finished); - if (!t) { disconnect(j.str.c_str()); return; } - - if (t->alerts().should_post()) - t->alerts().post_alert(file_error_alert(j.error_file, t->get_handle(), j.str)); - t->set_error(j.error, j.error_file); - t->pause(); + + // handle_disk_error may disconnect us + t->handle_disk_error(j, this); return; } @@ -3530,10 +3526,8 @@ namespace libtorrent } else { - if (t->alerts().should_post()) - t->alerts().post_alert(file_error_alert(j.error_file, t->get_handle(), j.str)); - t->set_error(j.error, j.error_file); - t->pause(); + // handle_disk_error may disconnect us + t->handle_disk_error(j, this); } return; } diff --git a/src/piece_picker.cpp b/src/piece_picker.cpp index 1fb4891d0..7666fad42 100644 --- a/src/piece_picker.cpp +++ b/src/piece_picker.cpp @@ -2117,20 +2117,15 @@ namespace libtorrent } } - void piece_picker::write_failed(piece_block block) + void piece_picker::write_failed(int piece) { TORRENT_PIECE_PICKER_INVARIANT_CHECK; std::vector::iterator i - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(piece)); TORRENT_ASSERT(i != m_downloads.end()); - block_info& info = i->info[block.block_index]; - TORRENT_ASSERT(info.state == block_info::state_writing); - TORRENT_ASSERT(info.num_peers == 0); - --i->writing; - info.state = block_info::state_none; - info.peer = 0; + erase_download_piece(i); } void piece_picker::mark_as_finished(piece_block block, void* peer) diff --git a/src/storage.cpp b/src/storage.cpp index 58281757e..96412e4a5 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -481,6 +481,7 @@ namespace libtorrent TORRENT_ASSERT(!error()); int num_read = 0; int slot_size = piece_size - ph.offset; + m_last_op = disk_io_job::read; if (slot_size > 0) { int block_size = 16 * 1024; @@ -1485,6 +1486,7 @@ ret: , m_scratch_buffer2(io, 0) , m_scratch_piece(-1) , m_last_piece(-1) + , m_last_op(-1) , m_storage_constructor(sc) , m_io_thread(io) , m_torrent(torrent) @@ -1747,6 +1749,7 @@ ret: TORRENT_ASSERT(offset >= 0); TORRENT_ASSERT(num_bufs > 0); m_last_piece = piece_index; + m_last_op = disk_io_job::read; int slot = slot_for(piece_index); return m_storage->readv(bufs, slot, offset, num_bufs); } @@ -1767,6 +1770,7 @@ ret: file::iovec_t* iov = TORRENT_ALLOCA(file::iovec_t, num_bufs); std::copy(bufs, bufs + num_bufs, iov); m_last_piece = piece_index; + m_last_op = disk_io_job::write; int slot = allocate_slot_for_piece(piece_index); int ret = m_storage->writev(bufs, slot, offset, num_bufs); // only save the partial hash if the write succeeds @@ -2340,6 +2344,7 @@ ret: // the slot where this piece belongs is // free. Just move the piece there. m_last_piece = piece; + m_last_op = disk_io_job::write; m_storage->move_slot(m_current_slot, piece); if (m_storage->error()) return -1; @@ -2567,6 +2572,7 @@ ret: bool ret = false; m_last_piece = piece_index; + m_last_op = disk_io_job::write; if (other_piece >= 0) ret |= m_storage->swap_slots(other_slot, m_current_slot); else @@ -2606,6 +2612,7 @@ ret: } m_last_piece = other_piece; + m_last_op = disk_io_job::write; if (ret) return skip_file(); TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned @@ -2645,6 +2652,7 @@ ret: TORRENT_ASSERT(piece_index == slot1); m_last_piece = piece_index; + m_last_op = disk_io_job::write; m_storage->swap_slots(m_current_slot, slot1); TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned @@ -2692,6 +2700,7 @@ ret: } m_last_piece = piece_index; + m_last_op = disk_io_job::write; if (ret) return skip_file(); TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned @@ -2838,6 +2847,7 @@ ret: , m_piece_to_slot[piece_at_our_slot]); m_last_piece = piece_index; + m_last_op = disk_io_job::write; m_storage->move_slot(piece_index, slot_index); TORRENT_ASSERT(m_slot_to_piece[piece_index] == piece_index); @@ -2882,6 +2892,7 @@ ret: if (m_piece_to_slot[pos] != has_no_slot) { m_last_piece = pos; + m_last_op = disk_io_job::write; new_free_slot = m_piece_to_slot[pos]; m_storage->move_slot(new_free_slot, pos); m_slot_to_piece[pos] = pos; diff --git a/src/torrent.cpp b/src/torrent.cpp index 4a3fabd76..179dae67d 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -340,6 +340,42 @@ namespace libtorrent } } + void torrent::handle_disk_error(disk_io_job const& j, peer_connection* c) + { + if (!j.error) return; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_ses.m_logger) << "disk error: '" << j.str << "' while " + << (j.error_op == disk_io_job::read?"reading ":"writing ") + << " piece " << j.error_piece << " in file " << j.error_file << "\n"; +#endif + + if (j.error == error_code(boost::system::errc::not_enough_memory, get_posix_category())) + { + if (alerts().should_post()) + alerts().post_alert(file_error_alert(j.error_file, get_handle(), j.str)); + if (c) c->disconnect("no memory"); + return; + } + + TORRENT_ASSERT(j.error_piece >= 0); + + if (j.error_op == disk_io_job::write) + { + // we failed to write j.error_piece to disk + // tell the piece picker + if (has_picker() && j.error_piece >= 0) picker().write_failed(j.error_piece); + } + + // notify the user of the error + if (alerts().should_post()) + alerts().post_alert(file_error_alert(j.error_file, get_handle(), j.str)); + + // put the torrent in an error-state + set_error(j.error, j.error_file); + pause(); + } + void torrent::on_disk_read_complete(int ret, disk_io_job const& j, peer_request r, read_piece_struct* rp) { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); @@ -350,8 +386,7 @@ namespace libtorrent if (ret != r.length) { rp->fail = true; - set_error(j.error, j.error_file); - pause(); + handle_disk_error(j); } else { @@ -428,11 +463,7 @@ namespace libtorrent if (ret == -1) { - if (has_picker()) picker().write_failed(block_finished); - if (alerts().should_post()) - alerts().post_alert(file_error_alert(j.error_file, get_handle(), j.str)); - set_error(j.error, j.error_file); - pause(); + handle_disk_error(j); return; } picker().mark_as_finished(block_finished, 0);