hold an owning reference to storage objects in try_flush_write_blocks

It is possible for all other references to a storage object to be
destroyed while try_flush_write_blocks is running. If the storage is
destroyed then find_piece will crash when trying to re-aquire the
shared_ptr. To prevent this, keep the storage alive by holding a
shared_ptr to it in try_flush_write_blocks.

Normally the fence job when stopping a torrent would prevent the storage
object from being destroyed until all flush jobs are complete.
try_flush_write_blocks can be run after every disk job though so it has
the potential to "stradle the fence".

If the associated torrent does get unloaded then it is expected that
find_piece will return NULL thus causing the entry to be ignored.
This commit is contained in:
Steven Siloti 2017-08-29 13:29:00 -07:00 committed by Arvid Norberg
parent 52ccad23b9
commit 621da10e60
1 changed files with 6 additions and 6 deletions

View File

@ -899,22 +899,22 @@ namespace libtorrent
DLOG("try_flush_write_blocks: %d\n", num);
list_iterator<cached_piece_entry> range = m_disk_cache.write_lru_pieces();
std::vector<std::pair<piece_manager*, int> > pieces;
std::vector<std::pair<boost::shared_ptr<piece_manager>, int> > pieces;
pieces.reserve(m_disk_cache.num_write_lru_pieces());
for (list_iterator<cached_piece_entry> p = range; p.get() && num > 0; p.next())
{
cached_piece_entry* e = p.get();
if (e->num_dirty == 0) continue;
pieces.push_back(std::make_pair(e->storage.get(), int(e->piece)));
pieces.push_back(std::make_pair(e->storage, int(e->piece)));
}
for (std::vector<std::pair<piece_manager*, int> >::iterator i = pieces.begin()
for (std::vector<std::pair<boost::shared_ptr<piece_manager>, int> >::iterator i = pieces.begin()
, end(pieces.end()); i != end; ++i)
{
// TODO: instead of doing a lookup each time through the loop, save
// cached_piece_entry pointers with piece_refcount incremented to pin them
cached_piece_entry* pe = m_disk_cache.find_piece(i->first, i->second);
cached_piece_entry* pe = m_disk_cache.find_piece(i->first.get(), i->second);
if (pe == NULL) continue;
// another thread may flush this piece while we're looping and
@ -941,10 +941,10 @@ namespace libtorrent
// if we still need to flush blocks, start over and flush
// everything in LRU order (degrade to lru cache eviction)
for (std::vector<std::pair<piece_manager*, int> >::iterator i = pieces.begin()
for (std::vector<std::pair<boost::shared_ptr<piece_manager>, int> >::iterator i = pieces.begin()
, end(pieces.end()); i != end; ++i)
{
cached_piece_entry* pe = m_disk_cache.find_piece(i->first, i->second);
cached_piece_entry* pe = m_disk_cache.find_piece(i->first.get(), i->second);
if (pe == NULL) continue;
if (pe->num_dirty == 0) continue;