diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 17b56ad17c..da112fd9d4 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -344,6 +344,7 @@ set(pcsx2DEV9Headers DEV9/DEV9.h DEV9/net.h DEV9/pcap_io.h + DEV9/SimpleQueue.h DEV9/smap.h ${pcsx2DEV9UIHeaders} ) diff --git a/pcsx2/DEV9/ATA/ATA.h b/pcsx2/DEV9/ATA/ATA.h index 6f5ad214d8..6a81cee8da 100644 --- a/pcsx2/DEV9/ATA/ATA.h +++ b/pcsx2/DEV9/ATA/ATA.h @@ -26,6 +26,8 @@ #include "PS2Edefs.h" #include "PS2Eext.h" +#include "../SimpleQueue.h" + class ATA { public: @@ -106,15 +108,11 @@ private: struct WriteQueueEntry { - std::atomic_bool ready{false}; - WriteQueueEntry* next; u8* data; u32 length; u64 sector; }; - - WriteQueueEntry* head = nullptr; - WriteQueueEntry* tail = nullptr; + SimpleQueue writeQueue; std::thread ioThread; bool ioRunning = false; @@ -213,10 +211,6 @@ private: bool HDD_CanAssessOrSetError(); void HDD_SetErrorAtTransferEnd(); - void QueueWrite(u64 sector, u8* data, u32 length); - bool DequeueWrite(u64* sector, u8** data, u32* length); - bool IsQueueEmpty(); - //Commands void IDE_ExecCmd(u16 value); diff --git a/pcsx2/DEV9/ATA/ATA_State.cpp b/pcsx2/DEV9/ATA/ATA_State.cpp index c2a31ba4ec..ecdfc78be4 100644 --- a/pcsx2/DEV9/ATA/ATA_State.cpp +++ b/pcsx2/DEV9/ATA/ATA_State.cpp @@ -53,10 +53,6 @@ int ATA::Open(ghc::filesystem::path hddPath) hddImage.seekg(0, std::ios::end); hddImageSize = hddImage.tellg(); - //Setup Write queue - tail = new WriteQueueEntry(); - head = tail; - { std::lock_guard ioSignallock(ioMutex); ioRead = false; @@ -84,20 +80,15 @@ void ATA::Close() ioThread.join(); ioRunning = false; } - //Delete queue - if (head != nullptr) - { - if (!IsQueueEmpty()) - { - Console.Error("DEV9: ATA: Write queue not empty"); - pxAssert(false); - abort(); //All data must be written at this point - } - delete head; - head = nullptr; - tail = nullptr; + //verify queue + if (!writeQueue.IsQueueEmpty()) + { + Console.Error("DEV9: ATA: Write queue not empty, possible data loss"); + pxAssert(false); + abort(); //All data must be written at this point } + //Close File Handle if (hddImage.is_open()) hddImage.close(); @@ -314,7 +305,7 @@ void ATA::Async(uint cycles) waitingCmd = nullptr; (this->*cmd)(); } - else if (!IsQueueEmpty()) //Flush cache + else if (!writeQueue.IsQueueEmpty()) //Flush cache { //Log_Info("Starting async write"); { diff --git a/pcsx2/DEV9/ATA/ATA_Transfer.cpp b/pcsx2/DEV9/ATA/ATA_Transfer.cpp index 204bdd3034..500805a6e6 100644 --- a/pcsx2/DEV9/ATA/ATA_Transfer.cpp +++ b/pcsx2/DEV9/ATA/ATA_Transfer.cpp @@ -89,18 +89,16 @@ void ATA::IO_Read() bool ATA::IO_Write() { - u64 sector = 0; - u8* data = nullptr; - u32 len = 0; - if (!DequeueWrite(§or, &data, &len)) + WriteQueueEntry entry; + if (!writeQueue.Dequeue(&entry)) { std::lock_guard ioSignallock(ioMutex); ioWrite = false; return false; } - hddImage.seekp(sector * 512, std::ios::beg); - hddImage.write((char*)data, len); + hddImage.seekp(entry.sector * 512, std::ios::beg); + hddImage.write((char*)entry.data, entry.length); if (hddImage.fail()) { Console.Error("DEV9: ATA: File write error"); @@ -108,7 +106,7 @@ bool ATA::IO_Write() abort(); } hddImage.flush(); - delete[] data; + delete[] entry.data; return true; } @@ -211,40 +209,3 @@ void ATA::HDD_SetErrorAtTransferEnd() HDD_SetLBA(currSect); } } - -//Used by EE thread only -void ATA::QueueWrite(u64 sector, u8* data, u32 length) -{ - WriteQueueEntry* newEntry = head; - newEntry->data = data; - newEntry->length = length; - newEntry->sector = sector; - - //Allocate Next entry - newEntry->next = new WriteQueueEntry(); - head = newEntry->next; - //Set ready - newEntry->ready.store(true); -} - -//Used by IO thread only -bool ATA::DequeueWrite(u64* sector, u8** data, u32* length) -{ - if (!tail->ready.load()) - return false; - - WriteQueueEntry* entry = tail; - tail = entry->next; - - *sector = entry->sector; - *data = entry->data; - *length = entry->length; - delete entry; - return true; -} - -//Used by EE thread only -bool ATA::IsQueueEmpty() -{ - return !tail->ready.load(); -} diff --git a/pcsx2/DEV9/ATA/Commands/ATA_CmdDMA.cpp b/pcsx2/DEV9/ATA/Commands/ATA_CmdDMA.cpp index df6438d2b7..9cb21a2345 100644 --- a/pcsx2/DEV9/ATA/Commands/ATA_CmdDMA.cpp +++ b/pcsx2/DEV9/ATA/Commands/ATA_CmdDMA.cpp @@ -62,7 +62,11 @@ void ATA::DRQCmdDMADataFromHost() } void ATA::PostCmdDMADataFromHost() { - QueueWrite(currentWriteSectors, currentWrite, currentWriteLength); + WriteQueueEntry entry{0}; + entry.data = currentWrite; + entry.length = currentWriteLength; + entry.sector = currentWriteSectors; + writeQueue.Enqueue(entry); currentWrite = nullptr; currentWriteLength = 0; currentWriteSectors = 0; diff --git a/pcsx2/DEV9/SimpleQueue.h b/pcsx2/DEV9/SimpleQueue.h new file mode 100644 index 0000000000..8b9bb9b8ba --- /dev/null +++ b/pcsx2/DEV9/SimpleQueue.h @@ -0,0 +1,110 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2021 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +//Designed to allow one thread to queue data to another thread +template +class SimpleQueue +{ +private: + struct SimpleQueueEntry + { + std::atomic_bool ready{false}; + SimpleQueueEntry* next; + T value; + }; + + std::atomic head{nullptr}; + SimpleQueueEntry* tail = nullptr; + +public: + SimpleQueue(); + + //Used by single queue thread (i.e. EE) + void Enqueue(T entry); + //Used by single worker thread (i.e. IO) + bool Dequeue(T* entry); + //May return false negative when another thread is mid Queue() + //Intended to only be used from queue thread + bool IsQueueEmpty(); + + ~SimpleQueue(); +}; + +template +SimpleQueue::SimpleQueue() +{ + tail = new SimpleQueueEntry(); + head.store(tail); +} + +template +void SimpleQueue::Enqueue(T entry) +{ + //Allocate Next entry, and assign to head + SimpleQueueEntry* newHead = new SimpleQueueEntry(); + SimpleQueueEntry* newEntry = head.exchange(newHead); + + //Fill in + newEntry->value = entry; + newEntry->next = newHead; + + //Set ready (can be dequeued) + newEntry->ready.store(true); +} + +template +bool SimpleQueue::Dequeue(T* entry) +{ + if (!tail->ready.load()) + return false; + + SimpleQueueEntry* retEntry = tail; + tail = retEntry->next; + + *entry = retEntry->value; + delete retEntry; + return true; +} + +//Note, next entry may not be ready to dequeue +template +bool SimpleQueue::IsQueueEmpty() +{ + return head.load() == tail; +} + +template +SimpleQueue::~SimpleQueue() +{ + if (head != nullptr) + { + if (!IsQueueEmpty()) + { + Console.Error("DEV9: Queue not empty"); + pxAssert(false); + + //Empty Queue + T entry; + while (!IsQueueEmpty()) + Dequeue(&entry); + } + + delete head; + head = nullptr; + tail = nullptr; + } +} diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj b/pcsx2/windows/VCprojects/pcsx2.vcxproj index 1ae0a57185..636516b172 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj @@ -650,6 +650,7 @@ + diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters index 82333fb182..a5c4b9b71f 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters @@ -1851,6 +1851,9 @@ System\Ps2\DEV9 + + System\Ps2\DEV9 + System\Ps2\DEV9