|
|
|
|
@ -2,8 +2,10 @@
|
|
|
|
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
|
|
|
|
|
|
|
|
|
#include "cdrom_async_reader.h"
|
|
|
|
|
#include "host.h"
|
|
|
|
|
|
|
|
|
|
#include "common/assert.h"
|
|
|
|
|
#include "common/error.h"
|
|
|
|
|
#include "common/log.h"
|
|
|
|
|
#include "common/threading.h"
|
|
|
|
|
#include "common/timer.h"
|
|
|
|
|
@ -12,6 +14,7 @@ LOG_CHANNEL(CDROMAsyncReader);
|
|
|
|
|
|
|
|
|
|
namespace CDROMAsyncReader {
|
|
|
|
|
|
|
|
|
|
static bool IsReadaheadEnabled();
|
|
|
|
|
static void EmptyBuffers();
|
|
|
|
|
static bool ReadSectorIntoBuffer(std::unique_lock<Threading::Mutex>& lock);
|
|
|
|
|
static void ReadSectorNonThreaded(CDImage::LBA lba);
|
|
|
|
|
@ -26,17 +29,19 @@ struct Locals
|
|
|
|
|
|
|
|
|
|
std::atomic<CDImage::LBA> next_position{};
|
|
|
|
|
std::atomic_bool next_position_set{false};
|
|
|
|
|
std::atomic_bool shutdown_flag{true};
|
|
|
|
|
std::atomic_bool shutdown_flag{false};
|
|
|
|
|
std::atomic_bool readahead_enabled{false};
|
|
|
|
|
|
|
|
|
|
std::atomic_bool is_reading{false};
|
|
|
|
|
std::atomic_bool can_readahead{false};
|
|
|
|
|
std::atomic_bool seek_error{false};
|
|
|
|
|
|
|
|
|
|
std::vector<BufferSlot> buffers;
|
|
|
|
|
std::atomic<u32> buffer_front{0};
|
|
|
|
|
std::atomic<u32> buffer_back{0};
|
|
|
|
|
std::atomic<u32> buffer_count{0};
|
|
|
|
|
|
|
|
|
|
std::vector<BufferSlot> buffers;
|
|
|
|
|
|
|
|
|
|
Threading::Mutex mutex;
|
|
|
|
|
Threading::ConditionVariable do_read_cv;
|
|
|
|
|
Threading::ConditionVariable notify_read_complete_cv;
|
|
|
|
|
@ -50,32 +55,32 @@ ALIGN_TO_CACHE_LINE static Locals s_locals;
|
|
|
|
|
|
|
|
|
|
CDImage::LBA CDROMAsyncReader::GetLastReadSector()
|
|
|
|
|
{
|
|
|
|
|
return s_locals.buffers[s_locals.buffer_front.load()].lba;
|
|
|
|
|
return s_locals.buffers[s_locals.buffer_front.load(std::memory_order_acquire)].lba;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const CDROMAsyncReader::SectorBuffer& CDROMAsyncReader::GetSectorBuffer()
|
|
|
|
|
{
|
|
|
|
|
return s_locals.buffers[s_locals.buffer_front.load()].data;
|
|
|
|
|
return s_locals.buffers[s_locals.buffer_front.load(std::memory_order_acquire)].data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const CDImage::SubChannelQ& CDROMAsyncReader::GetSectorSubQ()
|
|
|
|
|
{
|
|
|
|
|
return s_locals.buffers[s_locals.buffer_front.load()].subq;
|
|
|
|
|
return s_locals.buffers[s_locals.buffer_front.load(std::memory_order_acquire)].subq;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u32 CDROMAsyncReader::GetBufferedSectorCount()
|
|
|
|
|
{
|
|
|
|
|
return s_locals.buffer_count.load();
|
|
|
|
|
return s_locals.buffer_count.load(std::memory_order_acquire);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CDROMAsyncReader::HasBufferedSectors()
|
|
|
|
|
{
|
|
|
|
|
return (s_locals.buffer_count.load() > 0);
|
|
|
|
|
return (s_locals.buffer_count.load(std::memory_order_acquire) > 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u32 CDROMAsyncReader::GetReadaheadCount()
|
|
|
|
|
{
|
|
|
|
|
return static_cast<u32>(s_locals.buffers.size());
|
|
|
|
|
return s_locals.readahead_enabled.load(std::memory_order_relaxed) ? static_cast<u32>(s_locals.buffers.size()) : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CDROMAsyncReader::HasMedia()
|
|
|
|
|
@ -93,54 +98,47 @@ const std::string& CDROMAsyncReader::GetMediaPath()
|
|
|
|
|
return s_locals.media->GetPath();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CDROMAsyncReader::IsUsingThread()
|
|
|
|
|
{
|
|
|
|
|
return s_locals.read_thread.Joinable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDROMAsyncReader::StartThread(u32 readahead_count)
|
|
|
|
|
bool CDROMAsyncReader::ProcessStartup(Error* error)
|
|
|
|
|
{
|
|
|
|
|
if (IsUsingThread())
|
|
|
|
|
StopThread();
|
|
|
|
|
|
|
|
|
|
s_locals.buffers.clear();
|
|
|
|
|
s_locals.buffers.resize(readahead_count);
|
|
|
|
|
EmptyBuffers();
|
|
|
|
|
if (!s_locals.read_thread.Start(&CDROMAsyncReader::WorkerThreadEntryPoint))
|
|
|
|
|
{
|
|
|
|
|
Error::SetStringView(error, "Failed to start CDROMAsyncReader thread");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s_locals.shutdown_flag.store(false);
|
|
|
|
|
s_locals.read_thread.Start(&CDROMAsyncReader::WorkerThreadEntryPoint);
|
|
|
|
|
INFO_LOG("Read thread started with readahead of {} sectors", readahead_count);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDROMAsyncReader::StopThread()
|
|
|
|
|
void CDROMAsyncReader::ProcessShutdown()
|
|
|
|
|
{
|
|
|
|
|
if (!IsUsingThread())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock lock(s_locals.mutex);
|
|
|
|
|
s_locals.shutdown_flag.store(true);
|
|
|
|
|
std::lock_guard lock(s_locals.mutex);
|
|
|
|
|
s_locals.shutdown_flag.store(true, std::memory_order_release);
|
|
|
|
|
s_locals.do_read_cv.notify_one();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s_locals.read_thread.Join();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDROMAsyncReader::SetReadaheadSectors(u32 readahead_sectors)
|
|
|
|
|
{
|
|
|
|
|
CancelReadahead();
|
|
|
|
|
EmptyBuffers();
|
|
|
|
|
s_locals.buffers.clear();
|
|
|
|
|
s_locals.buffers.resize(std::max(readahead_sectors, 1u));
|
|
|
|
|
s_locals.readahead_enabled.store(readahead_sectors >= 1, std::memory_order_relaxed);
|
|
|
|
|
DEV_LOG("Readahead set to {} sectors", readahead_sectors);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDROMAsyncReader::SetMedia(std::unique_ptr<CDImage> media)
|
|
|
|
|
{
|
|
|
|
|
if (IsUsingThread())
|
|
|
|
|
CancelReadahead();
|
|
|
|
|
|
|
|
|
|
CancelReadahead();
|
|
|
|
|
s_locals.media = std::move(media);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<CDImage> CDROMAsyncReader::RemoveMedia()
|
|
|
|
|
{
|
|
|
|
|
if (IsUsingThread())
|
|
|
|
|
CancelReadahead();
|
|
|
|
|
|
|
|
|
|
CancelReadahead();
|
|
|
|
|
return std::move(s_locals.media);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -183,55 +181,62 @@ bool CDROMAsyncReader::Precache(ProgressCallback* callback, Error* error)
|
|
|
|
|
|
|
|
|
|
void CDROMAsyncReader::QueueReadSector(CDImage::LBA lba)
|
|
|
|
|
{
|
|
|
|
|
if (!IsUsingThread())
|
|
|
|
|
if (!IsReadaheadEnabled())
|
|
|
|
|
{
|
|
|
|
|
ReadSectorNonThreaded(lba);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const u32 buffer_count = s_locals.buffer_count.load();
|
|
|
|
|
if (buffer_count > 0)
|
|
|
|
|
std::unique_lock lock(s_locals.mutex);
|
|
|
|
|
|
|
|
|
|
// If there's already a seek pending, the existing buffers belong to the
|
|
|
|
|
// old position and must not satisfy this newer request.
|
|
|
|
|
if (!s_locals.next_position_set.load(std::memory_order_relaxed))
|
|
|
|
|
{
|
|
|
|
|
// don't re-read the same sector if it was the last one we read
|
|
|
|
|
// the CDC code does this when seeking->reading
|
|
|
|
|
const u32 buffer_front = s_locals.buffer_front.load();
|
|
|
|
|
if (s_locals.buffers[buffer_front].lba == lba)
|
|
|
|
|
const u32 buffer_count = s_locals.buffer_count.load(std::memory_order_acquire);
|
|
|
|
|
if (buffer_count > 0)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_LOG("Skipping re-reading same sector {}", lba);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// don't re-read the same sector if it was the last one we read
|
|
|
|
|
// the CDC code does this when seeking->reading
|
|
|
|
|
const u32 buffer_front = s_locals.buffer_front.load(std::memory_order_acquire);
|
|
|
|
|
if (s_locals.buffers[buffer_front].lba == lba)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_LOG("Skipping re-reading same sector {}", lba);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// did we readahead to the correct sector?
|
|
|
|
|
const u32 next_buffer = (buffer_front + 1) % static_cast<u32>(s_locals.buffers.size());
|
|
|
|
|
if (s_locals.buffer_count > 1 && s_locals.buffers[next_buffer].lba == lba)
|
|
|
|
|
{
|
|
|
|
|
// great, don't need a seek, but still kick the thread to start reading ahead again
|
|
|
|
|
DEBUG_LOG("Readahead buffer hit for sector {}", lba);
|
|
|
|
|
s_locals.buffer_front.store(next_buffer);
|
|
|
|
|
s_locals.buffer_count.fetch_sub(1);
|
|
|
|
|
s_locals.can_readahead.store(true);
|
|
|
|
|
s_locals.do_read_cv.notify_one();
|
|
|
|
|
return;
|
|
|
|
|
// did we readahead to the correct sector?
|
|
|
|
|
const u32 next_buffer = (buffer_front + 1) % static_cast<u32>(s_locals.buffers.size());
|
|
|
|
|
if (buffer_count > 1 && s_locals.buffers[next_buffer].lba == lba)
|
|
|
|
|
{
|
|
|
|
|
// great, don't need a seek, but still kick the thread to start reading ahead again
|
|
|
|
|
DEBUG_LOG("Readahead buffer hit for sector {}", lba);
|
|
|
|
|
s_locals.buffer_front.store(next_buffer, std::memory_order_release);
|
|
|
|
|
s_locals.buffer_count.fetch_sub(1, std::memory_order_release);
|
|
|
|
|
s_locals.can_readahead.store(true, std::memory_order_release);
|
|
|
|
|
s_locals.do_read_cv.notify_one();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we need to toss away our readahead and start fresh
|
|
|
|
|
// We either missed, or another seek was already queued.
|
|
|
|
|
// Replace the pending position so the newest request wins.
|
|
|
|
|
DEBUG_LOG("Readahead buffer miss, queueing seek to {}", lba);
|
|
|
|
|
std::unique_lock lock(s_locals.mutex);
|
|
|
|
|
s_locals.next_position_set.store(true);
|
|
|
|
|
s_locals.next_position = lba;
|
|
|
|
|
s_locals.next_position.store(lba, std::memory_order_relaxed);
|
|
|
|
|
s_locals.next_position_set.store(true, std::memory_order_release);
|
|
|
|
|
s_locals.do_read_cv.notify_one();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CDROMAsyncReader::ReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ* subq, SectorBuffer* data)
|
|
|
|
|
{
|
|
|
|
|
if (!IsUsingThread())
|
|
|
|
|
if (!IsReadaheadEnabled())
|
|
|
|
|
return InternalReadSectorUncached(lba, subq, data);
|
|
|
|
|
|
|
|
|
|
std::unique_lock lock(s_locals.mutex);
|
|
|
|
|
|
|
|
|
|
// wait until the read thread is idle
|
|
|
|
|
s_locals.notify_read_complete_cv.wait(lock, []() { return !s_locals.is_reading.load(); });
|
|
|
|
|
s_locals.notify_read_complete_cv.wait(lock, []() { return !s_locals.is_reading.load(std::memory_order_acquire); });
|
|
|
|
|
|
|
|
|
|
// read while the lock is held so it has to wait
|
|
|
|
|
const CDImage::LBA prev_lba = s_locals.media->GetPositionOnDisc();
|
|
|
|
|
@ -239,7 +244,7 @@ bool CDROMAsyncReader::ReadSectorUncached(CDImage::LBA lba, CDImage::SubChannelQ
|
|
|
|
|
if (!s_locals.media->Seek(prev_lba)) [[unlikely]]
|
|
|
|
|
{
|
|
|
|
|
ERROR_LOG("Failed to re-seek to cached position {}", prev_lba);
|
|
|
|
|
s_locals.can_readahead.store(false);
|
|
|
|
|
s_locals.can_readahead.store(false, std::memory_order_release);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
@ -264,11 +269,12 @@ bool CDROMAsyncReader::InternalReadSectorUncached(CDImage::LBA lba, CDImage::Sub
|
|
|
|
|
|
|
|
|
|
bool CDROMAsyncReader::WaitForReadToComplete()
|
|
|
|
|
{
|
|
|
|
|
// Safe without locking with memory_order_seq_cst.
|
|
|
|
|
if (!s_locals.next_position_set.load() && s_locals.buffer_count.load() > 0)
|
|
|
|
|
if (!s_locals.next_position_set.load(std::memory_order_acquire) &&
|
|
|
|
|
s_locals.buffer_count.load(std::memory_order_acquire) > 0)
|
|
|
|
|
{
|
|
|
|
|
TRACE_LOG("Returning sector {}", s_locals.buffers[s_locals.buffer_front.load()].lba);
|
|
|
|
|
return s_locals.buffers[s_locals.buffer_front.load()].result;
|
|
|
|
|
const u32 buffer_index = s_locals.buffer_front.load(std::memory_order_acquire);
|
|
|
|
|
TRACE_LOG("Returning sector {}", s_locals.buffers[buffer_index].lba);
|
|
|
|
|
return s_locals.buffers[buffer_index].result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Timer wait_timer;
|
|
|
|
|
@ -276,15 +282,17 @@ bool CDROMAsyncReader::WaitForReadToComplete()
|
|
|
|
|
|
|
|
|
|
std::unique_lock lock(s_locals.mutex);
|
|
|
|
|
s_locals.notify_read_complete_cv.wait(lock, []() {
|
|
|
|
|
return (s_locals.buffer_count.load() > 0 || s_locals.seek_error.load()) && !s_locals.next_position_set.load();
|
|
|
|
|
return (s_locals.buffer_count.load(std::memory_order_acquire) > 0 ||
|
|
|
|
|
s_locals.seek_error.load(std::memory_order_acquire)) &&
|
|
|
|
|
!s_locals.next_position_set.load(std::memory_order_acquire);
|
|
|
|
|
});
|
|
|
|
|
if (s_locals.seek_error.load()) [[unlikely]]
|
|
|
|
|
if (s_locals.seek_error.load(std::memory_order_acquire)) [[unlikely]]
|
|
|
|
|
{
|
|
|
|
|
s_locals.seek_error.store(false);
|
|
|
|
|
s_locals.seek_error.store(false, std::memory_order_release);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const u32 front = s_locals.buffer_front.load();
|
|
|
|
|
const u32 front = s_locals.buffer_front.load(std::memory_order_acquire);
|
|
|
|
|
const double wait_time = wait_timer.GetTimeMilliseconds();
|
|
|
|
|
if (wait_time > 1.0f) [[unlikely]]
|
|
|
|
|
WARNING_LOG("Had to wait {:.2f} msec for LBA {}", wait_time, s_locals.buffers[front].lba);
|
|
|
|
|
@ -295,36 +303,46 @@ bool CDROMAsyncReader::WaitForReadToComplete()
|
|
|
|
|
|
|
|
|
|
void CDROMAsyncReader::WaitForIdle()
|
|
|
|
|
{
|
|
|
|
|
if (!IsUsingThread())
|
|
|
|
|
if (!IsReadaheadEnabled())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
std::unique_lock lock(s_locals.mutex);
|
|
|
|
|
s_locals.notify_read_complete_cv.wait(
|
|
|
|
|
lock, []() { return (!s_locals.is_reading.load() && !s_locals.next_position_set.load()); });
|
|
|
|
|
s_locals.notify_read_complete_cv.wait(lock, []() {
|
|
|
|
|
return (!s_locals.is_reading.load(std::memory_order_acquire) &&
|
|
|
|
|
!s_locals.next_position_set.load(std::memory_order_acquire));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CDROMAsyncReader::IsReadaheadEnabled()
|
|
|
|
|
{
|
|
|
|
|
// NOTE: Not called on worker thread.
|
|
|
|
|
DebugAssert(Host::IsOnCoreThread());
|
|
|
|
|
return s_locals.readahead_enabled.load(std::memory_order_relaxed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDROMAsyncReader::EmptyBuffers()
|
|
|
|
|
{
|
|
|
|
|
s_locals.buffer_front.store(0);
|
|
|
|
|
s_locals.buffer_back.store(0);
|
|
|
|
|
s_locals.buffer_count.store(0);
|
|
|
|
|
s_locals.buffer_front.store(0, std::memory_order_release);
|
|
|
|
|
s_locals.buffer_back.store(0, std::memory_order_release);
|
|
|
|
|
s_locals.buffer_count.store(0, std::memory_order_release);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CDROMAsyncReader::ReadSectorIntoBuffer(std::unique_lock<Threading::Mutex>& lock)
|
|
|
|
|
{
|
|
|
|
|
Timer timer;
|
|
|
|
|
|
|
|
|
|
const u32 slot = s_locals.buffer_back.load();
|
|
|
|
|
const u32 slot = s_locals.buffer_back.load(std::memory_order_acquire);
|
|
|
|
|
s_locals.buffer_back.store((slot + 1) % static_cast<u32>(s_locals.buffers.size()));
|
|
|
|
|
|
|
|
|
|
BufferSlot& buffer = s_locals.buffers[slot];
|
|
|
|
|
buffer.lba = s_locals.media->GetPositionOnDisc();
|
|
|
|
|
s_locals.is_reading.store(true);
|
|
|
|
|
s_locals.is_reading.store(true, std::memory_order_release);
|
|
|
|
|
lock.unlock();
|
|
|
|
|
|
|
|
|
|
TRACE_LOG("Reading LBA {}...", buffer.lba);
|
|
|
|
|
|
|
|
|
|
buffer.result = s_locals.media->ReadRawSector(buffer.data.data(), &buffer.subq);
|
|
|
|
|
const bool read_result = s_locals.media->ReadRawSector(buffer.data.data(), &buffer.subq);
|
|
|
|
|
buffer.result = read_result;
|
|
|
|
|
if (buffer.result) [[likely]]
|
|
|
|
|
{
|
|
|
|
|
const double read_time = timer.GetTimeMilliseconds();
|
|
|
|
|
@ -337,24 +355,24 @@ bool CDROMAsyncReader::ReadSectorIntoBuffer(std::unique_lock<Threading::Mutex>&
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lock.lock();
|
|
|
|
|
s_locals.is_reading.store(false);
|
|
|
|
|
s_locals.buffer_count.fetch_add(1);
|
|
|
|
|
s_locals.is_reading.store(false, std::memory_order_release);
|
|
|
|
|
s_locals.buffer_count.fetch_add(1, std::memory_order_release);
|
|
|
|
|
s_locals.notify_read_complete_cv.notify_all();
|
|
|
|
|
return true;
|
|
|
|
|
return read_result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDROMAsyncReader::ReadSectorNonThreaded(CDImage::LBA lba)
|
|
|
|
|
{
|
|
|
|
|
Timer timer;
|
|
|
|
|
|
|
|
|
|
s_locals.buffers.resize(1);
|
|
|
|
|
s_locals.seek_error.store(false);
|
|
|
|
|
Assert(!s_locals.buffers.empty());
|
|
|
|
|
s_locals.seek_error.store(false, std::memory_order_release);
|
|
|
|
|
EmptyBuffers();
|
|
|
|
|
|
|
|
|
|
if (s_locals.media->GetPositionOnDisc() != lba && !s_locals.media->Seek(lba))
|
|
|
|
|
{
|
|
|
|
|
WARNING_LOG("Seek to LBA {} failed", lba);
|
|
|
|
|
s_locals.seek_error.store(true);
|
|
|
|
|
s_locals.seek_error.store(true, std::memory_order_release);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -375,7 +393,7 @@ void CDROMAsyncReader::ReadSectorNonThreaded(CDImage::LBA lba)
|
|
|
|
|
ERROR_LOG("Read of LBA {} failed", buffer.lba);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s_locals.buffer_count.fetch_add(1);
|
|
|
|
|
s_locals.buffer_count.fetch_add(1, std::memory_order_release);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDROMAsyncReader::CancelReadahead()
|
|
|
|
|
@ -385,35 +403,42 @@ void CDROMAsyncReader::CancelReadahead()
|
|
|
|
|
std::unique_lock lock(s_locals.mutex);
|
|
|
|
|
|
|
|
|
|
// wait until the read thread is idle
|
|
|
|
|
s_locals.notify_read_complete_cv.wait(lock, []() { return !s_locals.is_reading.load(); });
|
|
|
|
|
s_locals.notify_read_complete_cv.wait(lock, []() {
|
|
|
|
|
return !s_locals.is_reading.load(std::memory_order_acquire) &&
|
|
|
|
|
!s_locals.next_position_set.load(std::memory_order_acquire);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// prevent it from doing any more when it re-acquires the lock
|
|
|
|
|
s_locals.can_readahead.store(false);
|
|
|
|
|
s_locals.can_readahead.store(false, std::memory_order_release);
|
|
|
|
|
EmptyBuffers();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDROMAsyncReader::WorkerThreadEntryPoint()
|
|
|
|
|
{
|
|
|
|
|
Threading::SetNameOfCurrentThread("CDROM Async Reader");
|
|
|
|
|
|
|
|
|
|
std::unique_lock lock(s_locals.mutex);
|
|
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
|
{
|
|
|
|
|
s_locals.do_read_cv.wait(lock, []() {
|
|
|
|
|
return (s_locals.shutdown_flag.load() || s_locals.next_position_set.load() || s_locals.can_readahead.load());
|
|
|
|
|
return (s_locals.shutdown_flag.load(std::memory_order_acquire) ||
|
|
|
|
|
s_locals.next_position_set.load(std::memory_order_acquire) ||
|
|
|
|
|
s_locals.can_readahead.load(std::memory_order_acquire));
|
|
|
|
|
});
|
|
|
|
|
if (s_locals.shutdown_flag.load())
|
|
|
|
|
if (s_locals.shutdown_flag.load(std::memory_order_relaxed))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
|
{
|
|
|
|
|
if (s_locals.next_position_set.load())
|
|
|
|
|
if (s_locals.next_position_set.load(std::memory_order_acquire))
|
|
|
|
|
{
|
|
|
|
|
// discard buffers, we're seeking to a new location
|
|
|
|
|
const CDImage::LBA seek_location = s_locals.next_position.load();
|
|
|
|
|
const CDImage::LBA seek_location = s_locals.next_position.load(std::memory_order_relaxed);
|
|
|
|
|
EmptyBuffers();
|
|
|
|
|
s_locals.next_position_set.store(false);
|
|
|
|
|
s_locals.seek_error.store(false);
|
|
|
|
|
s_locals.is_reading.store(true);
|
|
|
|
|
s_locals.next_position_set.store(false, std::memory_order_release);
|
|
|
|
|
s_locals.seek_error.store(false, std::memory_order_release);
|
|
|
|
|
s_locals.is_reading.store(true, std::memory_order_release);
|
|
|
|
|
lock.unlock();
|
|
|
|
|
|
|
|
|
|
// seek without lock held in case it takes time
|
|
|
|
|
@ -422,10 +447,10 @@ void CDROMAsyncReader::WorkerThreadEntryPoint()
|
|
|
|
|
(s_locals.media->GetPositionOnDisc() == seek_location || s_locals.media->Seek(seek_location));
|
|
|
|
|
|
|
|
|
|
lock.lock();
|
|
|
|
|
s_locals.is_reading.store(false);
|
|
|
|
|
s_locals.is_reading.store(false, std::memory_order_release);
|
|
|
|
|
|
|
|
|
|
// did another request come in? abort if so
|
|
|
|
|
if (s_locals.next_position_set.load())
|
|
|
|
|
if (s_locals.next_position_set.load(std::memory_order_acquire))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// did we fail the seek?
|
|
|
|
|
@ -433,24 +458,24 @@ void CDROMAsyncReader::WorkerThreadEntryPoint()
|
|
|
|
|
{
|
|
|
|
|
// add the error result, and don't try to read ahead
|
|
|
|
|
WARNING_LOG("Seek to LBA {} failed", seek_location);
|
|
|
|
|
s_locals.seek_error.store(true);
|
|
|
|
|
s_locals.seek_error.store(true, std::memory_order_release);
|
|
|
|
|
s_locals.notify_read_complete_cv.notify_all();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// go go read ahead!
|
|
|
|
|
s_locals.can_readahead.store(true);
|
|
|
|
|
s_locals.can_readahead.store(true, std::memory_order_release);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!s_locals.can_readahead.load())
|
|
|
|
|
if (!s_locals.can_readahead.load(std::memory_order_acquire))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// readahead time! read as many sectors as we have space for
|
|
|
|
|
DEBUG_LOG("Reading ahead {} sectors...",
|
|
|
|
|
static_cast<u32>(s_locals.buffers.size()) - s_locals.buffer_count.load());
|
|
|
|
|
while (s_locals.buffer_count.load() < static_cast<u32>(s_locals.buffers.size()))
|
|
|
|
|
static_cast<u32>(s_locals.buffers.size()) - s_locals.buffer_count.load(std::memory_order_acquire));
|
|
|
|
|
while (s_locals.buffer_count.load(std::memory_order_acquire) < static_cast<u32>(s_locals.buffers.size()))
|
|
|
|
|
{
|
|
|
|
|
if (s_locals.next_position_set.load())
|
|
|
|
|
if (s_locals.next_position_set.load(std::memory_order_acquire))
|
|
|
|
|
{
|
|
|
|
|
// a seek request came in while we're reading, so bail out
|
|
|
|
|
break;
|
|
|
|
|
@ -462,7 +487,7 @@ void CDROMAsyncReader::WorkerThreadEntryPoint()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// readahead buffer is full or errored at this point
|
|
|
|
|
s_locals.can_readahead.store(false);
|
|
|
|
|
s_locals.can_readahead.store(false, std::memory_order_release);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|