avoid concurrent access to hash offset

Since partial_hash::offset is not an atomic it is technically UD to access it
concurrently from multiple threads.
This commit is contained in:
Steven Siloti 2016-10-24 21:13:35 -07:00 committed by Arvid Norberg
parent 241e9fd25a
commit f36b9a805a
1 changed files with 20 additions and 10 deletions

View File

@ -2153,6 +2153,9 @@ namespace libtorrent
DLOG("kick_hasher: %d - %d (piece: %d offset: %d)\n" DLOG("kick_hasher: %d - %d (piece: %d offset: %d)\n"
, cursor, end, int(pe->piece), ph->offset); , cursor, end, int(pe->piece), ph->offset);
// save a local copy of offset to avoid concurrent access
int offset = ph->offset;
l.unlock(); l.unlock();
time_point start_time = clock_type::now(); time_point start_time = clock_type::now();
@ -2160,15 +2163,17 @@ namespace libtorrent
for (int i = cursor; i < end; ++i) for (int i = cursor; i < end; ++i)
{ {
cached_block_entry& bl = pe->blocks[i]; cached_block_entry& bl = pe->blocks[i];
int size = (std::min)(block_size, piece_size - ph->offset); int size = (std::min)(block_size, piece_size - offset);
ph->h.update(bl.buf, size); ph->h.update(bl.buf, size);
ph->offset += size; offset += size;
} }
boost::uint64_t hash_time = total_microseconds(clock_type::now() - start_time); boost::uint64_t hash_time = total_microseconds(clock_type::now() - start_time);
l.lock(); l.lock();
ph->offset = offset;
TORRENT_PIECE_ASSERT(pe->hashing, pe); TORRENT_PIECE_ASSERT(pe->hashing, pe);
TORRENT_PIECE_ASSERT(pe->hash, pe); TORRENT_PIECE_ASSERT(pe->hash, pe);
@ -2388,22 +2393,25 @@ namespace libtorrent
// to keep the cache footprint low, try to evict a volatile piece // to keep the cache footprint low, try to evict a volatile piece
m_disk_cache.try_evict_one_volatile(); m_disk_cache.try_evict_one_volatile();
// save a local copy of offset to avoid concurrent access
int offset = ph->offset;
l.unlock(); l.unlock();
int ret = 0; int ret = 0;
int next_locked_block = 0; int next_locked_block = 0;
for (int i = ph->offset / block_size; i < blocks_in_piece; ++i) for (int i = offset / block_size; i < blocks_in_piece; ++i)
{ {
file::iovec_t iov; file::iovec_t iov;
iov.iov_len = (std::min)(block_size, piece_size - ph->offset); iov.iov_len = (std::min)(block_size, piece_size - offset);
if (next_locked_block < num_locked_blocks if (next_locked_block < num_locked_blocks
&& locked_blocks[next_locked_block] == i) && locked_blocks[next_locked_block] == i)
{ {
++next_locked_block; ++next_locked_block;
TORRENT_PIECE_ASSERT(pe->blocks[i].buf, pe); TORRENT_PIECE_ASSERT(pe->blocks[i].buf, pe);
TORRENT_PIECE_ASSERT(ph->offset == i * block_size, pe); TORRENT_PIECE_ASSERT(offset == i * block_size, pe);
ph->offset += iov.iov_len; offset += iov.iov_len;
ph->h.update(pe->blocks[i].buf, iov.iov_len); ph->h.update(pe->blocks[i].buf, iov.iov_len);
} }
else else
@ -2436,9 +2444,9 @@ namespace libtorrent
time_point start_time = clock_type::now(); time_point start_time = clock_type::now();
TORRENT_PIECE_ASSERT(ph->offset == i * block_size, pe); TORRENT_PIECE_ASSERT(offset == i * block_size, pe);
ret = j->storage->get_storage_impl()->readv(&iov, 1, j->piece ret = j->storage->get_storage_impl()->readv(&iov, 1, j->piece
, ph->offset, file_flags, j->error); , offset, file_flags, j->error);
if (ret < 0) if (ret < 0)
{ {
@ -2474,8 +2482,8 @@ namespace libtorrent
m_stats_counters.inc_stats_counter(counters::disk_job_time, read_time); m_stats_counters.inc_stats_counter(counters::disk_job_time, read_time);
} }
TORRENT_PIECE_ASSERT(ph->offset == i * block_size, pe); TORRENT_PIECE_ASSERT(offset == i * block_size, pe);
ph->offset += iov.iov_len; offset += iov.iov_len;
ph->h.update(static_cast<char const*>(iov.iov_base), iov.iov_len); ph->h.update(static_cast<char const*>(iov.iov_base), iov.iov_len);
l.lock(); l.lock();
@ -2486,6 +2494,8 @@ namespace libtorrent
l.lock(); l.lock();
ph->offset = offset;
// decrement the refcounts of the blocks we just hashed // decrement the refcounts of the blocks we just hashed
for (int i = 0; i < num_locked_blocks; ++i) for (int i = 0; i < num_locked_blocks; ++i)
m_disk_cache.dec_block_refcount(pe, locked_blocks[i], block_cache::ref_hashing); m_disk_cache.dec_block_refcount(pe, locked_blocks[i], block_cache::ref_hashing);