steps towards removing disk_io_job from disk_interface

This commit is contained in:
arvidn 2016-11-15 21:57:44 -05:00 committed by Arvid Norberg
parent ca6f446c26
commit 5b3a730b1f
4 changed files with 88 additions and 86 deletions

View File

@ -449,7 +449,11 @@ namespace libtorrent
std::string resolve_filename(int file) const; std::string resolve_filename(int file) const;
void handle_exception(); void handle_exception();
void handle_disk_error(disk_io_job const* j, peer_connection* c = 0);
enum class disk_class { none, write };
void handle_disk_error(string_view job_name
, storage_error const& error, peer_connection* c = nullptr
, disk_class rw = disk_class::none);
void clear_error(); void clear_error();
void set_error(error_code const& ec, int file); void set_error(error_code const& ec, int file);

View File

@ -773,7 +773,7 @@ namespace libtorrent
{ {
disk_io_job* j = src.pop_front(); disk_io_job* j = src.pop_front();
TORRENT_ASSERT((j->flags & disk_io_job::in_progress) || !j->storage); TORRENT_ASSERT((j->flags & disk_io_job::in_progress) || !j->storage);
j->ret = -1; j->ret = disk_interface::fatal_disk_error;
j->error = e; j->error = e;
dst.push_back(j); dst.push_back(j);
} }
@ -1101,25 +1101,26 @@ namespace libtorrent
} }
catch (boost::system::system_error const& err) catch (boost::system::system_error const& err)
{ {
ret = -1; ret = disk_interface::fatal_disk_error;
j->error.ec = err.code(); j->error.ec = err.code();
j->error.operation = storage_error::exception; j->error.operation = storage_error::exception;
} }
catch (std::bad_alloc const&) catch (std::bad_alloc const&)
{ {
ret = -1; ret = disk_interface::fatal_disk_error;
j->error.ec = errors::no_memory; j->error.ec = errors::no_memory;
j->error.operation = storage_error::exception; j->error.operation = storage_error::exception;
} }
catch (std::exception const&) catch (std::exception const&)
{ {
ret = -1; ret = disk_interface::fatal_disk_error;
j->error.ec = boost::asio::error::fault; j->error.ec = boost::asio::error::fault;
j->error.operation = storage_error::exception; j->error.operation = storage_error::exception;
} }
// note that -2 errors are OK // note that -2 errors are OK
TORRENT_ASSERT(ret != -1 || (j->error.ec && j->error.operation != 0)); TORRENT_ASSERT(ret != disk_interface::fatal_disk_error
|| (j->error.ec && j->error.operation != 0));
m_stats_counters.inc_stats_counter(counters::num_running_disk_jobs, -1); m_stats_counters.inc_stats_counter(counters::num_running_disk_jobs, -1);
@ -1178,7 +1179,7 @@ namespace libtorrent
{ {
j->error.ec = error::no_memory; j->error.ec = error::no_memory;
j->error.operation = storage_error::alloc_cache_piece; j->error.operation = storage_error::alloc_cache_piece;
return -1; return disk_interface::fatal_disk_error;
} }
time_point start_time = clock_type::now(); time_point start_time = clock_type::now();
@ -1464,7 +1465,7 @@ namespace libtorrent
TORRENT_ASSERT(pe->blocks[j->d.io.offset / 16 / 1024].buf != nullptr); TORRENT_ASSERT(pe->blocks[j->d.io.offset / 16 / 1024].buf != nullptr);
j->error.ec = error::operation_aborted; j->error.ec = error::operation_aborted;
j->error.operation = storage_error::write; j->error.operation = storage_error::write;
return -1; return disk_interface::fatal_disk_error;
} }
pe = m_disk_cache.add_dirty_block(j); pe = m_disk_cache.add_dirty_block(j);
@ -1598,7 +1599,7 @@ namespace libtorrent
if (pe == nullptr) if (pe == nullptr)
{ {
j->ret = -1; j->ret = disk_interface::fatal_disk_error;
j->error.ec = error::no_memory; j->error.ec = error::no_memory;
j->error.operation = storage_error::read; j->error.operation = storage_error::read;
return 0; return 0;
@ -2126,7 +2127,7 @@ namespace libtorrent
sha1_hash piece_hash = h.final(); sha1_hash piece_hash = h.final();
std::memcpy(j->d.piece_hash, piece_hash.data(), 20); std::memcpy(j->d.piece_hash, piece_hash.data(), 20);
return ret >= 0 ? 0 : -1; return ret >= 0 ? 0 : disk_interface::fatal_disk_error;
} }
int disk_io_thread::do_hash(disk_io_job* j, jobqueue_t& /* completed_jobs */ ) int disk_io_thread::do_hash(disk_io_job* j, jobqueue_t& /* completed_jobs */ )
@ -2187,7 +2188,7 @@ namespace libtorrent
{ {
j->error.ec = error::no_memory; j->error.ec = error::no_memory;
j->error.operation = storage_error::alloc_cache_piece; j->error.operation = storage_error::alloc_cache_piece;
return -1; return disk_interface::fatal_disk_error;
} }
if (pe->hashing) if (pe->hashing)
@ -2286,7 +2287,7 @@ namespace libtorrent
j->error.ec = errors::no_memory; j->error.ec = errors::no_memory;
j->error.operation = storage_error::alloc_cache_piece; j->error.operation = storage_error::alloc_cache_piece;
return -1; return disk_interface::fatal_disk_error;
} }
DLOG("do_hash: reading (piece: %d block: %d)\n", int(pe->piece), i); DLOG("do_hash: reading (piece: %d block: %d)\n", int(pe->piece), i);
@ -2309,7 +2310,7 @@ namespace libtorrent
// of this file // of this file
if (ret != iov.iov_len) if (ret != iov.iov_len)
{ {
ret = -1; ret = disk_interface::fatal_disk_error;
j->error.ec = boost::asio::error::eof; j->error.ec = boost::asio::error::eof;
j->error.operation = storage_error::read; j->error.operation = storage_error::read;
m_disk_cache.free_buffer(static_cast<char*>(iov.iov_base)); m_disk_cache.free_buffer(static_cast<char*>(iov.iov_base));
@ -2392,7 +2393,7 @@ namespace libtorrent
l.unlock(); l.unlock();
j->storage->release_files(j->error); j->storage->release_files(j->error);
return j->error ? -1 : 0; return j->error ? disk_interface::fatal_disk_error : 0;
} }
int disk_io_thread::do_delete_files(disk_io_job* j, jobqueue_t& completed_jobs) int disk_io_thread::do_delete_files(disk_io_job* j, jobqueue_t& completed_jobs)
@ -2409,7 +2410,7 @@ namespace libtorrent
l.unlock(); l.unlock();
j->storage->delete_files(j->buffer.delete_options, j->error); j->storage->delete_files(j->buffer.delete_options, j->error);
return j->error ? -1 : 0; return j->error ? disk_interface::fatal_disk_error : 0;
} }
int disk_io_thread::do_check_fastresume(disk_io_job* j, jobqueue_t& /* completed_jobs */ ) int disk_io_thread::do_check_fastresume(disk_io_job* j, jobqueue_t& /* completed_jobs */ )
@ -2485,7 +2486,7 @@ namespace libtorrent
// if files need to be closed, that's the storage's responsibility // if files need to be closed, that's the storage's responsibility
j->storage->rename_file(j->piece, j->buffer.string j->storage->rename_file(j->piece, j->buffer.string
, j->error); , j->error);
return j->error ? -1 : 0; return j->error ? disk_interface::fatal_disk_error : disk_interface::no_error;
} }
int disk_io_thread::do_stop_torrent(disk_io_job* j, jobqueue_t& completed_jobs) int disk_io_thread::do_stop_torrent(disk_io_job* j, jobqueue_t& completed_jobs)
@ -2503,7 +2504,7 @@ namespace libtorrent
m_disk_cache.release_memory(); m_disk_cache.release_memory();
j->storage->release_files(j->error); j->storage->release_files(j->error);
return j->error ? -1 : 0; return j->error ? disk_interface::fatal_disk_error : disk_interface::no_error;
} }
namespace { namespace {

View File

@ -2998,12 +2998,47 @@ namespace libtorrent
// to allow to receive more data // to allow to receive more data
setup_receive(); setup_receive();
piece_block block_finished(p.piece, p.start / t->block_size()); piece_block const block_finished(p.piece, p.start / t->block_size());
if (j->ret < 0) if (j->ret < 0)
{ {
// we failed to write j->piece to disk tell the piece picker
// this will block any other peer from issuing requests
// to this piece, until we've cleared it.
if (j->error.ec == boost::asio::error::operation_aborted)
{
if (t->has_picker())
t->picker().mark_as_canceled(block_finished, nullptr);
}
else
{
// if any other peer has a busy request to this block, we need
// to cancel it too
t->cancel_block(block_finished);
if (t->has_picker())
t->picker().write_failed(block_finished);
if (t->has_storage())
{
// when this returns, all outstanding jobs to the
// piece are done, and we can restore it, allowing
// new requests to it
m_disk_thread.async_clear_piece(&t->storage(), j->piece
, std::bind(&torrent::on_piece_fail_sync, t, _1, block_finished));
}
else
{
// is m_abort true? if so, we should probably just
// exit this function early, no need to keep the picker
// state up-to-date, right?
disk_io_job sj;
sj.piece = j->piece;
t->on_piece_fail_sync(&sj, block_finished);
}
}
t->update_gauge();
// handle_disk_error may disconnect us // handle_disk_error may disconnect us
t->handle_disk_error(j, this); t->handle_disk_error("write", j->error, this, torrent::disk_class::write);
return; return;
} }
@ -5146,7 +5181,7 @@ namespace libtorrent
if (j->error) if (j->error)
{ {
t->handle_disk_error(j, this); t->handle_disk_error("hash", j->error, this);
t->leave_seed_mode(false); t->leave_seed_mode(false);
return; return;
} }
@ -5261,7 +5296,7 @@ namespace libtorrent
if (j->ret != r.length) if (j->ret != r.length)
{ {
// handle_disk_error may disconnect us // handle_disk_error may disconnect us
t->handle_disk_error(j, this); t->handle_disk_error("read", j->error, this);
return; return;
} }

View File

@ -1032,89 +1032,51 @@ namespace libtorrent
} }
} }
void torrent::handle_disk_error(disk_io_job const* j, peer_connection* c) void torrent::handle_disk_error(string_view job_name
, storage_error const& error
, peer_connection* c
, disk_class rw)
{ {
TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(is_single_thread());
if (!j->error) return; TORRENT_ASSERT(error);
#ifndef TORRENT_DISABLE_LOGGING #ifndef TORRENT_DISABLE_LOGGING
if (should_log()) if (should_log())
{ {
debug_log("disk error: (%d) %s [%s : %s] in file: %s" debug_log("disk error: (%d) %s [%*s : %s] in file: %s"
, j->error.ec.value(), j->error.ec.message().c_str() , error.ec.value(), error.ec.message().c_str()
, job_name(j->action), j->error.operation_str() , int(job_name.size()), job_name.data(), error.operation_str()
, resolve_filename(j->error.file).c_str()); , resolve_filename(error.file).c_str());
} }
#endif #endif
if (j->action == disk_io_job::write) if (error.ec == boost::system::errc::not_enough_memory)
{
piece_block block_finished(j->piece, j->d.io.offset / block_size());
// we failed to write j->piece to disk tell the piece picker
// this will block any other peer from issuing requests
// to this piece, until we've cleared it.
if (j->error.ec == boost::asio::error::operation_aborted)
{
if (has_picker())
picker().mark_as_canceled(block_finished, nullptr);
}
else
{
// if any other peer has a busy request to this block, we need
// to cancel it too
cancel_block(block_finished);
if (has_picker())
picker().write_failed(block_finished);
if (m_storage)
{
// when this returns, all outstanding jobs to the
// piece are done, and we can restore it, allowing
// new requests to it
m_ses.disk_thread().async_clear_piece(m_storage.get(), j->piece
, std::bind(&torrent::on_piece_fail_sync, shared_from_this(), _1, block_finished));
}
else
{
// is m_abort true? if so, we should probably just
// exit this function early, no need to keep the picker
// state up-to-date, right?
disk_io_job sj;
sj.piece = j->piece;
on_piece_fail_sync(&sj, block_finished);
}
}
update_gauge();
}
if (j->error.ec == boost::system::errc::not_enough_memory)
{ {
if (alerts().should_post<file_error_alert>()) if (alerts().should_post<file_error_alert>())
alerts().emplace_alert<file_error_alert>(j->error.ec alerts().emplace_alert<file_error_alert>(error.ec
, resolve_filename(j->error.file), j->error.operation_str(), get_handle()); , resolve_filename(error.file), error.operation_str(), get_handle());
if (c) c->disconnect(errors::no_memory, op_file); if (c) c->disconnect(errors::no_memory, op_file);
return; return;
} }
if (j->error.ec == boost::asio::error::operation_aborted) return; if (error.ec == boost::asio::error::operation_aborted) return;
// notify the user of the error // notify the user of the error
if (alerts().should_post<file_error_alert>()) if (alerts().should_post<file_error_alert>())
alerts().emplace_alert<file_error_alert>(j->error.ec alerts().emplace_alert<file_error_alert>(error.ec
, resolve_filename(j->error.file), j->error.operation_str(), get_handle()); , resolve_filename(error.file), error.operation_str(), get_handle());
// if a write operation failed, and future writes are likely to // if a write operation failed, and future writes are likely to
// fail, while reads may succeed, just set the torrent to upload mode // fail, while reads may succeed, just set the torrent to upload mode
// if we make an incorrect assumption here, it's not the end of the // if we make an incorrect assumption here, it's not the end of the
// world, if we ever issue a read request and it fails as well, we // world, if we ever issue a read request and it fails as well, we
// won't get in here and we'll actually end up pausing the torrent // won't get in here and we'll actually end up pausing the torrent
if (j->action == disk_io_job::write if (rw == disk_class::write
&& (j->error.ec == boost::system::errc::read_only_file_system && (error.ec == boost::system::errc::read_only_file_system
|| j->error.ec == boost::system::errc::permission_denied || error.ec == boost::system::errc::permission_denied
|| j->error.ec == boost::system::errc::operation_not_permitted || error.ec == boost::system::errc::operation_not_permitted
|| j->error.ec == boost::system::errc::no_space_on_device || error.ec == boost::system::errc::no_space_on_device
|| j->error.ec == boost::system::errc::file_too_large)) || error.ec == boost::system::errc::file_too_large))
{ {
// if we failed to write, stop downloading and just // if we failed to write, stop downloading and just
// keep seeding. // keep seeding.
@ -1128,7 +1090,7 @@ namespace libtorrent
} }
// put the torrent in an error-state // put the torrent in an error-state
set_error(j->error.ec, j->error.file); set_error(error.ec, error.file);
// if the error appears to be more serious than a full disk, just pause the torrent // if the error appears to be more serious than a full disk, just pause the torrent
pause(); pause();
@ -1178,7 +1140,7 @@ namespace libtorrent
{ {
rp->fail = true; rp->fail = true;
rp->error = j->error.ec; rp->error = j->error.ec;
handle_disk_error(j); handle_disk_error("read", j->error);
} }
else else
{ {
@ -1334,7 +1296,7 @@ namespace libtorrent
if (j->ret == -1) if (j->ret == -1)
{ {
handle_disk_error(j); handle_disk_error("write", j->error);
return; return;
} }
@ -1988,7 +1950,7 @@ namespace libtorrent
{ {
TORRENT_ASSERT(m_outstanding_check_files == false); TORRENT_ASSERT(m_outstanding_check_files == false);
m_add_torrent_params.reset(); m_add_torrent_params.reset();
handle_disk_error(j); handle_disk_error("check_resume_data", j->error);
auto_managed(false); auto_managed(false);
pause(); pause();
set_state(torrent_status::checking_files); set_state(torrent_status::checking_files);
@ -2240,7 +2202,7 @@ namespace libtorrent
if (j->ret == disk_interface::fatal_disk_error) if (j->ret == disk_interface::fatal_disk_error)
{ {
handle_disk_error(j); handle_disk_error("force_recheck", j->error);
return; return;
} }
if (j->ret == 0) if (j->ret == 0)
@ -3708,7 +3670,7 @@ namespace libtorrent
} }
else if (ret == -1) else if (ret == -1)
{ {
handle_disk_error(j); handle_disk_error("piece_verified", j->error);
} }
else else
{ {