mirror of
https://github.com/libretro/ppsspp.git
synced 2025-01-06 00:18:21 +00:00
Merge pull request #3126 from unknownbrackets/io-async
Use a separate thread for reading/writing
This commit is contained in:
commit
66569b8e59
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -251,6 +251,7 @@
|
||||
<ClCompile Include="HW\MpegDemux.cpp" />
|
||||
<ClCompile Include="HW\OMAConvert.cpp" />
|
||||
<ClCompile Include="HW\SasAudio.cpp" />
|
||||
<ClCompile Include="HW\AsyncIOManager.cpp" />
|
||||
<ClCompile Include="Loaders.cpp" />
|
||||
<ClCompile Include="MemMap.cpp" />
|
||||
<ClCompile Include="MemmapFunctions.cpp" />
|
||||
@ -437,6 +438,7 @@
|
||||
<ClInclude Include="HW\OMAConvert.h" />
|
||||
<ClInclude Include="HW\SasAudio.h" />
|
||||
<ClInclude Include="HW\MemoryStick.h" />
|
||||
<ClInclude Include="HW\AsyncIOManager.h" />
|
||||
<ClInclude Include="Loaders.h" />
|
||||
<ClInclude Include="MemMap.h" />
|
||||
<ClInclude Include="MIPS\ARM\ArmAsm.h">
|
||||
|
@ -451,6 +451,9 @@
|
||||
<ClCompile Include="FileSystems\VirtualDiscFileSystem.cpp">
|
||||
<Filter>FileSystems</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\AsyncIOManager.cpp">
|
||||
<Filter>HW</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ELF\ElfReader.h">
|
||||
@ -840,6 +843,9 @@
|
||||
<ClInclude Include="ThreadEventQueue.h">
|
||||
<Filter>Core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\AsyncIOManager.h">
|
||||
<Filter>HW</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
@ -16,33 +16,36 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <cstdlib>
|
||||
#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);
|
||||
|
@ -19,9 +19,9 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#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);
|
||||
|
97
Core/HW/AsyncIOManager.cpp
Normal file
97
Core/HW/AsyncIOManager.cpp
Normal file
@ -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");
|
||||
}
|
75
Core/HW/AsyncIOManager.h
Normal file
75
Core/HW/AsyncIOManager.h
Normal file
@ -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 <map>
|
||||
#include <set>
|
||||
#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<NoBase, AsyncIOEvent, AsyncIOEventType, IO_EVENT_INVALID, IO_EVENT_SYNC, IO_EVENT_FINISH> 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<u32> resultsPending_;
|
||||
std::map<u32, AsyncIOResult> results_;
|
||||
};
|
@ -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;
|
||||
|
@ -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_;
|
||||
|
@ -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 {
|
||||
|
@ -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 \
|
||||
|
Loading…
Reference in New Issue
Block a user