Example #1
0
	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;
		}
	}