diff --git a/CMakeLists.txt b/CMakeLists.txt index 36f71bfb8..95f44d975 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -898,6 +898,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/scePauth.h Core/HW/atrac3plus.cpp Core/HW/atrac3plus.h + Core/HW/AsyncIOManager.cpp + Core/HW/AsyncIOManager.h Core/HW/MediaEngine.cpp Core/HW/MediaEngine.h Core/HW/MpegDemux.cpp diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 9d1b4bf89..90dbf87d3 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -74,6 +74,7 @@ set(SRCS HLE/sceParseHttp.cpp HLE/sceVaudio.cpp HLE/sceAudiocodec.cpp + HW/AsyncIOManager.cpp HW/MemoryStick.cpp HW/MediaEngine.cpp HW/SasAudio.cpp diff --git a/Core/Config.cpp b/Core/Config.cpp index e3c614c6e..863184e3d 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -90,6 +90,7 @@ void Config::Load(const char *iniFileName) cpu->Get("Jit", &bJit, true); #endif cpu->Get("SeparateCPUThread", &bSeparateCPUThread, false); + cpu->Get("SeparateIOThread", &bSeparateIOThread, true); cpu->Get("FastMemory", &bFastMemory, false); cpu->Get("CPUSpeed", &iLockedCPUSpeed, false); @@ -228,6 +229,7 @@ void Config::Save() IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU"); cpu->Set("Jit", bJit); cpu->Set("SeparateCPUThread", bSeparateCPUThread); + cpu->Set("SeparateIOThread", bSeparateIOThread); cpu->Set("FastMemory", bFastMemory); cpu->Set("CPUSpeed", iLockedCPUSpeed); diff --git a/Core/Config.h b/Core/Config.h index 042dc6786..094e08dc4 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -58,6 +58,7 @@ public: bool bJit; // Definitely cannot be changed while game is running. bool bSeparateCPUThread; + bool bSeparateIOThread; int iLockedCPUSpeed; bool bAutoSaveSymbolMap; std::string sReportHost; diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 1416bbb4d..60a98ee84 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -251,6 +251,7 @@ + @@ -437,6 +438,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index d9861b0b9..e96aaf9a5 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -451,6 +451,9 @@ FileSystems + + HW + @@ -840,6 +843,9 @@ Core + + HW + diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index f34ff7d9c..b26c82820 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -16,33 +16,36 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include "native/thread/thread.h" +#include "native/thread/threadutil.h" #include "Core/Config.h" #include "Core/System.h" #include "Core/Host.h" #include "Core/SaveState.h" -#include "HLE.h" +#include "Core/HLE/HLE.h" #include "Core/MIPS/MIPS.h" #include "Core/HW/MemoryStick.h" +#include "Core/HW/AsyncIOManager.h" #include "Core/CoreTiming.h" #include "Core/Reporting.h" -#include "../FileSystems/FileSystem.h" -#include "../FileSystems/MetaFileSystem.h" -#include "../FileSystems/ISOFileSystem.h" -#include "../FileSystems/DirectoryFileSystem.h" +#include "Core/FileSystems/FileSystem.h" +#include "Core/FileSystems/MetaFileSystem.h" +#include "Core/FileSystems/ISOFileSystem.h" +#include "Core/FileSystems/DirectoryFileSystem.h" extern "C" { #include "ext/libkirk/amctrl.h" }; -#include "sceIo.h" -#include "sceRtc.h" -#include "sceKernel.h" -#include "sceKernelMemory.h" -#include "sceKernelThread.h" +#include "Core/HLE/sceIo.h" +#include "Core/HLE/sceRtc.h" +#include "Core/HLE/sceKernel.h" +#include "Core/HLE/sceKernelMemory.h" +#include "Core/HLE/sceKernelThread.h" // For headless screenshots. -#include "sceDisplay.h" +#include "Core/HLE/sceDisplay.h" const int ERROR_ERRNO_FILE_NOT_FOUND = 0x80010002; const int ERROR_ERRNO_FILE_ALREADY_EXISTS = 0x80010011; @@ -101,7 +104,14 @@ const int PSP_COUNT_FDS = 64; // TODO: Should be 3, and stdin/stdout/stderr are special values aliased to 0? const int PSP_MIN_FD = 4; static int asyncNotifyEvent = -1; +static int syncNotifyEvent = -1; static SceUID fds[PSP_COUNT_FDS]; +static AsyncIOManager ioManager; +static bool ioManagerThreadEnabled = false; +static std::thread *ioManagerThread; + +// TODO: Is it better to just put all on the thread? +const int IO_THREAD_MIN_DATA_SIZE = 256; #define SCE_STM_FDIR 0x1000 #define SCE_STM_FREG 0x2000 @@ -292,6 +302,31 @@ void __IoAsyncNotify(u64 userdata, int cyclesLate) { } } +void __IoSyncNotify(u64 userdata, int cyclesLate) { + SceUID threadID = userdata >> 32; + int fd = (int) (userdata & 0xFFFFFFFF); + + s64 result = -1; + u32 error; + FileNode *f = __IoGetFd(fd, error); + if (f) { + f->pendingAsyncResult = false; + f->hasAsyncResult = true; + + AsyncIOResult managerResult; + if (ioManager.WaitResult(f->handle, managerResult)) { + result = managerResult; + } else { + ERROR_LOG(HLE, "Unable to complete IO operation."); + } + } + + SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_IO, error); + if (waitID == fd && error == 0) { + __KernelResumeThreadFromWait(threadID, result); + } +} + static DirectoryFileSystem *memstickSystem = NULL; #ifdef ANDROID static VFSFileSystem *flash0System = NULL; @@ -299,12 +334,20 @@ static VFSFileSystem *flash0System = NULL; static DirectoryFileSystem *flash0System = NULL; #endif +void __IoManagerThread() { + setCurrentThreadName("IOThread"); + while (ioManagerThreadEnabled) { + ioManager.RunEventsUntil(CoreTiming::GetTicks() + msToCycles(1000)); + } +} + void __IoInit() { INFO_LOG(HLE, "Starting up I/O..."); MemoryStick_SetFatState(PSP_FAT_MEMORYSTICK_STATE_ASSIGNED); asyncNotifyEvent = CoreTiming::RegisterEvent("IoAsyncNotify", __IoAsyncNotify); + syncNotifyEvent = CoreTiming::RegisterEvent("IoSyncNotify", __IoSyncNotify); std::string memstickpath; std::string flash0path; @@ -324,15 +367,33 @@ void __IoInit() { __KernelListenThreadEnd(&TellFsThreadEnded); memset(fds, 0, sizeof(fds)); + + ioManagerThreadEnabled = g_Config.bSeparateIOThread; + ioManager.SetThreadEnabled(ioManagerThreadEnabled); + if (ioManagerThreadEnabled) { + ioManagerThread = new std::thread(&__IoManagerThread); + } } void __IoDoState(PointerWrap &p) { + ioManager.DoState(p); p.DoArray(fds, ARRAY_SIZE(fds)); p.Do(asyncNotifyEvent); CoreTiming::RestoreRegisterEvent(asyncNotifyEvent, "IoAsyncNotify", __IoAsyncNotify); + p.Do(syncNotifyEvent); + CoreTiming::RestoreRegisterEvent(syncNotifyEvent, "IoSyncNotify", __IoSyncNotify); + p.DoMarker("sceIo"); } void __IoShutdown() { + ioManagerThreadEnabled = false; + ioManager.SyncThread(); + ioManager.FinishEventLoop(); + if (ioManagerThread != NULL) { + delete ioManagerThread; + ioManagerThread = NULL; + } + pspFileSystem.Unmount("ms0:", memstickSystem); pspFileSystem.Unmount("fatms0:", memstickSystem); pspFileSystem.Unmount("fatms:", memstickSystem); @@ -400,6 +461,12 @@ void __IoCompleteAsyncIO(int fd) { u32 error; FileNode *f = __IoGetFd(fd, error); if (f) { + AsyncIOResult managerResult; + if (ioManager.WaitResult(f->handle, managerResult)) { + f->asyncResult = managerResult; + } else { + // It's okay, not all operations are deferred. + } if (f->callbackID) { __KernelNotifyCallback(THREAD_CALLBACK_IO, f->callbackID, f->callbackArg); } @@ -446,6 +513,14 @@ void __IoSchedAsync(FileNode *f, int fd, int usec) { f->hasAsyncResult = false; } +void __IoSchedSync(FileNode *f, int fd, int usec) { + u64 param = ((u64)__KernelGetCurThread()) << 32 | fd; + CoreTiming::ScheduleEvent(usToCycles(usec), syncNotifyEvent, param); + + f->pendingAsyncResult = true; + f->hasAsyncResult = false; +} + u32 sceIoGetstat(const char *filename, u32 addr) { // TODO: Improve timing (although this seems normally slow..) int usec = 1000; @@ -525,7 +600,7 @@ u32 npdrmRead(FileNode *f, u8 *data, int size) { return size; } -int __IoRead(int id, u32 data_addr, int size) { +bool __IoRead(int &result, int id, u32 data_addr, int size) { if (id == 3) { DEBUG_LOG(HLE, "sceIoRead STDIN"); return 0; //stdin @@ -534,58 +609,88 @@ int __IoRead(int id, u32 data_addr, int size) { u32 error; FileNode *f = __IoGetFd(id, error); if (f) { - if(!(f->openMode & FILEACCESS_READ)) - { - return ERROR_KERNEL_BAD_FILE_DESCRIPTOR; - } - else if (Memory::IsValidAddress(data_addr)) { + if (!(f->openMode & FILEACCESS_READ)) { + result = ERROR_KERNEL_BAD_FILE_DESCRIPTOR; + return true; + } else if (Memory::IsValidAddress(data_addr)) { u8 *data = (u8*) Memory::GetPointer(data_addr); - if(f->npdrm){ - return npdrmRead(f, data, size); - }else{ - return (int) pspFileSystem.ReadFile(f->handle, data, size); + if (f->npdrm) { + result = npdrmRead(f, data, size); + return true; + } else if (__KernelIsDispatchEnabled() && ioManagerThreadEnabled && size > IO_THREAD_MIN_DATA_SIZE) { + AsyncIOEvent ev = IO_EVENT_READ; + ev.handle = f->handle; + ev.buf = data; + ev.bytes = size; + ioManager.ScheduleOperation(ev); + return false; + } else { + result = (int) pspFileSystem.ReadFile(f->handle, data, size); + return true; } } else { ERROR_LOG(HLE, "sceIoRead Reading into bad pointer %08x", data_addr); // TODO: Returning 0 because it wasn't being sign-extended in async result before. // What should this do? - return 0; + result = 0; + return true; } } else { ERROR_LOG(HLE, "sceIoRead ERROR: no file open"); - return error; + result = error; + return true; } } u32 sceIoRead(int id, u32 data_addr, int size) { // TODO: Check id is valid first? - if (!__KernelIsDispatchEnabled() && id > 2) + if (!__KernelIsDispatchEnabled() && id > 2) { return -1; - - int result = __IoRead(id, data_addr, size); - if (result >= 0) { - DEBUG_LOG(HLE, "%x=sceIoRead(%d, %08x, %x)", result, id, data_addr, size); - // TODO: Timing is probably not very accurate, low estimate. - int us = result/100; - if(us==0) - us = 100; - return hleDelayResult(result, "io read", us); } - else + + // TODO: Timing is probably not very accurate, low estimate. + int us = size / 100; + if (us < 100) { + us = 100; + } + + int result; + bool complete = __IoRead(result, id, data_addr, size); + if (!complete) { + DEBUG_LOG(HLE, "sceIoRead(%d, %08x, %x): deferring result", id, data_addr, size); + + u32 error; + FileNode *f = __IoGetFd(id, error); + __IoSchedSync(f, id, us); + __KernelWaitCurThread(WAITTYPE_IO, id, 0, 0, false, "io read"); + return 0; + } else if (result >= 0) { + DEBUG_LOG(HLE, "%x=sceIoRead(%d, %08x, %x)", result, id, data_addr, size); + return hleDelayResult(result, "io read", us); + } else { return result; + } } u32 sceIoReadAsync(int id, u32 data_addr, int size) { + // TODO: Not sure what the correct delay is (and technically we shouldn't read into the buffer yet...) + int us = size / 100; + if (us < 100) { + us = 100; + } + u32 error; FileNode *f = __IoGetFd(id, error); if (f) { - f->asyncResult = __IoRead(id, data_addr, size); - // TODO: Not sure what the correct delay is (and technically we shouldn't read into the buffer yet...) - int us = f->asyncResult/100; - if(us==0) - us = 100; + int result; + bool complete = __IoRead(result, id, data_addr, size); + if (complete) { + f->asyncResult = result; + DEBUG_LOG(HLE, "%llx=sceIoReadAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size); + } else { + DEBUG_LOG(HLE, "sceIoReadAsync(%d, %08x, %x): deferring result", id, data_addr, size); + } __IoSchedAsync(f, id, us); - DEBUG_LOG(HLE, "%llx=sceIoReadAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size); return 0; } else { ERROR_LOG(HLE, "sceIoReadAsync: bad file %d", id); @@ -593,24 +698,37 @@ u32 sceIoReadAsync(int id, u32 data_addr, int size) { } } -int __IoWrite(int id, void *data_ptr, int size) { +bool __IoWrite(int &result, int id, void *data_ptr, int size) { // Let's handle stdout/stderr specially. if (id == 1 || id == 2) { const char *str = (const char *) data_ptr; const int str_size = size == 0 ? 0 : (str[size - 1] == '\n' ? size - 1 : size); INFO_LOG(HLE, "%s: %.*s", id == 1 ? "stdout" : "stderr", str_size, str); - return size; + result = size; + return true; } u32 error; FileNode *f = __IoGetFd(id, error); if (f) { - if(!(f->openMode & FILEACCESS_WRITE)) { - return ERROR_KERNEL_BAD_FILE_DESCRIPTOR; + if (!(f->openMode & FILEACCESS_WRITE)) { + result = ERROR_KERNEL_BAD_FILE_DESCRIPTOR; + return true; } - return (int) pspFileSystem.WriteFile(f->handle, (u8*) data_ptr, size); + if (__KernelIsDispatchEnabled() && ioManagerThreadEnabled && size > IO_THREAD_MIN_DATA_SIZE) { + AsyncIOEvent ev = IO_EVENT_WRITE; + ev.handle = f->handle; + ev.buf = (u8 *) data_ptr; + ev.bytes = size; + ioManager.ScheduleOperation(ev); + return false; + } else { + result = (int) pspFileSystem.WriteFile(f->handle, (u8 *) data_ptr, size); + } + return true; } else { ERROR_LOG(HLE, "sceIoWrite ERROR: no file open"); - return (s32) error; + result = (s32) error; + return true; } } @@ -619,33 +737,53 @@ u32 sceIoWrite(int id, u32 data_addr, int size) { if (!__KernelIsDispatchEnabled() && id > 2) return -1; - int result = __IoWrite(id, Memory::GetPointer(data_addr), size); - if (result >= 0) { - DEBUG_LOG(HLE, "%x=sceIoWrite(%d, %08x, %x)", result, id, data_addr, size); - // TODO: Timing is probably not very accurate, low estimate. - int us = result/100; - if(us==0) - us = 100; - if (__KernelIsDispatchEnabled()) - return hleDelayResult(result, "io write", us); - else - return result; + // TODO: Timing is probably not very accurate, low estimate. + int us = size / 100; + if (us < 100) { + us = 100; } - else + + int result; + bool complete = __IoWrite(result, id, Memory::GetPointer(data_addr), size); + if (!complete) { + DEBUG_LOG(HLE, "sceIoWrite(%d, %08x, %x): deferring result", id, data_addr, size); + + u32 error; + FileNode *f = __IoGetFd(id, error); + __IoSchedSync(f, id, us); + __KernelWaitCurThread(WAITTYPE_IO, id, 0, 0, false, "io write"); + return 0; + } else if (result >= 0) { + DEBUG_LOG(HLE, "%x=sceIoWrite(%d, %08x, %x)", result, id, data_addr, size); + if (__KernelIsDispatchEnabled()) { + return hleDelayResult(result, "io write", us); + } else { + return result; + } + } else { return result; + } } u32 sceIoWriteAsync(int id, u32 data_addr, int size) { + // TODO: Not sure what the correct delay is (and technically we shouldn't read from the buffer yet...) + int us = size / 100; + if (us < 100) { + us = 100; + } + u32 error; FileNode *f = __IoGetFd(id, error); if (f) { - f->asyncResult = __IoWrite(id, Memory::GetPointer(data_addr), size); - // TODO: Not sure what the correct delay is (and technically we shouldn't read into the buffer yet...) - int us = f->asyncResult/100; - if(us==0) - us = 100; + int result; + bool complete = __IoWrite(result, id, Memory::GetPointer(data_addr), size); + if (complete) { + f->asyncResult = result; + DEBUG_LOG(HLE, "%llx=sceIoWriteAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size); + } else { + DEBUG_LOG(HLE, "sceIoWriteAsync(%d, %08x, %x): deferring result", id, data_addr, size); + } __IoSchedAsync(f, id, us); - DEBUG_LOG(HLE, "%llx=sceIoWriteAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size); return 0; } else { ERROR_LOG(HLE, "sceIoWriteAsync: bad file %d", id); diff --git a/Core/HLE/sceIo.h b/Core/HLE/sceIo.h index 2945a396c..961b763eb 100644 --- a/Core/HLE/sceIo.h +++ b/Core/HLE/sceIo.h @@ -19,9 +19,9 @@ #include -#include "../System.h" -#include "HLE.h" -#include "sceKernel.h" +#include "Core/System.h" +#include "Core/HLE/HLE.h" +#include "Core/HLE/sceKernel.h" void __IoInit(); void __IoDoState(PointerWrap &p); diff --git a/Core/HW/AsyncIOManager.cpp b/Core/HW/AsyncIOManager.cpp new file mode 100644 index 000000000..062fab688 --- /dev/null +++ b/Core/HW/AsyncIOManager.cpp @@ -0,0 +1,97 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/Reporting.h" +#include "Core/System.h" +#include "Core/HW/AsyncIOManager.h" +#include "Core/FileSystems/MetaFileSystem.h" + +void AsyncIOManager::ScheduleOperation(AsyncIOEvent ev) { + lock_guard guard(resultsLock_); + resultsPending_.insert(ev.handle); + ScheduleEvent(ev); +} + +bool AsyncIOManager::PopResult(u32 handle, AsyncIOResult &result) { + lock_guard guard(resultsLock_); + if (results_.find(handle) != results_.end()) { + result = results_[handle]; + results_.erase(handle); + resultsPending_.erase(handle); + return true; + } else { + return false; + } +} + +bool AsyncIOManager::WaitResult(u32 handle, AsyncIOResult &result) { + lock_guard guard(resultsLock_); + ScheduleEvent(IO_EVENT_SYNC); + while (HasEvents() && resultsPending_.find(handle) != resultsPending_.end()) { + if (PopResult(handle, result)) { + return true; + } + resultsWait_.wait_for(resultsLock_, 16); + } + if (PopResult(handle, result)) { + return true; + } + + return false; +} + +void AsyncIOManager::ProcessEvent(AsyncIOEvent ev) { + switch (ev.type) { + case IO_EVENT_READ: + Read(ev.handle, ev.buf, ev.bytes); + break; + + case IO_EVENT_WRITE: + Write(ev.handle, ev.buf, ev.bytes); + break; + + default: + ERROR_LOG(HLE, "Unsupported IO event type"); + } +} + +void AsyncIOManager::Read(u32 handle, u8 *buf, size_t bytes) { + size_t result = pspFileSystem.ReadFile(handle, buf, bytes); + EventResult(handle, result); +} + +void AsyncIOManager::Write(u32 handle, u8 *buf, size_t bytes) { + size_t result = pspFileSystem.WriteFile(handle, buf, bytes); + EventResult(handle, result); +} + +void AsyncIOManager::EventResult(u32 handle, AsyncIOResult result) { + lock_guard guard(resultsLock_); + if (results_.find(handle) != results_.end()) { + ERROR_LOG_REPORT(HLE, "Overwriting previous result for file action on handle %d", handle); + } + results_[handle] = result; + resultsWait_.notify_one(); +} + +void AsyncIOManager::DoState(PointerWrap &p) { + SyncThread(); + lock_guard guard(resultsLock_); + p.Do(resultsPending_); + p.Do(results_); + p.DoMarker("AsyncIOManager"); +} diff --git a/Core/HW/AsyncIOManager.h b/Core/HW/AsyncIOManager.h new file mode 100644 index 000000000..6c057da9b --- /dev/null +++ b/Core/HW/AsyncIOManager.h @@ -0,0 +1,75 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include +#include "native/base/mutex.h" +#include "Core/ThreadEventQueue.h" + +class NoBase { +}; + +enum AsyncIOEventType { + IO_EVENT_INVALID, + IO_EVENT_SYNC, + IO_EVENT_FINISH, + IO_EVENT_READ, + IO_EVENT_WRITE, +}; + +struct AsyncIOEvent { + AsyncIOEvent(AsyncIOEventType t) : type(t) {} + AsyncIOEventType type; + u32 handle; + u8 *buf; + size_t bytes; + + operator AsyncIOEventType() const { + return type; + } +}; + +// TODO: Something better. +typedef size_t AsyncIOResult; + +typedef ThreadEventQueue IOThreadEventQueue; +class AsyncIOManager : public IOThreadEventQueue { +public: + void DoState(PointerWrap &p); + + void ScheduleOperation(AsyncIOEvent ev); + + bool PopResult(u32 handle, AsyncIOResult &result); + bool WaitResult(u32 handle, AsyncIOResult &result); + +protected: + virtual void ProcessEvent(AsyncIOEvent ref); + virtual bool ShouldExitEventLoop() { + return coreState == CORE_ERROR || coreState == CORE_POWERDOWN; + } + +private: + void Read(u32 handle, u8 *buf, size_t bytes); + void Write(u32 handle, u8 *buf, size_t bytes); + + void EventResult(u32 handle, AsyncIOResult result); + + recursive_mutex resultsLock_; + condition_variable resultsWait_; + std::set resultsPending_; + std::map results_; +}; \ No newline at end of file diff --git a/Core/System.cpp b/Core/System.cpp index 13a6a2d45..2355f6258 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -20,6 +20,7 @@ #endif #include "native/thread/thread.h" +#include "native/thread/threadutil.h" #include "native/base/mutex.h" #include "Core/MemMap.h" @@ -42,7 +43,6 @@ #include "Core/PSPLoaders.h" #include "Core/ELF/ParamSFO.h" #include "Core/SaveState.h" -#include "Common/Thread.h" #include "Common/LogManager.h" #include "GPU/GPUState.h" @@ -196,7 +196,7 @@ void CPU_Shutdown() { } void CPU_RunLoop() { - Common::SetCurrentThreadName("CPUThread"); + setCurrentThreadName("CPUThread"); if (!CPU_NextState(CPU_THREAD_PENDING, CPU_THREAD_STARTING)) { ERROR_LOG(CPU, "CPU thread in unexpected state: %d", cpuThreadState); return; diff --git a/Core/ThreadEventQueue.h b/Core/ThreadEventQueue.h index 11231ea6a..c0732b6a8 100644 --- a/Core/ThreadEventQueue.h +++ b/Core/ThreadEventQueue.h @@ -74,7 +74,7 @@ struct ThreadEventQueue : public B { } // Quit the loop if the queue is drained and coreState has tripped, or threading is disabled. - if (coreState != CORE_RUNNING || !threadEnabled_) { + if (ShouldExitEventLoop() || !threadEnabled_) { return; } @@ -104,6 +104,7 @@ struct ThreadEventQueue : public B { protected: virtual void ProcessEvent(Event ev) = 0; + virtual bool ShouldExitEventLoop() = 0; private: bool threadEnabled_; diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 411021ea1..dc3297569 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -48,6 +48,9 @@ protected: void ProcessDLQueueInternal(); void ReapplyGfxStateInternal(); virtual void ProcessEvent(GPUEvent ev); + virtual bool ShouldExitEventLoop() { + return coreState != CORE_RUNNING; + } // Allows early unlocking with a guard. Do not double unlock. class easy_guard { diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 838d6be0e..9f99b804f 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -186,6 +186,7 @@ LOCAL_SRC_FILES := \ $(SRC)/Core/ELF/PrxDecrypter.cpp \ $(SRC)/Core/ELF/ParamSFO.cpp \ $(SRC)/Core/HW/atrac3plus.cpp \ + $(SRC)/Core/HW/AsyncIOManager.cpp \ $(SRC)/Core/HW/MemoryStick.cpp \ $(SRC)/Core/HW/MpegDemux.cpp.arm \ $(SRC)/Core/HW/OMAConvert.cpp.arm \