#include "cdrom_async_reader.h" #include "common/assert.h" #include "common/log.h" #include "common/timer.h" Log_SetChannel(CDROMAsyncReader); CDROMAsyncReader::CDROMAsyncReader() = default; CDROMAsyncReader::~CDROMAsyncReader() { StopThread(); } void CDROMAsyncReader::StartThread() { if (IsUsingThread()) return; m_shutdown_flag.store(false); m_read_thread = std::thread(&CDROMAsyncReader::WorkerThreadEntryPoint, this); } void CDROMAsyncReader::StopThread() { if (!IsUsingThread()) return; { std::unique_lock lock(m_mutex); if (m_sector_read_pending.load()) m_notify_read_complete_cv.wait(lock, [this]() { return !m_sector_read_pending.load(); }); m_shutdown_flag.store(true); m_do_read_cv.notify_one(); } m_read_thread.join(); } void CDROMAsyncReader::SetMedia(std::unique_ptr media) { WaitForReadToComplete(); m_media = std::move(media); } void CDROMAsyncReader::RemoveMedia() { WaitForReadToComplete(); m_media.reset(); } void CDROMAsyncReader::QueueReadSector(CDImage::LBA lba) { if (!IsUsingThread()) { m_sector_read_pending.store(true); m_next_position_set.store(true); m_next_position = lba; DoSectorRead(); return; } std::unique_lock lock(m_mutex); if (m_sector_read_pending.load()) m_notify_read_complete_cv.wait(lock, [this]() { return !m_sector_read_pending.load(); }); // don't re-read the same sector if it was the last one we read // the CDC code does this when seeking->reading if (m_last_read_sector == lba && m_sector_read_result.load()) { Log_DebugPrintf("Skipping re-reading same sector %u", lba); return; } m_sector_read_pending.store(true); m_next_position_set.store(true); m_next_position = lba; m_do_read_cv.notify_one(); } bool CDROMAsyncReader::ReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ* subq, SectorBuffer* data) { WaitForReadToComplete(); if (m_media->GetPositionOnDisc() != lba && !m_media->Seek(lba)) { Log_WarningPrintf("Seek to LBA %u failed", lba); return false; } if ((subq && !m_media->ReadSubChannelQ(subq)) || (data && !m_media->ReadRawSector(data->data()))) { Log_WarningPrintf("Read of LBA %u failed", lba); return false; } return true; } void CDROMAsyncReader::QueueReadNextSector() { if (!IsUsingThread()) { m_sector_read_pending.store(true); DoSectorRead(); return; } std::unique_lock lock(m_mutex); if (m_sector_read_pending.load()) m_notify_read_complete_cv.wait(lock, [this]() { return !m_sector_read_pending.load(); }); m_sector_read_pending.store(true); m_do_read_cv.notify_one(); } bool CDROMAsyncReader::WaitForReadToComplete() { if (!IsUsingThread()) return m_sector_read_result.load(); std::unique_lock lock(m_mutex); if (m_sector_read_pending.load()) { Log_DebugPrintf("Sector read pending, waiting"); Common::Timer wait_timer; m_notify_read_complete_cv.wait(lock, [this]() { return !m_sector_read_pending.load(); }); const double wait_time = wait_timer.GetTimeMilliseconds(); if (wait_time > 1.0f) Log_WarningPrintf("Had to wait %.2f msec for LBA %u", wait_time, m_last_read_sector); } return m_sector_read_result.load(); } void CDROMAsyncReader::DoSectorRead() { Common::Timer timer; if (m_next_position_set.load()) { if (m_media->GetPositionOnDisc() != m_next_position && !m_media->Seek(m_next_position)) { Log_WarningPrintf("Seek to LBA %u failed", m_next_position); m_sector_read_result.store(false); return; } } CDImage::LBA pos = m_media->GetPositionOnDisc(); if (!m_media->ReadSubChannelQ(&m_subq) || !m_media->ReadRawSector(m_sector_buffer.data())) { m_sector_read_result.store(false); Log_WarningPrintf("Read of LBA %u failed", pos); return; } m_last_read_sector = pos; m_sector_read_result.store(true); const double read_time = timer.GetTimeMilliseconds(); if (read_time > 1.0f) Log_DevPrintf("Read LBA %u took %.2f msec", pos, read_time); } void CDROMAsyncReader::WorkerThreadEntryPoint() { std::unique_lock lock(m_mutex); while (!m_shutdown_flag.load()) { m_do_read_cv.wait(lock, [this]() { return (m_shutdown_flag.load() || m_sector_read_pending.load()); }); if (m_sector_read_pending.load()) { lock.unlock(); DoSectorRead(); lock.lock(); m_sector_read_pending.store(false); m_notify_read_complete_cv.notify_one(); } } }