void part_file::export_file(file& f, std::int64_t const offset, std::int64_t size, error_code& ec) { std::unique_lock<std::mutex> l(m_mutex); piece_index_t piece(int(offset / m_piece_size)); piece_index_t const end = piece_index_t(int(((offset + size) + m_piece_size - 1) / m_piece_size)); std::unique_ptr<char[]> buf; std::int64_t piece_offset = offset - std::int64_t(static_cast<int>(piece)) * m_piece_size; std::int64_t file_offset = 0; for (; piece < end; ++piece) { auto const i = m_piece_map.find(piece); int const block_to_copy = int(std::min(m_piece_size - piece_offset, size)); if (i != m_piece_map.end()) { slot_index_t const slot = i->second; open_file(file::read_only, ec); if (ec) return; if (!buf) buf.reset(new char[m_piece_size]); std::int64_t const slot_offset = std::int64_t(m_header_size) + std::int64_t(static_cast<int>(slot)) * m_piece_size; // don't hold the lock during disk I/O l.unlock(); file::iovec_t v = {buf.get(), std::size_t(block_to_copy)}; v.iov_len = std::size_t(m_file.readv(slot_offset + piece_offset, v, ec)); TORRENT_ASSERT(!ec); if (ec || v.iov_len == 0) return; std::int64_t const ret = f.writev(file_offset, v, ec); TORRENT_ASSERT(ec || ret == std::int64_t(v.iov_len)); if (ec || ret != std::int64_t(v.iov_len)) return; // we're done with the disk I/O, grab the lock again to update // the slot map l.lock(); if (block_to_copy == m_piece_size) { // since we released the lock, it's technically possible that // another thread removed this slot map entry, and invalidated // our iterator. Now that we hold the lock again, perform // another lookup to be sure. auto const j = m_piece_map.find(piece); if (j != m_piece_map.end()) { // if the slot moved, that's really suspicious TORRENT_ASSERT(j->second == slot); m_free_slots.push_back(j->second); m_piece_map.erase(j); m_dirty_metadata = true; } } } file_offset += block_to_copy; piece_offset = 0; size -= block_to_copy; } }