Merge pull request #12328 from unknownbrackets/io-async

Use PSP threads for async IO
This commit is contained in:
Henrik Rydgård 2019-10-07 21:50:57 +02:00 committed by GitHub
commit eb2109a0f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 334 additions and 147 deletions

View File

@ -49,17 +49,20 @@ HLEHelperThread::HLEHelperThread(const char *threadName, const char *module, con
}
HLEHelperThread::~HLEHelperThread() {
__KernelDeleteThread(id_, SCE_KERNEL_ERROR_THREAD_TERMINATED, "helper deleted");
kernelMemory.Free(entry_);
if (id_)
__KernelDeleteThread(id_, SCE_KERNEL_ERROR_THREAD_TERMINATED, "helper deleted");
if (entry_)
kernelMemory.Free(entry_);
}
void HLEHelperThread::AllocEntry(u32 size) {
entry_ = kernelMemory.Alloc(size);
Memory::Memset(entry_, 0, size);
currentMIPS->InvalidateICache(entry_, size);
}
void HLEHelperThread::Create(const char *threadName, u32 prio, int stacksize) {
id_ = __KernelCreateThreadInternal(threadName, __KernelGetCurThreadModuleId(), entry_, prio, stacksize, 0);
id_ = __KernelCreateThreadInternal(threadName, __KernelGetCurThreadModuleId(), entry_, prio, stacksize, 0x00001000);
}
void HLEHelperThread::DoState(PointerWrap &p) {
@ -79,3 +82,12 @@ void HLEHelperThread::Start(u32 a0, u32 a1) {
void HLEHelperThread::Terminate() {
__KernelStopThread(id_, SCE_KERNEL_ERROR_THREAD_TERMINATED, "helper terminated");
}
bool HLEHelperThread::Stopped() {
return KernelIsThreadDormant(id_);
}
void HLEHelperThread::Forget() {
id_ = 0;
entry_ = 0;
}

View File

@ -31,6 +31,10 @@ public:
void Start(u32 a0, u32 a1);
void Terminate();
bool Stopped();
// For savestates.
void Forget();
private:
void AllocEntry(u32 size);
@ -38,4 +42,4 @@ private:
SceUID id_;
u32 entry_;
};
};

View File

@ -33,6 +33,7 @@
#include "Core/Host.h"
#include "Core/SaveState.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/HLEHelperThread.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/HLE/sceDisplay.h"
#include "Core/HLE/sceKernel.h"
@ -159,6 +160,46 @@ struct SceIoDirEnt {
u32_le d_private;
};
enum class IoAsyncOp {
NONE,
OPEN,
CLOSE,
READ,
WRITE,
SEEK,
IOCTL,
};
struct IoAsyncParams {
IoAsyncOp op = IoAsyncOp::NONE;
int priority = -1;
union {
struct {
u32 filenameAddr;
int flags;
int mode;
} open;
struct {
u32 addr;
u32 size;
} std;
struct {
s64 pos;
int whence;
} seek;
struct {
u32 cmd;
u32 inAddr;
u32 inSize;
u32 outAddr;
u32 outSize;
} ioctl;
};
};
static IoAsyncParams asyncParams[PSP_COUNT_FDS];
static HLEHelperThread *asyncThreads[PSP_COUNT_FDS]{};
class FileNode : public KernelObject {
public:
FileNode() : callbackID(0), callbackArg(0), asyncResult(0), hasAsyncResult(false), pendingAsyncResult(false), sectorBlockMode(false), closePending(false), npdrm(0), pgdInfo(NULL) {}
@ -253,6 +294,13 @@ public:
u64 __IoCompleteAsyncIO(FileNode *f);
static void IoAsyncCleanupThread(int fd, bool force = false) {
if (asyncThreads[fd] && (asyncThreads[fd]->Stopped() || force)) {
delete asyncThreads[fd];
asyncThreads[fd] = nullptr;
}
}
static int GetIOTimingMethod() {
if (PSP_CoreParameter().compat.flags().ForceUMDDelay) {
return IOTIMING_REALISTIC;
@ -316,6 +364,8 @@ static void __IoFreeFd(int fd, u32 &error) {
// Discard any pending results.
AsyncIOResult managerResult;
ioManager.WaitResult(f->handle, managerResult);
IoAsyncCleanupThread(fd);
}
error = kernelObjects.Destroy<FileNode>(fds[fd]);
fds[fd] = 0;
@ -335,7 +385,6 @@ static void __IoFreeFd(int fd, u32 &error) {
// TODO: Closed files are a bit special: until the fd is reused (?), the async result is still available.
// Clearly a buffer is used, it doesn't seem like they are actually kernel objects.
// TODO: We don't do any of that yet.
// For now, let's at least delay the callback notification.
static void __IoAsyncNotify(u64 userdata, int cyclesLate) {
int fd = (int) userdata;
@ -388,6 +437,8 @@ static void __IoAsyncNotify(u64 userdata, int cyclesLate) {
if (f->closePending) {
__IoFreeFd(fd, error);
}
IoAsyncCleanupThread(fd);
}
}
@ -436,6 +487,8 @@ static void __IoSyncNotify(u64 userdata, int cyclesLate) {
HLEKernel::ResumeFromWait(threadID, WAITTYPE_IO, fd, result);
f->waitingSyncThreads.erase(std::remove(f->waitingSyncThreads.begin(), f->waitingSyncThreads.end(), threadID), f->waitingSyncThreads.end());
IoAsyncCleanupThread(fd);
}
static void __IoAsyncBeginCallback(SceUID threadID, SceUID prevCallbackId) {
@ -611,7 +664,7 @@ void __IoInit() {
}
void __IoDoState(PointerWrap &p) {
auto s = p.Section("sceIo", 1, 3);
auto s = p.Section("sceIo", 1, 4);
if (!s)
return;
@ -644,6 +697,33 @@ void __IoDoState(PointerWrap &p) {
p.Do(lastMemStickState);
p.Do(lastMemStickFatState);
}
for (int i = 0; i < PSP_COUNT_FDS; ++i) {
if (s >= 4) {
p.DoVoid(&asyncParams[i], (int)sizeof(IoAsyncParams));
bool hasThread = asyncThreads[i] != nullptr;
p.Do(hasThread);
if (hasThread) {
if (asyncThreads[i])
asyncThreads[i]->Forget();
delete asyncThreads[i];
asyncThreads[i] = nullptr;
p.DoClass(asyncThreads[i]);
} else if (!hasThread) {
if (asyncThreads[i])
asyncThreads[i]->Forget();
delete asyncThreads[i];
asyncThreads[i] = nullptr;
}
} else {
asyncParams[i].op = IoAsyncOp::NONE;
asyncParams[i].priority = -1;
if (asyncThreads[i])
asyncThreads[i]->Forget();
delete asyncThreads[i];
asyncThreads[i] = nullptr;
}
}
}
void __IoShutdown() {
@ -687,6 +767,14 @@ u32 __IoGetFileHandleFromId(u32 id, u32 &outError)
return f->handle;
}
static void IoStartAsyncThread(int id, FileNode *f) {
IoAsyncCleanupThread(id, true);
int priority = asyncParams[id].priority == -1 ? KernelCurThreadPriority() : asyncParams[id].priority;
asyncThreads[id] = new HLEHelperThread("SceIoAsync", "IoFileMgrForUser", "__IoAsyncFinish", priority, 0x200);
asyncThreads[id]->Start(id, 0);
f->pendingAsyncResult = true;
}
static u32 sceIoAssign(u32 alias_addr, u32 physical_addr, u32 filesystem_addr, int mode, u32 arg_addr, int argSize)
{
std::string alias = Memory::GetCharPointer(alias_addr);
@ -990,28 +1078,21 @@ static u32 sceIoRead(int id, u32 data_addr, int size) {
}
static u32 sceIoReadAsync(int id, u32 data_addr, int size) {
// TODO: Technically we shouldn't read into the buffer yet...
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f) {
if (f->asyncBusy()) {
WARN_LOG(SCEIO, "sceIoReadAsync(%d, %08x, %x): async busy", id, data_addr, size);
return SCE_KERNEL_ERROR_ASYNC_BUSY;
return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_ASYNC_BUSY, "async busy");
}
int result;
int us;
bool complete = __IoRead(result, id, data_addr, size, us);
if (complete) {
f->asyncResult = (s64)result;
DEBUG_LOG(SCEIO, "%llx=sceIoReadAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size);
} else {
DEBUG_LOG(SCEIO, "sceIoReadAsync(%d, %08x, %x): deferring result", id, data_addr, size);
}
__IoSchedAsync(f, id, us);
return 0;
auto &params = asyncParams[id];
params.op = IoAsyncOp::READ;
params.std.addr = data_addr;
params.std.size = size;
IoStartAsyncThread(id, f);
return hleLogSuccessI(SCEIO, 0);
} else {
ERROR_LOG(SCEIO, "sceIoReadAsync: bad file %d", id);
return error;
return hleLogError(SCEIO, error, "bad file descriptor");
}
}
@ -1124,28 +1205,21 @@ static u32 sceIoWrite(int id, u32 data_addr, int size) {
}
static u32 sceIoWriteAsync(int id, u32 data_addr, int size) {
// TODO: Technically we shouldn't read from the buffer yet...
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f) {
if (f->asyncBusy()) {
WARN_LOG(SCEIO, "sceIoWriteAsync(%d, %08x, %x): async busy", id, data_addr, size);
return SCE_KERNEL_ERROR_ASYNC_BUSY;
return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_ASYNC_BUSY, "async busy");
}
int result;
int us;
bool complete = __IoWrite(result, id, data_addr, size, us);
if (complete) {
f->asyncResult = (s64)result;
DEBUG_LOG(SCEIO, "%llx=sceIoWriteAsync(%d, %08x, %x)", f->asyncResult, id, data_addr, size);
} else {
DEBUG_LOG(SCEIO, "sceIoWriteAsync(%d, %08x, %x): deferring result", id, data_addr, size);
}
__IoSchedAsync(f, id, us);
return 0;
auto &params = asyncParams[id];
params.op = IoAsyncOp::WRITE;
params.std.addr = data_addr;
params.std.size = size;
IoStartAsyncThread(id, f);
return hleLogSuccessI(SCEIO, 0);
} else {
ERROR_LOG(SCEIO, "sceIoWriteAsync: bad file %d", id);
return error;
return hleLogError(SCEIO, error, "bad file descriptor");
}
}
@ -1270,8 +1344,7 @@ static s64 sceIoLseek(int id, s64 offset, int whence) {
hleReSchedule("io seek");
return result;
} else {
ERROR_LOG(SCEIO, "sceIoLseek(%d, %llx, %i) - ERROR: invalid file", id, offset, whence);
return result;
return hleLogError(SCEIO, result, "bad file descriptor");
}
}
@ -1284,8 +1357,7 @@ static u32 sceIoLseek32(int id, int offset, int whence) {
hleReSchedule("io seek");
return result;
} else {
ERROR_LOG(SCEIO, "sceIoLseek32(%d, %x, %i) - ERROR: invalid file", id, offset, whence);
return result;
return hleLogError(SCEIO, result, "bad file descriptor");
}
}
@ -1294,21 +1366,20 @@ static u32 sceIoLseekAsync(int id, s64 offset, int whence) {
FileNode *f = __IoGetFd(id, error);
if (f) {
if (whence < 0 || whence > 2) {
WARN_LOG(SCEIO, "sceIoLseekAsync(%d, %llx, %i): invalid whence", id, offset, whence);
return SCE_KERNEL_ERROR_INVAL;
return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_INVAL, "invalid whence");
}
if (f->asyncBusy()) {
WARN_LOG(SCEIO, "sceIoLseekAsync(%d, %llx, %i): async busy", id, offset, whence);
return SCE_KERNEL_ERROR_ASYNC_BUSY;
return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_ASYNC_BUSY, "async busy");
}
f->asyncResult = __IoLseek(id, offset, whence);
// Educated guess at timing.
__IoSchedAsync(f, id, 100);
DEBUG_LOG(SCEIO, "%lli = sceIoLseekAsync(%d, %llx, %i)", f->asyncResult, id, offset, whence);
return 0;
auto &params = asyncParams[id];
params.op = IoAsyncOp::SEEK;
params.seek.pos = offset;
params.seek.whence = whence;
IoStartAsyncThread(id, f);
return hleLogSuccessI(SCEIO, 0);
} else {
ERROR_LOG(SCEIO, "sceIoLseekAsync(%d, %llx, %i) - ERROR: invalid file", id, offset, whence);
return error;
return hleLogError(SCEIO, error, "bad file descriptor");
}
return 0;
}
@ -1318,21 +1389,20 @@ static u32 sceIoLseek32Async(int id, int offset, int whence) {
FileNode *f = __IoGetFd(id, error);
if (f) {
if (whence < 0 || whence > 2) {
WARN_LOG(SCEIO, "sceIoLseek32Async(%d, %x, %i): invalid whence", id, offset, whence);
return SCE_KERNEL_ERROR_INVAL;
return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_INVAL, "invalid whence");
}
if (f->asyncBusy()) {
WARN_LOG(SCEIO, "sceIoLseek*(%d, %x, %i): async busy", id, offset, whence);
return SCE_KERNEL_ERROR_ASYNC_BUSY;
return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_ASYNC_BUSY, "async busy");
}
f->asyncResult = __IoLseek(id, offset, whence);
// Educated guess at timing.
__IoSchedAsync(f, id, 100);
DEBUG_LOG(SCEIO, "%lli = sceIoLseek32Async(%d, %x, %i)", f->asyncResult, id, offset, whence);
return 0;
auto &params = asyncParams[id];
params.op = IoAsyncOp::SEEK;
params.seek.pos = offset;
params.seek.whence = whence;
IoStartAsyncThread(id, f);
return hleLogSuccessI(SCEIO, 0);
} else {
ERROR_LOG(SCEIO, "sceIoLseek32Async(%d, %x, %i) - ERROR: invalid file", id, offset, whence);
return error;
return hleLogError(SCEIO, error, "bad file descriptor");
}
return 0;
}
@ -1407,6 +1477,7 @@ static u32 sceIoOpen(const char *filename, int flags, int mode) {
return id;
} else {
DEBUG_LOG(SCEIO, "%i=sceIoOpen(%s, %08x, %08x)", id, filename, flags, mode);
asyncParams[id].priority = -1;
// Timing is not accurate, aiming low for now.
return hleDelayResult(id, "file opened", 100);
}
@ -1881,32 +1952,36 @@ static u32 sceIoChdir(const char *dirname) {
return pspFileSystem.ChDir(dirname);
}
static int sceIoChangeAsyncPriority(int id, int priority)
{
static int sceIoChangeAsyncPriority(int id, int priority) {
// priority = -1 is valid
if (priority < 0 && priority != -1) {
ERROR_LOG(SCEIO, "sceIoChangeAsyncPriority : Illegal Priority %i", priority);
return SCE_KERNEL_ERROR_ILLEGAL_PRIORITY;
if (priority != -1 && (priority < 0x08 || priority > 0x77)) {
return hleLogError(SCEIO, SCE_KERNEL_ERROR_ILLEGAL_PRIORITY, "illegal priority %d", priority);
}
WARN_LOG(SCEIO, "UNIMPL sceIoChangeAsyncPriority(%d, %d)", id, priority);
return 0;
u32 error;
FileNode *f = __IoGetFd(id, error);
if (!f) {
return hleLogError(SCEIO, error, "bad file descriptor");
}
asyncParams[id].priority = priority;
return hleLogSuccessI(SCEIO, 0);
}
static int sceIoCloseAsync(int id)
{
DEBUG_LOG(SCEIO, "sceIoCloseAsync(%d)", id);
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f)
{
if (f) {
f->closePending = true;
f->asyncResult = 0;
// TODO: Rough estimate.
__IoSchedAsync(f, id, 100);
return 0;
auto &params = asyncParams[id];
params.op = IoAsyncOp::CLOSE;
IoStartAsyncThread(id, f);
return hleLogSuccessI(SCEIO, 0);
} else {
return hleLogError(SCEIO, error, "bad file descriptor");
}
else
return error;
}
static u32 sceIoSetAsyncCallback(int id, u32 clbckId, u32 clbckArg)
@ -1936,41 +2011,40 @@ static u32 sceIoOpenAsync(const char *filename, int flags, int mode)
int error;
FileNode *f = __IoOpen(error, filename, flags, mode);
int fd;
// We have to return an fd here, which may have been destroyed when we reach Wait if it failed.
if (f == NULL)
{
ERROR_LOG(SCEIO, "ERROR_ERRNO_FILE_NOT_FOUND=sceIoOpenAsync(%s, %08x, %08x) - file not found", filename, flags, mode);
if (f == nullptr) {
if (error == 0)
error = SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND;
f = new FileNode();
f->handle = kernelObjects.Create(f);
f->fullpath = filename;
f->asyncResult = error == 0 ? (s64)SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND : (s64)error;
f->closePending = true;
fd = __IoAllocFd(f);
}
else
{
fd = __IoAllocFd(f);
if (fd >= 0) {
DEBUG_LOG(SCEIO, "%x=sceIoOpenAsync(%s, %08x, %08x)", fd, filename, flags, mode);
f->asyncResult = fd;
}
}
// We need an fd even for errors, since it's async.
int fd = __IoAllocFd(f);
if (fd < 0) {
ERROR_LOG(SCEIO, "%08x=sceIoOpenAsync(%s, %08x, %08x): out of fds", fd, filename, flags, mode);
kernelObjects.Destroy<FileNode>(f->GetUID());
return fd;
return hleLogError(SCEIO, fd, "out of fds");
}
// TODO: Timing is very inconsistent. From ms0, 10ms - 20ms depending on filesize/dir depth? From umd, can take > 1s.
// For now let's aim low.
__IoSchedAsync(f, fd, 100);
auto &params = asyncParams[fd];
params.op = IoAsyncOp::OPEN;
params.priority = -1;
params.open.filenameAddr = PARAM(0);
params.open.flags = flags;
params.open.mode = mode;
IoStartAsyncThread(fd, f);
return fd;
if (error != 0) {
f->asyncResult = (s64)error;
return hleLogError(SCEIO, fd, "file not found");
}
f->asyncResult = fd;
return hleLogSuccessI(SCEIO, fd);
}
static u32 sceIoGetAsyncStat(int id, u32 poll, u32 address) {
@ -2004,6 +2078,7 @@ static u32 sceIoGetAsyncStat(int id, u32 poll, u32 address) {
DEBUG_LOG(SCEIO, "%lli = sceIoGetAsyncStat(%i, %i, %08x)", f->asyncResult, id, poll, address);
Memory::Write_U64((u64) f->asyncResult, address);
f->hasAsyncResult = false;
IoAsyncCleanupThread(id);
if (f->closePending) {
__IoFreeFd(id, error);
@ -2026,37 +2101,34 @@ static int sceIoWaitAsync(int id, u32 address) {
FileNode *f = __IoGetFd(id, error);
if (f) {
if (__IsInInterrupt()) {
DEBUG_LOG(SCEIO, "%lli = sceIoWaitAsync(%i, %08x): illegal context", f->asyncResult, id, address);
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "illegal context");
}
if (f->pendingAsyncResult) {
if (!__KernelIsDispatchEnabled()) {
DEBUG_LOG(SCEIO, "%lli = sceIoWaitAsync(%i, %08x): dispatch disabled", f->asyncResult, id, address);
return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled");
}
DEBUG_LOG(SCEIO, "%lli = sceIoWaitAsync(%i, %08x): waiting", f->asyncResult, id, address);
f->waitingThreads.push_back(__KernelGetCurThread());
__KernelWaitCurThread(WAITTYPE_ASYNCIO, f->GetUID(), address, 0, false, "io waited");
return hleLogSuccessI(SCEIO, 0, "waiting");
} else if (f->hasAsyncResult) {
if (!__KernelIsDispatchEnabled()) {
DEBUG_LOG(SCEIO, "%lli = sceIoWaitAsync(%i, %08x): dispatch disabled", f->asyncResult, id, address);
return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled");
}
DEBUG_LOG(SCEIO, "%lli = sceIoWaitAsync(%i, %08x)", f->asyncResult, id, address);
Memory::Write_U64((u64) f->asyncResult, address);
f->hasAsyncResult = false;
IoAsyncCleanupThread(id);
if (f->closePending) {
__IoFreeFd(id, error);
}
return hleLogSuccessI(SCEIO, 0, "complete");
} else {
WARN_LOG(SCEIO, "SCE_KERNEL_ERROR_NOASYNC = sceIoWaitAsync(%i, %08x)", id, address);
return SCE_KERNEL_ERROR_NOASYNC;
return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_NOASYNC, "no async pending");
}
return 0; //completed
} else {
ERROR_LOG(SCEIO, "ERROR - sceIoWaitAsync waiting for invalid id %i", id);
return SCE_KERNEL_ERROR_BADF;
return hleLogError(SCEIO, SCE_KERNEL_ERROR_BADF, "invalid fd");
}
}
@ -2066,32 +2138,30 @@ static int sceIoWaitAsyncCB(int id, u32 address) {
FileNode *f = __IoGetFd(id, error);
if (f) {
if (__IsInInterrupt()) {
DEBUG_LOG(SCEIO, "%lli = sceIoWaitAsyncCB(%i, %08x): illegal context", f->asyncResult, id, address);
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "illegal context");
}
hleCheckCurrentCallbacks();
if (f->pendingAsyncResult) {
DEBUG_LOG(SCEIO, "%lli = sceIoWaitAsyncCB(%i, %08x): waiting", f->asyncResult, id, address);
// TODO: This seems to re-enable dispatch or something?
f->waitingThreads.push_back(__KernelGetCurThread());
__KernelWaitCurThread(WAITTYPE_ASYNCIO, f->GetUID(), address, 0, false, "io waited");
__KernelWaitCurThread(WAITTYPE_ASYNCIO, f->GetUID(), address, 0, true, "io waited");
return hleLogSuccessI(SCEIO, 0, "waiting");
} else if (f->hasAsyncResult) {
DEBUG_LOG(SCEIO, "%lli = sceIoWaitAsyncCB(%i, %08x)", f->asyncResult, id, address);
Memory::Write_U64((u64) f->asyncResult, address);
f->hasAsyncResult = false;
IoAsyncCleanupThread(id);
if (f->closePending) {
__IoFreeFd(id, error);
}
return hleLogSuccessI(SCEIO, 0, "complete");
} else {
WARN_LOG(SCEIO, "SCE_KERNEL_ERROR_NOASYNC = sceIoWaitAsyncCB(%i, %08x)", id, address);
return SCE_KERNEL_ERROR_NOASYNC;
return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_NOASYNC, "no async pending");
}
return 0; //completed
} else {
ERROR_LOG(SCEIO, "ERROR - sceIoWaitAsyncCB waiting for invalid id %i", id);
return SCE_KERNEL_ERROR_BADF;
return hleLogError(SCEIO, SCE_KERNEL_ERROR_BADF, "invalid fd");
}
}
@ -2100,25 +2170,21 @@ static u32 sceIoPollAsync(int id, u32 address) {
FileNode *f = __IoGetFd(id, error);
if (f) {
if (f->pendingAsyncResult) {
VERBOSE_LOG(SCEIO, "%lli = sceIoPollAsync(%i, %08x): not ready", f->asyncResult, id, address);
return 1;
return hleLogSuccessVerboseI(SCEIO, 1, "not ready");
} else if (f->hasAsyncResult) {
DEBUG_LOG(SCEIO, "%lli = sceIoPollAsync(%i, %08x)", f->asyncResult, id, address);
Memory::Write_U64((u64) f->asyncResult, address);
f->hasAsyncResult = false;
IoAsyncCleanupThread(id);
if (f->closePending) {
__IoFreeFd(id, error);
}
return 0; //completed
return hleLogSuccessI(SCEIO, 0);
} else {
// This seems to be normal-ish usage so demoted to DEBUG from WARN.
DEBUG_LOG(SCEIO, "SCE_KERNEL_ERROR_NOASYNC = sceIoPollAsync(%i, %08x)", id, address);
return SCE_KERNEL_ERROR_NOASYNC;
return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_NOASYNC, "no async pending");
}
} else {
ERROR_LOG(SCEIO, "ERROR - sceIoPollAsync waiting for invalid id %i", id);
return SCE_KERNEL_ERROR_BADF;
return hleLogError(SCEIO, SCE_KERNEL_ERROR_BADF, "invalid fd");
}
}
@ -2510,17 +2576,20 @@ static u32 sceIoIoctlAsync(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdat
FileNode *f = __IoGetFd(id, error);
if (f) {
if (f->asyncBusy()) {
WARN_LOG(SCEIO, "sceIoIoctlAsync(%08x, %08x, %08x, %08x, %08x, %08x): async busy", id, cmd, indataPtr, inlen, outdataPtr, outlen);
return SCE_KERNEL_ERROR_ASYNC_BUSY;
return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_ASYNC_BUSY, "async busy");
}
DEBUG_LOG(SCEIO, "sceIoIoctlAsync(%08x, %08x, %08x, %08x, %08x, %08x)", id, cmd, indataPtr, inlen, outdataPtr, outlen);
int usec = 100;
f->asyncResult = __IoIoctl(id, cmd, indataPtr, inlen, outdataPtr, outlen, usec);
__IoSchedAsync(f, id, usec);
return 0;
auto &params = asyncParams[id];
params.op = IoAsyncOp::IOCTL;
params.ioctl.cmd = cmd;
params.ioctl.inAddr = indataPtr;
params.ioctl.inSize = inlen;
params.ioctl.outAddr = outdataPtr;
params.ioctl.outSize = outlen;
IoStartAsyncThread(id, f);
return hleLogSuccessI(SCEIO, 0);
} else {
ERROR_LOG(SCEIO, "UNIMPL %08x=sceIoIoctlAsync id: %08x, cmd %08x, bad file", error, id, cmd);
return error;
return hleLogError(SCEIO, error, "bad file descriptor");
}
}
@ -2569,6 +2638,81 @@ static u32 sceKernelRegisterStdoutPipe(u32 msgPipeUID) {
return 0;
}
static int IoAsyncFinish(int id) {
u32 error;
FileNode *f = __IoGetFd(id, error);
if (f) {
// Reset this so the Io funcs don't reject the request.
f->pendingAsyncResult = false;
auto &params = asyncParams[id];
int result;
int us;
bool complete;
switch (params.op) {
case IoAsyncOp::READ:
complete = __IoRead(result, id, params.std.addr, params.std.size, us);
if (complete) {
f->asyncResult = (s64)result;
DEBUG_LOG(SCEIO, "ASYNC %llx=sceIoReadAsync(%d, %08x, %x)", f->asyncResult, id, params.std.addr, params.std.size);
} else {
DEBUG_LOG(SCEIO, "ASYNC sceIoReadAsync(%d, %08x, %x): deferring result", id, params.std.addr, params.std.size);
}
break;
case IoAsyncOp::WRITE:
complete = __IoWrite(result, id, params.std.addr, params.std.size, us);
if (complete) {
f->asyncResult = (s64)result;
DEBUG_LOG(SCEIO, "ASYNC %llx=sceIoWriteAsync(%d, %08x, %x)", f->asyncResult, id, params.std.addr, params.std.size);
} else {
DEBUG_LOG(SCEIO, "ASYNC sceIoWriteAsync(%d, %08x, %x): deferring result", id, params.std.addr, params.std.size);
}
break;
case IoAsyncOp::SEEK:
f->asyncResult = __IoLseek(id, params.seek.pos, params.seek.whence);
// Educated guess at timing.
us = 100;
DEBUG_LOG(SCEIO, "ASYNC %lli = sceIoLseekAsync(%d, %llx, %i)", f->asyncResult, id, params.seek.pos, params.seek.whence);
break;
case IoAsyncOp::OPEN:
// TODO: Timing is very inconsistent. From ms0, 10ms - 20ms depending on filesize/dir depth? From umd, can take > 1s.
// For now let's aim low.
us = 100;
break;
case IoAsyncOp::CLOSE:
f->asyncResult = 0;
// TODO: Rough estimate.
us = 100;
DEBUG_LOG(SCEIO, "ASYNC %lli = sceIoCloseAsync(%d)", f->asyncResult, id);
break;
case IoAsyncOp::IOCTL:
us = 100;
f->asyncResult = __IoIoctl(id, params.ioctl.cmd, params.ioctl.inAddr, params.ioctl.inSize, params.ioctl.outAddr, params.ioctl.outSize, us);
DEBUG_LOG(SCEIO, "ASYNC sceIoIoctlAsync(%08x, %08x, %08x, %08x, %08x, %08x)", id, params.ioctl.cmd, params.ioctl.inAddr, params.ioctl.inSize, params.ioctl.outAddr, params.ioctl.outSize);
break;
default:
ERROR_LOG_REPORT(SCEIO, "Unknown async op %d", params.op);
break;
}
__IoSchedAsync(f, id, us);
__KernelWaitCurThread(WAITTYPE_ASYNCIO, id, 0, 0, false, "async io");
params.op = IoAsyncOp::NONE;
return 0;
} else {
return hleLogError(SCEIO, error, "bad file descriptor");
}
}
KernelObject *__KernelFileNodeObject() {
return new FileNode;
}
@ -2615,6 +2759,7 @@ const HLEFunction IoFileMgrForUser[] = {
{0x35DBD746, &WrapI_IU<sceIoWaitAsyncCB>, "sceIoWaitAsyncCB", 'i', "iP" },
{0xE23EEC33, &WrapI_IU<sceIoWaitAsync>, "sceIoWaitAsync", 'i', "iP" },
{0X5C2BE2CC, &WrapU_UIU<sceIoGetFdList>, "sceIoGetFdList", 'i', "xip" },
{0x13370001, &WrapI_I<IoAsyncFinish>, "__IoAsyncFinish", 'i', "i" },
};
void Register_IoFileMgrForUser() {

View File

@ -399,8 +399,9 @@ public:
static int GetStaticIDType() { return SCE_KERNEL_TMID_Thread; }
int GetIDType() const override { return SCE_KERNEL_TMID_Thread; }
bool AllocateStack(u32 &stackSize)
{
bool AllocateStack(u32 &stackSize) {
_assert_msg_(SCEKERNEL, stackSize >= 0x200, "thread stack should be 256 bytes or larger");
FreeStack();
bool fromTop = (nt.attr & PSP_THREAD_ATTR_LOW_STACK) == 0;
@ -1183,6 +1184,14 @@ const char *__KernelGetThreadName(SceUID threadID)
return "ERROR";
}
bool KernelIsThreadDormant(SceUID threadID) {
u32 error;
Thread *t = kernelObjects.Get<Thread>(threadID, error);
if (t)
return (t->nt.status & (THREADSTATUS_DEAD | THREADSTATUS_DORMANT)) != 0;
return 0;
}
u32 __KernelGetWaitValue(SceUID threadID, u32 &error)
{
Thread *t = kernelObjects.Get<Thread>(threadID, error);
@ -2122,7 +2131,7 @@ void __KernelReturnFromThread()
Thread *thread = __GetCurrentThread();
_dbg_assert_msg_(SCEKERNEL, thread != NULL, "Returned from a NULL thread.");
INFO_LOG(SCEKERNEL,"__KernelReturnFromThread: %d", exitStatus);
DEBUG_LOG(SCEKERNEL, "__KernelReturnFromThread: %d", exitStatus);
__KernelStopThread(currentThread, exitStatus, "thread returned");
hleReSchedule("thread returned");
@ -2352,6 +2361,13 @@ SceUID __KernelGetCurThread()
return currentThread;
}
int KernelCurThreadPriority() {
Thread *t = __GetCurrentThread();
if (t)
return t->nt.currentPriority;
return 0;
}
SceUID __KernelGetCurThreadModuleId()
{
Thread *t = __GetCurrentThread();

View File

@ -161,9 +161,11 @@ KernelObject *__KernelCallbackObject();
void __KernelScheduleWakeup(int threadnumber, s64 usFromNow);
SceUID __KernelGetCurThread();
int KernelCurThreadPriority();
u32 __KernelGetCurThreadStack();
u32 __KernelGetCurThreadStackStart();
const char *__KernelGetThreadName(SceUID threadID);
bool KernelIsThreadDormant(SceUID threadID);
void __KernelSaveContext(ThreadContext *ctx, bool vfpuEnabled);
void __KernelLoadContext(ThreadContext *ctx, bool vfpuEnabled);

View File

@ -220,7 +220,7 @@ public:
void ScheduleFinish(u32 handle) {
if (!finishThread) {
finishThread = new HLEHelperThread("scePsmfPlayer", "scePsmfPlayer", "__PsmfPlayerFinish", playbackThreadPriority, 0x100);
finishThread = new HLEHelperThread("scePsmfPlayer", "scePsmfPlayer", "__PsmfPlayerFinish", playbackThreadPriority, 0x200);
finishThread->Start(handle, 0);
}
}
@ -543,15 +543,23 @@ void PsmfPlayer::DoState(PointerWrap &p) {
}
p.Do(psmfPlayerAvcAu);
if (s >= 7) {
bool hasFinishThread = finishThread != NULL;
bool hasFinishThread = finishThread != nullptr;
p.Do(hasFinishThread);
if (hasFinishThread) {
p.Do(finishThread);
} else {
if (finishThread)
finishThread->Forget();
delete finishThread;
finishThread = nullptr;
}
} else if (s >= 6) {
p.Do(finishThread);
} else {
finishThread = NULL;
if (finishThread)
finishThread->Forget();
delete finishThread;
finishThread = nullptr;
}
if (s >= 8) {