mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Expose EventFlag/Mutex/Semaphore/MsgPipe/Callback in their headers
These KernelObject subclasses (and their Native* status structs) were private implementation details of their respective .cpp files. Move them into the matching .h instead, so external code - specifically the upcoming WebSocket kernel-object introspection endpoints - can read a live object's state directly via kernelObjects.Get<T>()/Iterate<T>(), the same way PSPModule/PSPThread already can. Read-only by convention: nothing outside each file should call DoState() or otherwise mutate these; the fields are public here for that file's own pre-existing use, not an invitation to write from elsewhere. To avoid pulling each type's full dependency set (Memory::, BlockAllocator, CoreTiming, HLEKernel::...) into headers many other files include, non-trivial method bodies (DoState, and MsgPipe's buffer/wait-list management) are declared in the header but still defined out-of-line in the .cpp, same as before - only genuinely trivial one-liners went inline. KernelObjectPool also gains IterateAll(), a type-agnostic sibling of the existing Iterate<T>() - walks every live kernel object regardless of type, for a coarse "what's alive right now" overview. No behavior change - this is a pure visibility/declaration-vs-definition move, not new functionality. That lands in a follow-up commit. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
This commit is contained in:
1 parent
ce33c964f7
commit
21b7ce7392
11 files changed
+513
-408
No files matched your search
@@ -225,6 +225,20 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Like Iterate<T>(), but every live object regardless of type - only the base KernelObject
|
||||
// interface (GetUID/GetTypeName/GetName/GetQuickInfo/...) is available on each. Used for a
|
||||
// coarse "what's alive right now" overview across every kernel object kind at once, e.g. the
|
||||
// WebSocket debugger's hle.object.list.
|
||||
template <typename F>
|
||||
void IterateAll(F func) {
|
||||
for (int i = 0; i < maxCount; i++) {
|
||||
if (!occupied[i])
|
||||
continue;
|
||||
if (!func(i + handleOffset, pool[i]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int ListIDType(int type, SceUID_le *uids, int count) const {
|
||||
int total = 0;
|
||||
for (int i = 0; i < maxCount; i++) {
|
||||
|
||||
@@ -32,61 +32,18 @@
|
||||
|
||||
void __KernelEventFlagTimeout(u64 userdata, int cycleslate);
|
||||
|
||||
struct NativeEventFlag {
|
||||
u32_le size;
|
||||
char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
|
||||
u32_le attr;
|
||||
u32_le initPattern;
|
||||
u32_le currentPattern;
|
||||
s32_le numWaitThreads;
|
||||
};
|
||||
// NativeEventFlag/EventFlagTh/EventFlag itself now live in sceKernelEventFlag.h - see the
|
||||
// comment on the class there for why.
|
||||
void EventFlag::DoState(PointerWrap &p) {
|
||||
auto s = p.Section("EventFlag", 1);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
struct EventFlagTh {
|
||||
SceUID threadID;
|
||||
u32 bits;
|
||||
u32 wait;
|
||||
u32 outAddr;
|
||||
u64 pausedTimeout;
|
||||
|
||||
bool operator ==(const SceUID &otherThreadID) const {
|
||||
return threadID == otherThreadID;
|
||||
}
|
||||
};
|
||||
|
||||
class EventFlag : public KernelObject {
|
||||
public:
|
||||
const char *GetName() override { return nef.name; }
|
||||
const char *GetTypeName() override { return GetStaticTypeName(); }
|
||||
static const char *GetStaticTypeName() { return "EventFlag"; }
|
||||
void GetQuickInfo(char *ptr, int size) override {
|
||||
snprintf(ptr, size, "init=%08x cur=%08x numwait=%i",
|
||||
nef.initPattern,
|
||||
nef.currentPattern,
|
||||
nef.numWaitThreads);
|
||||
}
|
||||
|
||||
static u32 GetMissingErrorCode() {
|
||||
return SCE_KERNEL_ERROR_UNKNOWN_EVFID;
|
||||
}
|
||||
static int GetStaticIDType() { return SCE_KERNEL_TMID_EventFlag; }
|
||||
int GetIDType() const override { return SCE_KERNEL_TMID_EventFlag; }
|
||||
|
||||
void DoState(PointerWrap &p) override {
|
||||
auto s = p.Section("EventFlag", 1);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
Do(p, nef);
|
||||
EventFlagTh eft = { 0 };
|
||||
Do(p, waitingThreads, eft);
|
||||
Do(p, pausedWaits);
|
||||
}
|
||||
|
||||
NativeEventFlag nef;
|
||||
std::vector<EventFlagTh> waitingThreads;
|
||||
// Key is the callback id it was for, or if no callback, the thread id.
|
||||
std::map<SceUID, EventFlagTh> pausedWaits;
|
||||
};
|
||||
Do(p, nef);
|
||||
EventFlagTh eft = { 0 };
|
||||
Do(p, waitingThreads, eft);
|
||||
Do(p, pausedWaits);
|
||||
}
|
||||
|
||||
|
||||
/** Event flag creation attributes */
|
||||
|
||||
@@ -17,6 +17,67 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "Core/HLE/sceKernel.h"
|
||||
#include "Core/HLE/ErrorCodes.h"
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
struct NativeEventFlag {
|
||||
u32_le size;
|
||||
char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
|
||||
u32_le attr;
|
||||
u32_le initPattern;
|
||||
u32_le currentPattern;
|
||||
s32_le numWaitThreads;
|
||||
};
|
||||
|
||||
struct EventFlagTh {
|
||||
SceUID threadID;
|
||||
u32 bits;
|
||||
u32 wait;
|
||||
u32 outAddr;
|
||||
u64 pausedTimeout;
|
||||
|
||||
bool operator ==(const SceUID &otherThreadID) const {
|
||||
return threadID == otherThreadID;
|
||||
}
|
||||
};
|
||||
|
||||
// Exposed here (rather than kept private to sceKernelEventFlag.cpp) so the WebSocket debugger
|
||||
// can read a live object's state directly via kernelObjects.Get<EventFlag>()/Iterate<EventFlag>()
|
||||
// - see HLEKernelObjectSubscriber.cpp. That's a read-only use: nothing outside this file should
|
||||
// call DoState() or otherwise mutate an EventFlag - it's public here for this file's own use as
|
||||
// before, not an invitation to write to it from elsewhere.
|
||||
class EventFlag : public KernelObject {
|
||||
public:
|
||||
const char *GetName() override { return nef.name; }
|
||||
const char *GetTypeName() override { return GetStaticTypeName(); }
|
||||
static const char *GetStaticTypeName() { return "EventFlag"; }
|
||||
void GetQuickInfo(char *ptr, int size) override {
|
||||
snprintf(ptr, size, "init=%08x cur=%08x numwait=%i",
|
||||
nef.initPattern,
|
||||
nef.currentPattern,
|
||||
nef.numWaitThreads);
|
||||
}
|
||||
|
||||
static u32 GetMissingErrorCode() {
|
||||
return SCE_KERNEL_ERROR_UNKNOWN_EVFID;
|
||||
}
|
||||
static int GetStaticIDType() { return SCE_KERNEL_TMID_EventFlag; }
|
||||
int GetIDType() const override { return SCE_KERNEL_TMID_EventFlag; }
|
||||
|
||||
void DoState(PointerWrap &p) override;
|
||||
|
||||
NativeEventFlag nef;
|
||||
std::vector<EventFlagTh> waitingThreads;
|
||||
// Key is the callback id it was for, or if no callback, the thread id.
|
||||
std::map<SceUID, EventFlagTh> pausedWaits;
|
||||
};
|
||||
|
||||
int sceKernelCreateEventFlag(const char *name, u32 flag_attr, u32 flag_initPattern, u32 optPtr);
|
||||
u32 sceKernelClearEventFlag(SceUID id, u32 bits);
|
||||
u32 sceKernelDeleteEventFlag(SceUID uid);
|
||||
|
||||
+179
-224
@@ -48,261 +48,216 @@ static const u32 MSGPIPE_WAIT_VALUE_RECV = 1;
|
||||
// State: the timer for MsgPipe timeouts.
|
||||
static int waitTimer = -1;
|
||||
|
||||
struct NativeMsgPipe
|
||||
{
|
||||
SceSize_le size;
|
||||
char name[32];
|
||||
SceUInt_le attr;
|
||||
s32_le bufSize;
|
||||
s32_le freeSize;
|
||||
s32_le numSendWaitThreads;
|
||||
s32_le numReceiveWaitThreads;
|
||||
};
|
||||
|
||||
struct MsgPipeWaitingThread
|
||||
{
|
||||
SceUID threadID;
|
||||
u32 bufAddr;
|
||||
u32 bufSize;
|
||||
// Free space at the end for receive, valid/free to read bytes from end for send.
|
||||
u32 freeSize;
|
||||
s32 waitMode;
|
||||
PSPPointer<u32_le> transferredBytes;
|
||||
u64 pausedTimeout;
|
||||
|
||||
bool IsStillWaiting(SceUID waitID) const
|
||||
{
|
||||
return HLEKernel::VerifyWait(threadID, WAITTYPE_MSGPIPE, waitID);
|
||||
}
|
||||
|
||||
void WriteCurrentTimeout(SceUID waitID) const
|
||||
{
|
||||
u32 error;
|
||||
if (IsStillWaiting(waitID))
|
||||
{
|
||||
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
|
||||
if (timeoutPtr != 0 && waitTimer != -1)
|
||||
{
|
||||
// Remove any event for this thread.
|
||||
s64 cyclesLeft = CoreTiming::UnscheduleEvent(waitTimer, threadID);
|
||||
Memory::WriteOrException_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Complete(SceUID waitID, int result) const
|
||||
{
|
||||
if (IsStillWaiting(waitID))
|
||||
{
|
||||
WriteCurrentTimeout(waitID);
|
||||
__KernelResumeThreadFromWait(threadID, result);
|
||||
}
|
||||
}
|
||||
|
||||
void Cancel(SceUID waitID, int result) const
|
||||
{
|
||||
Complete(waitID, result);
|
||||
}
|
||||
|
||||
void ReadBuffer(u32 destPtr, u32 len)
|
||||
{
|
||||
Memory::Memcpy(destPtr, bufAddr + bufSize - freeSize, len, "MsgPipeReadBuffer");
|
||||
freeSize -= len;
|
||||
if (transferredBytes.IsValid())
|
||||
*transferredBytes += len;
|
||||
}
|
||||
|
||||
void WriteBuffer(u32 srcPtr, u32 len)
|
||||
{
|
||||
Memory::Memcpy(bufAddr + (bufSize - freeSize), srcPtr, len, "MsgPipeWriteBuffer");
|
||||
freeSize -= len;
|
||||
if (transferredBytes.IsValid())
|
||||
*transferredBytes += len;
|
||||
}
|
||||
|
||||
bool operator ==(const SceUID &otherThreadID) const
|
||||
{
|
||||
return threadID == otherThreadID;
|
||||
}
|
||||
};
|
||||
// NativeMsgPipe/MsgPipeWaitingThread/MsgPipe itself now live in sceKernelMsgPipe.h - see the
|
||||
// comment on the class there for why.
|
||||
|
||||
static bool __KernelMsgPipeThreadSortPriority(const MsgPipeWaitingThread &thread1, const MsgPipeWaitingThread &thread2)
|
||||
{
|
||||
return __KernelThreadSortPriority(thread1.threadID, thread2.threadID);
|
||||
}
|
||||
|
||||
struct MsgPipe : public KernelObject
|
||||
bool MsgPipeWaitingThread::IsStillWaiting(SceUID waitID) const
|
||||
{
|
||||
const char *GetName() override { return nmp.name; }
|
||||
const char *GetTypeName() override { return GetStaticTypeName(); }
|
||||
static const char *GetStaticTypeName() { return "MsgPipe"; }
|
||||
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_MPPID; }
|
||||
static int GetStaticIDType() { return SCE_KERNEL_TMID_Mpipe; }
|
||||
int GetIDType() const override { return SCE_KERNEL_TMID_Mpipe; }
|
||||
return HLEKernel::VerifyWait(threadID, WAITTYPE_MSGPIPE, waitID);
|
||||
}
|
||||
|
||||
MsgPipe() : buffer(0) {}
|
||||
~MsgPipe() {
|
||||
if (buffer != 0) {
|
||||
BlockAllocator *alloc = BlockAllocatorFromAddr(buffer);
|
||||
_assert_msg_(alloc != nullptr, "Should always have a valid allocator/address");
|
||||
if (alloc)
|
||||
alloc->Free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetUsedSize()
|
||||
void MsgPipeWaitingThread::WriteCurrentTimeout(SceUID waitID) const
|
||||
{
|
||||
u32 error;
|
||||
if (IsStillWaiting(waitID))
|
||||
{
|
||||
return (u32)(nmp.bufSize - nmp.freeSize);
|
||||
}
|
||||
|
||||
void AddWaitingThread(std::vector<MsgPipeWaitingThread> &list, SceUID id, u32 addr, u32 size, int waitMode, u32 transferredBytesAddr)
|
||||
{
|
||||
MsgPipeWaitingThread thread = { id, addr, size, size, waitMode, { transferredBytesAddr } };
|
||||
// Start out with 0 transferred bytes while waiting.
|
||||
// TODO: for receive, it might be a different (partial) number.
|
||||
if (thread.transferredBytes.IsValid())
|
||||
*thread.transferredBytes = 0;
|
||||
|
||||
list.push_back(thread);
|
||||
}
|
||||
|
||||
void AddSendWaitingThread(SceUID id, u32 addr, u32 size, int waitMode, u32 transferredBytesAddr)
|
||||
{
|
||||
AddWaitingThread(sendWaitingThreads, id, addr, size, waitMode, transferredBytesAddr);
|
||||
}
|
||||
|
||||
void AddReceiveWaitingThread(SceUID id, u32 addr, u32 size, int waitMode, u32 transferredBytesAddr)
|
||||
{
|
||||
AddWaitingThread(receiveWaitingThreads, id, addr, size, waitMode, transferredBytesAddr);
|
||||
}
|
||||
|
||||
bool CheckSendThreads()
|
||||
{
|
||||
SortSendThreads();
|
||||
|
||||
bool wokeThreads = false;
|
||||
bool filledSpace = false;
|
||||
while (!sendWaitingThreads.empty() && nmp.freeSize > 0)
|
||||
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
|
||||
if (timeoutPtr != 0 && waitTimer != -1)
|
||||
{
|
||||
MsgPipeWaitingThread *thread = &sendWaitingThreads.front();
|
||||
u32 bytesToSend = std::min(thread->freeSize, (u32) nmp.freeSize);
|
||||
|
||||
thread->ReadBuffer(buffer + GetUsedSize(), bytesToSend);
|
||||
nmp.freeSize -= bytesToSend;
|
||||
filledSpace = true;
|
||||
|
||||
if (thread->waitMode == SCE_KERNEL_MPW_ASAP || thread->freeSize == 0)
|
||||
{
|
||||
thread->Complete(GetUID(), 0);
|
||||
sendWaitingThreads.erase(sendWaitingThreads.begin());
|
||||
wokeThreads = true;
|
||||
thread = NULL;
|
||||
}
|
||||
// Unlike receives, we don't do partial sends. Stop at first blocked thread.
|
||||
else
|
||||
break;
|
||||
// Remove any event for this thread.
|
||||
s64 cyclesLeft = CoreTiming::UnscheduleEvent(waitTimer, threadID);
|
||||
Memory::WriteOrException_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr);
|
||||
}
|
||||
|
||||
if (filledSpace)
|
||||
wokeThreads |= CheckReceiveThreads();
|
||||
|
||||
return wokeThreads;
|
||||
}
|
||||
}
|
||||
|
||||
// This function should be only ran when the temporary buffer size is not 0 (otherwise, data is copied directly to the threads)
|
||||
bool CheckReceiveThreads()
|
||||
void MsgPipeWaitingThread::Complete(SceUID waitID, int result) const
|
||||
{
|
||||
if (IsStillWaiting(waitID))
|
||||
{
|
||||
SortReceiveThreads();
|
||||
WriteCurrentTimeout(waitID);
|
||||
__KernelResumeThreadFromWait(threadID, result);
|
||||
}
|
||||
}
|
||||
|
||||
bool wokeThreads = false;
|
||||
bool freedSpace = false;
|
||||
while (!receiveWaitingThreads.empty() && GetUsedSize() > 0)
|
||||
void MsgPipeWaitingThread::Cancel(SceUID waitID, int result) const
|
||||
{
|
||||
Complete(waitID, result);
|
||||
}
|
||||
|
||||
void MsgPipeWaitingThread::ReadBuffer(u32 destPtr, u32 len)
|
||||
{
|
||||
Memory::Memcpy(destPtr, bufAddr + bufSize - freeSize, len, "MsgPipeReadBuffer");
|
||||
freeSize -= len;
|
||||
if (transferredBytes.IsValid())
|
||||
*transferredBytes += len;
|
||||
}
|
||||
|
||||
void MsgPipeWaitingThread::WriteBuffer(u32 srcPtr, u32 len)
|
||||
{
|
||||
Memory::Memcpy(bufAddr + (bufSize - freeSize), srcPtr, len, "MsgPipeWriteBuffer");
|
||||
freeSize -= len;
|
||||
if (transferredBytes.IsValid())
|
||||
*transferredBytes += len;
|
||||
}
|
||||
|
||||
MsgPipe::MsgPipe() : buffer(0) {}
|
||||
MsgPipe::~MsgPipe() {
|
||||
if (buffer != 0) {
|
||||
BlockAllocator *alloc = BlockAllocatorFromAddr(buffer);
|
||||
_assert_msg_(alloc != nullptr, "Should always have a valid allocator/address");
|
||||
if (alloc)
|
||||
alloc->Free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
u32 MsgPipe::GetUsedSize()
|
||||
{
|
||||
return (u32)(nmp.bufSize - nmp.freeSize);
|
||||
}
|
||||
|
||||
void MsgPipe::AddWaitingThread(std::vector<MsgPipeWaitingThread> &list, SceUID id, u32 addr, u32 size, int waitMode, u32 transferredBytesAddr)
|
||||
{
|
||||
MsgPipeWaitingThread thread = { id, addr, size, size, waitMode, { transferredBytesAddr } };
|
||||
// Start out with 0 transferred bytes while waiting.
|
||||
// TODO: for receive, it might be a different (partial) number.
|
||||
if (thread.transferredBytes.IsValid())
|
||||
*thread.transferredBytes = 0;
|
||||
|
||||
list.push_back(thread);
|
||||
}
|
||||
|
||||
void MsgPipe::AddSendWaitingThread(SceUID id, u32 addr, u32 size, int waitMode, u32 transferredBytesAddr)
|
||||
{
|
||||
AddWaitingThread(sendWaitingThreads, id, addr, size, waitMode, transferredBytesAddr);
|
||||
}
|
||||
|
||||
void MsgPipe::AddReceiveWaitingThread(SceUID id, u32 addr, u32 size, int waitMode, u32 transferredBytesAddr)
|
||||
{
|
||||
AddWaitingThread(receiveWaitingThreads, id, addr, size, waitMode, transferredBytesAddr);
|
||||
}
|
||||
|
||||
bool MsgPipe::CheckSendThreads()
|
||||
{
|
||||
SortSendThreads();
|
||||
|
||||
bool wokeThreads = false;
|
||||
bool filledSpace = false;
|
||||
while (!sendWaitingThreads.empty() && nmp.freeSize > 0)
|
||||
{
|
||||
MsgPipeWaitingThread *thread = &sendWaitingThreads.front();
|
||||
u32 bytesToSend = std::min(thread->freeSize, (u32) nmp.freeSize);
|
||||
|
||||
thread->ReadBuffer(buffer + GetUsedSize(), bytesToSend);
|
||||
nmp.freeSize -= bytesToSend;
|
||||
filledSpace = true;
|
||||
|
||||
if (thread->waitMode == SCE_KERNEL_MPW_ASAP || thread->freeSize == 0)
|
||||
{
|
||||
MsgPipeWaitingThread *thread = &receiveWaitingThreads.front();
|
||||
// Receive as much as possible, even if it's not enough to wake up.
|
||||
u32 bytesToSend = std::min(thread->freeSize, GetUsedSize());
|
||||
|
||||
u8* ptr = Memory::GetPointerWriteOrException(buffer);
|
||||
thread->WriteBuffer(buffer, bytesToSend);
|
||||
// Put the unused data at the start of the buffer.
|
||||
nmp.freeSize += bytesToSend;
|
||||
memmove(ptr, ptr + bytesToSend, GetUsedSize());
|
||||
freedSpace = true;
|
||||
|
||||
if (thread->waitMode == SCE_KERNEL_MPW_ASAP || thread->freeSize == 0)
|
||||
{
|
||||
thread->Complete(GetUID(), 0);
|
||||
receiveWaitingThreads.erase(receiveWaitingThreads.begin());
|
||||
wokeThreads = true;
|
||||
thread = NULL;
|
||||
}
|
||||
// Stop at the first that can't wake up.
|
||||
else
|
||||
break;
|
||||
thread->Complete(GetUID(), 0);
|
||||
sendWaitingThreads.erase(sendWaitingThreads.begin());
|
||||
wokeThreads = true;
|
||||
thread = NULL;
|
||||
}
|
||||
|
||||
if (freedSpace)
|
||||
wokeThreads |= CheckSendThreads();
|
||||
|
||||
return wokeThreads;
|
||||
// Unlike receives, we don't do partial sends. Stop at first blocked thread.
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
void SortThreads(std::vector<MsgPipeWaitingThread> &waitingThreads, bool usePrio)
|
||||
if (filledSpace)
|
||||
wokeThreads |= CheckReceiveThreads();
|
||||
|
||||
return wokeThreads;
|
||||
}
|
||||
|
||||
// This function should be only ran when the temporary buffer size is not 0 (otherwise, data is copied directly to the threads)
|
||||
bool MsgPipe::CheckReceiveThreads()
|
||||
{
|
||||
SortReceiveThreads();
|
||||
|
||||
bool wokeThreads = false;
|
||||
bool freedSpace = false;
|
||||
while (!receiveWaitingThreads.empty() && GetUsedSize() > 0)
|
||||
{
|
||||
// Clean up any not waiting at the same time.
|
||||
HLEKernel::CleanupWaitingThreads(WAITTYPE_MSGPIPE, GetUID(), waitingThreads);
|
||||
MsgPipeWaitingThread *thread = &receiveWaitingThreads.front();
|
||||
// Receive as much as possible, even if it's not enough to wake up.
|
||||
u32 bytesToSend = std::min(thread->freeSize, GetUsedSize());
|
||||
|
||||
if (usePrio)
|
||||
std::stable_sort(waitingThreads.begin(), waitingThreads.end(), __KernelMsgPipeThreadSortPriority);
|
||||
u8* ptr = Memory::GetPointerWriteOrException(buffer);
|
||||
thread->WriteBuffer(buffer, bytesToSend);
|
||||
// Put the unused data at the start of the buffer.
|
||||
nmp.freeSize += bytesToSend;
|
||||
memmove(ptr, ptr + bytesToSend, GetUsedSize());
|
||||
freedSpace = true;
|
||||
|
||||
if (thread->waitMode == SCE_KERNEL_MPW_ASAP || thread->freeSize == 0)
|
||||
{
|
||||
thread->Complete(GetUID(), 0);
|
||||
receiveWaitingThreads.erase(receiveWaitingThreads.begin());
|
||||
wokeThreads = true;
|
||||
thread = NULL;
|
||||
}
|
||||
// Stop at the first that can't wake up.
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
void SortReceiveThreads()
|
||||
{
|
||||
bool usePrio = (nmp.attr & SCE_KERNEL_MPA_THPRI_R) != 0;
|
||||
SortThreads(receiveWaitingThreads, usePrio);
|
||||
}
|
||||
if (freedSpace)
|
||||
wokeThreads |= CheckSendThreads();
|
||||
|
||||
void SortSendThreads()
|
||||
{
|
||||
bool usePrio = (nmp.attr & SCE_KERNEL_MPA_THPRI_S) != 0;
|
||||
SortThreads(sendWaitingThreads, usePrio);
|
||||
}
|
||||
return wokeThreads;
|
||||
}
|
||||
|
||||
void RemoveReceiveWaitingThread(SceUID threadID)
|
||||
{
|
||||
HLEKernel::RemoveWaitingThread(receiveWaitingThreads, threadID);
|
||||
}
|
||||
void MsgPipe::SortThreads(std::vector<MsgPipeWaitingThread> &waitingThreads, bool usePrio)
|
||||
{
|
||||
// Clean up any not waiting at the same time.
|
||||
HLEKernel::CleanupWaitingThreads(WAITTYPE_MSGPIPE, GetUID(), waitingThreads);
|
||||
|
||||
void RemoveSendWaitingThread(SceUID threadID)
|
||||
{
|
||||
HLEKernel::RemoveWaitingThread(sendWaitingThreads, threadID);
|
||||
}
|
||||
if (usePrio)
|
||||
std::stable_sort(waitingThreads.begin(), waitingThreads.end(), __KernelMsgPipeThreadSortPriority);
|
||||
}
|
||||
|
||||
void DoState(PointerWrap &p) override
|
||||
{
|
||||
auto s = p.Section("MsgPipe", 1);
|
||||
if (!s)
|
||||
return;
|
||||
void MsgPipe::SortReceiveThreads()
|
||||
{
|
||||
bool usePrio = (nmp.attr & SCE_KERNEL_MPA_THPRI_R) != 0;
|
||||
SortThreads(receiveWaitingThreads, usePrio);
|
||||
}
|
||||
|
||||
Do(p, nmp);
|
||||
MsgPipeWaitingThread mpwt1 = {0}, mpwt2 = {0};
|
||||
Do(p, sendWaitingThreads, mpwt1);
|
||||
Do(p, receiveWaitingThreads, mpwt2);
|
||||
Do(p, pausedSendWaits);
|
||||
Do(p, pausedReceiveWaits);
|
||||
Do(p, buffer);
|
||||
}
|
||||
void MsgPipe::SortSendThreads()
|
||||
{
|
||||
bool usePrio = (nmp.attr & SCE_KERNEL_MPA_THPRI_S) != 0;
|
||||
SortThreads(sendWaitingThreads, usePrio);
|
||||
}
|
||||
|
||||
NativeMsgPipe nmp;
|
||||
void MsgPipe::RemoveReceiveWaitingThread(SceUID threadID)
|
||||
{
|
||||
HLEKernel::RemoveWaitingThread(receiveWaitingThreads, threadID);
|
||||
}
|
||||
|
||||
std::vector<MsgPipeWaitingThread> sendWaitingThreads;
|
||||
std::vector<MsgPipeWaitingThread> receiveWaitingThreads;
|
||||
// Key is the callback id it was for, or if no callback, the thread id.
|
||||
std::map<SceUID, MsgPipeWaitingThread> pausedSendWaits;
|
||||
std::map<SceUID, MsgPipeWaitingThread> pausedReceiveWaits;
|
||||
void MsgPipe::RemoveSendWaitingThread(SceUID threadID)
|
||||
{
|
||||
HLEKernel::RemoveWaitingThread(sendWaitingThreads, threadID);
|
||||
}
|
||||
|
||||
u32 buffer;
|
||||
};
|
||||
void MsgPipe::DoState(PointerWrap &p)
|
||||
{
|
||||
auto s = p.Section("MsgPipe", 1);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
Do(p, nmp);
|
||||
MsgPipeWaitingThread mpwt1 = {0}, mpwt2 = {0};
|
||||
Do(p, sendWaitingThreads, mpwt1);
|
||||
Do(p, receiveWaitingThreads, mpwt2);
|
||||
Do(p, pausedSendWaits);
|
||||
Do(p, pausedReceiveWaits);
|
||||
Do(p, buffer);
|
||||
}
|
||||
|
||||
KernelObject *__KernelMsgPipeObject()
|
||||
{
|
||||
|
||||
@@ -17,8 +17,89 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "Core/HLE/sceKernel.h"
|
||||
#include "Core/HLE/ErrorCodes.h"
|
||||
#include "Core/MemMap.h"
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
struct NativeMsgPipe {
|
||||
SceSize_le size;
|
||||
char name[32];
|
||||
SceUInt_le attr;
|
||||
s32_le bufSize;
|
||||
s32_le freeSize;
|
||||
s32_le numSendWaitThreads;
|
||||
s32_le numReceiveWaitThreads;
|
||||
};
|
||||
|
||||
struct MsgPipeWaitingThread {
|
||||
SceUID threadID;
|
||||
u32 bufAddr;
|
||||
u32 bufSize;
|
||||
// Free space at the end for receive, valid/free to read bytes from end for send.
|
||||
u32 freeSize;
|
||||
s32 waitMode;
|
||||
PSPPointer<u32_le> transferredBytes;
|
||||
u64 pausedTimeout;
|
||||
|
||||
bool IsStillWaiting(SceUID waitID) const;
|
||||
void WriteCurrentTimeout(SceUID waitID) const;
|
||||
void Complete(SceUID waitID, int result) const;
|
||||
void Cancel(SceUID waitID, int result) const;
|
||||
void ReadBuffer(u32 destPtr, u32 len);
|
||||
void WriteBuffer(u32 srcPtr, u32 len);
|
||||
|
||||
bool operator ==(const SceUID &otherThreadID) const {
|
||||
return threadID == otherThreadID;
|
||||
}
|
||||
};
|
||||
|
||||
// Exposed here (rather than kept private to sceKernelMsgPipe.cpp) so the WebSocket debugger can
|
||||
// read a live object's state directly via kernelObjects.Get<MsgPipe>()/Iterate<MsgPipe>() - see
|
||||
// HLEKernelObjectSubscriber.cpp. That's a read-only use: nothing outside this file should call
|
||||
// any of these methods (DoState, the Check*/Sort*/Add* helpers, etc.) or otherwise mutate a
|
||||
// MsgPipe - it's public here for this file's own use as before, not an invitation to write to it
|
||||
// from elsewhere. Method bodies stay defined in the .cpp (only the trivial one-liners are
|
||||
// inline), so this header doesn't need to pull in Memory::/BlockAllocator/CoreTiming.
|
||||
struct MsgPipe : public KernelObject {
|
||||
const char *GetName() override { return nmp.name; }
|
||||
const char *GetTypeName() override { return GetStaticTypeName(); }
|
||||
static const char *GetStaticTypeName() { return "MsgPipe"; }
|
||||
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_MPPID; }
|
||||
static int GetStaticIDType() { return SCE_KERNEL_TMID_Mpipe; }
|
||||
int GetIDType() const override { return SCE_KERNEL_TMID_Mpipe; }
|
||||
|
||||
MsgPipe();
|
||||
~MsgPipe();
|
||||
|
||||
u32 GetUsedSize();
|
||||
void AddWaitingThread(std::vector<MsgPipeWaitingThread> &list, SceUID id, u32 addr, u32 size, int waitMode, u32 transferredBytesAddr);
|
||||
void AddSendWaitingThread(SceUID id, u32 addr, u32 size, int waitMode, u32 transferredBytesAddr);
|
||||
void AddReceiveWaitingThread(SceUID id, u32 addr, u32 size, int waitMode, u32 transferredBytesAddr);
|
||||
bool CheckSendThreads();
|
||||
bool CheckReceiveThreads();
|
||||
void SortThreads(std::vector<MsgPipeWaitingThread> &waitingThreads, bool usePrio);
|
||||
void SortReceiveThreads();
|
||||
void SortSendThreads();
|
||||
void RemoveReceiveWaitingThread(SceUID threadID);
|
||||
void RemoveSendWaitingThread(SceUID threadID);
|
||||
void DoState(PointerWrap &p) override;
|
||||
|
||||
NativeMsgPipe nmp;
|
||||
|
||||
std::vector<MsgPipeWaitingThread> sendWaitingThreads;
|
||||
std::vector<MsgPipeWaitingThread> receiveWaitingThreads;
|
||||
// Key is the callback id it was for, or if no callback, the thread id.
|
||||
std::map<SceUID, MsgPipeWaitingThread> pausedSendWaits;
|
||||
std::map<SceUID, MsgPipeWaitingThread> pausedReceiveWaits;
|
||||
|
||||
u32 buffer;
|
||||
};
|
||||
|
||||
int sceKernelCreateMsgPipe(const char *name, int partition, u32 attr, u32 size, u32 optionsPtr);
|
||||
int sceKernelDeleteMsgPipe(SceUID uid);
|
||||
int sceKernelSendMsgPipe(SceUID uid, u32 sendBufAddr, u32 sendSize, u32 waitMode, u32 resultAddr, u32 timeoutPtr);
|
||||
|
||||
+11
-38
@@ -39,45 +39,18 @@
|
||||
#define PSP_MUTEX_ATTR_ALLOW_RECURSIVE 0x200
|
||||
#define PSP_MUTEX_ATTR_KNOWN (PSP_MUTEX_ATTR_PRIORITY | PSP_MUTEX_ATTR_ALLOW_RECURSIVE)
|
||||
|
||||
struct NativeMutex
|
||||
{
|
||||
SceSize_le size;
|
||||
char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
|
||||
SceUInt_le attr;
|
||||
s32_le initialCount;
|
||||
s32_le lockLevel;
|
||||
SceUID_le lockThread;
|
||||
// Not kept up to date.
|
||||
s32_le numWaitThreads;
|
||||
};
|
||||
|
||||
struct PSPMutex : public KernelObject
|
||||
{
|
||||
const char *GetName() override { return nm.name; }
|
||||
const char *GetTypeName() override { return GetStaticTypeName(); }
|
||||
static const char *GetStaticTypeName() { return "Mutex"; }
|
||||
static u32 GetMissingErrorCode() { return SCE_MUTEX_ERROR_NO_SUCH_MUTEX; }
|
||||
static int GetStaticIDType() { return SCE_KERNEL_TMID_Mutex; }
|
||||
int GetIDType() const override { return SCE_KERNEL_TMID_Mutex; }
|
||||
|
||||
void DoState(PointerWrap &p) override
|
||||
{
|
||||
auto s = p.Section("Mutex", 1);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
Do(p, nm);
|
||||
SceUID dv = 0;
|
||||
Do(p, waitingThreads, dv);
|
||||
Do(p, pausedWaits);
|
||||
}
|
||||
|
||||
NativeMutex nm;
|
||||
std::vector<SceUID> waitingThreads;
|
||||
// Key is the callback id it was for, or if no callback, the thread id.
|
||||
std::map<SceUID, u64> pausedWaits;
|
||||
};
|
||||
// NativeMutex/PSPMutex itself now live in sceKernelMutex.h - see the comment on the class there
|
||||
// for why.
|
||||
void PSPMutex::DoState(PointerWrap &p) {
|
||||
auto s = p.Section("Mutex", 1);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
Do(p, nm);
|
||||
SceUID dv = 0;
|
||||
Do(p, waitingThreads, dv);
|
||||
Do(p, pausedWaits);
|
||||
}
|
||||
|
||||
struct NativeLwMutexWorkarea
|
||||
{
|
||||
|
||||
@@ -17,6 +17,46 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "Core/HLE/sceKernel.h"
|
||||
#include "Core/HLE/ErrorCodes.h"
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
struct NativeMutex {
|
||||
SceSize_le size;
|
||||
char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
|
||||
SceUInt_le attr;
|
||||
s32_le initialCount;
|
||||
s32_le lockLevel;
|
||||
SceUID_le lockThread;
|
||||
// Not kept up to date.
|
||||
s32_le numWaitThreads;
|
||||
};
|
||||
|
||||
// Exposed here (rather than kept private to sceKernelMutex.cpp) so the WebSocket debugger can
|
||||
// read a live object's state directly via kernelObjects.Get<PSPMutex>()/Iterate<PSPMutex>() - see
|
||||
// HLEKernelObjectSubscriber.cpp. That's a read-only use: nothing outside this file should call
|
||||
// DoState() or otherwise mutate a PSPMutex - it's public here for this file's own use as before,
|
||||
// not an invitation to write to it from elsewhere.
|
||||
struct PSPMutex : public KernelObject {
|
||||
const char *GetName() override { return nm.name; }
|
||||
const char *GetTypeName() override { return GetStaticTypeName(); }
|
||||
static const char *GetStaticTypeName() { return "Mutex"; }
|
||||
static u32 GetMissingErrorCode() { return SCE_MUTEX_ERROR_NO_SUCH_MUTEX; }
|
||||
static int GetStaticIDType() { return SCE_KERNEL_TMID_Mutex; }
|
||||
int GetIDType() const override { return SCE_KERNEL_TMID_Mutex; }
|
||||
|
||||
void DoState(PointerWrap &p) override;
|
||||
|
||||
NativeMutex nm;
|
||||
std::vector<SceUID> waitingThreads;
|
||||
// Key is the callback id it was for, or if no callback, the thread id.
|
||||
std::map<SceUID, u64> pausedWaits;
|
||||
};
|
||||
|
||||
int sceKernelCancelMutex(SceUID uid, int count, u32 numWaitThreadsPtr);
|
||||
int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optionsPtr);
|
||||
int sceKernelDeleteMutex(SceUID id);
|
||||
|
||||
@@ -38,51 +38,18 @@
|
||||
* @see sceKernelReferSemaStatus.
|
||||
*/
|
||||
|
||||
struct NativeSemaphore
|
||||
{
|
||||
/** Size of the ::SceKernelSemaInfo structure. */
|
||||
SceSize_le size;
|
||||
/** NUL-terminated name of the semaphore. */
|
||||
char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
|
||||
/** Attributes. */
|
||||
SceUInt_le attr;
|
||||
/** The initial count the semaphore was created with. */
|
||||
s32_le initCount;
|
||||
/** The current count. */
|
||||
s32_le currentCount;
|
||||
/** The maximum count. */
|
||||
s32_le maxCount;
|
||||
/** The number of threads waiting on the semaphore. */
|
||||
s32_le numWaitThreads;
|
||||
};
|
||||
// NativeSemaphore/PSPSemaphore itself now live in sceKernelSemaphore.h - see the comment on the
|
||||
// class there for why.
|
||||
void PSPSemaphore::DoState(PointerWrap &p) {
|
||||
auto s = p.Section("Semaphore", 1);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
|
||||
struct PSPSemaphore : public KernelObject {
|
||||
const char *GetName() override { return ns.name; }
|
||||
const char *GetTypeName() override { return GetStaticTypeName(); }
|
||||
static const char *GetStaticTypeName() { return "Semaphore"; }
|
||||
|
||||
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_SEMID; }
|
||||
static int GetStaticIDType() { return SCE_KERNEL_TMID_Semaphore; }
|
||||
int GetIDType() const override { return SCE_KERNEL_TMID_Semaphore; }
|
||||
|
||||
void DoState(PointerWrap &p) override
|
||||
{
|
||||
auto s = p.Section("Semaphore", 1);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
Do(p, ns);
|
||||
SceUID dv = 0;
|
||||
Do(p, waitingThreads, dv);
|
||||
Do(p, pausedWaits);
|
||||
}
|
||||
|
||||
NativeSemaphore ns;
|
||||
std::vector<SceUID> waitingThreads;
|
||||
// Key is the callback id it was for, or if no callback, the thread id.
|
||||
std::map<SceUID, u64> pausedWaits;
|
||||
};
|
||||
Do(p, ns);
|
||||
SceUID dv = 0;
|
||||
Do(p, waitingThreads, dv);
|
||||
Do(p, pausedWaits);
|
||||
}
|
||||
|
||||
static int semaWaitTimer = -1;
|
||||
|
||||
|
||||
@@ -17,6 +17,54 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "Core/HLE/sceKernel.h"
|
||||
#include "Core/HLE/ErrorCodes.h"
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
struct NativeSemaphore {
|
||||
/** Size of the ::SceKernelSemaInfo structure. */
|
||||
SceSize_le size;
|
||||
/** NUL-terminated name of the semaphore. */
|
||||
char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
|
||||
/** Attributes. */
|
||||
SceUInt_le attr;
|
||||
/** The initial count the semaphore was created with. */
|
||||
s32_le initCount;
|
||||
/** The current count. */
|
||||
s32_le currentCount;
|
||||
/** The maximum count. */
|
||||
s32_le maxCount;
|
||||
/** The number of threads waiting on the semaphore. */
|
||||
s32_le numWaitThreads;
|
||||
};
|
||||
|
||||
// Exposed here (rather than kept private to sceKernelSemaphore.cpp) so the WebSocket debugger
|
||||
// can read a live object's state directly via
|
||||
// kernelObjects.Get<PSPSemaphore>()/Iterate<PSPSemaphore>() - see HLEKernelObjectSubscriber.cpp.
|
||||
// That's a read-only use: nothing outside this file should call DoState() or otherwise mutate a
|
||||
// PSPSemaphore - it's public here for this file's own use as before, not an invitation to write
|
||||
// to it from elsewhere.
|
||||
struct PSPSemaphore : public KernelObject {
|
||||
const char *GetName() override { return ns.name; }
|
||||
const char *GetTypeName() override { return GetStaticTypeName(); }
|
||||
static const char *GetStaticTypeName() { return "Semaphore"; }
|
||||
|
||||
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_SEMID; }
|
||||
static int GetStaticIDType() { return SCE_KERNEL_TMID_Semaphore; }
|
||||
int GetIDType() const override { return SCE_KERNEL_TMID_Semaphore; }
|
||||
|
||||
void DoState(PointerWrap &p) override;
|
||||
|
||||
NativeSemaphore ns;
|
||||
std::vector<SceUID> waitingThreads;
|
||||
// Key is the callback id it was for, or if no callback, the thread id.
|
||||
std::map<SceUID, u64> pausedWaits;
|
||||
};
|
||||
|
||||
int sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr);
|
||||
int sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u32 optionPtr);
|
||||
int sceKernelDeleteSema(SceUID id);
|
||||
|
||||
@@ -107,56 +107,23 @@ enum ThreadEventType {
|
||||
|
||||
bool __KernelThreadTriggerEvent(bool isKernel, SceUID threadID, ThreadEventType type);
|
||||
|
||||
struct NativeCallback
|
||||
// NativeCallback/PSPCallback itself now live in sceKernelThread.h - see the comment on the class
|
||||
// there for why.
|
||||
void PSPCallback::DoState(PointerWrap &p)
|
||||
{
|
||||
SceUInt_le size;
|
||||
char name[32];
|
||||
SceUID_le threadId;
|
||||
u32_le entrypoint;
|
||||
u32_le commonArgument;
|
||||
auto s = p.Section("Callback", 1);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
s32_le notifyCount;
|
||||
s32_le notifyArg;
|
||||
};
|
||||
|
||||
class PSPCallback : public KernelObject {
|
||||
public:
|
||||
const char *GetName() override { return nc.name; }
|
||||
const char *GetTypeName() override { return GetStaticTypeName(); }
|
||||
static const char *GetStaticTypeName() { return "CallBack"; }
|
||||
|
||||
void GetQuickInfo(char *ptr, int size) override {
|
||||
snprintf(ptr, size, "thread=%i, argument= %08x",
|
||||
//hackAddress,
|
||||
nc.threadId,
|
||||
nc.commonArgument);
|
||||
}
|
||||
|
||||
~PSPCallback() {
|
||||
}
|
||||
|
||||
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_CBID; }
|
||||
static int GetStaticIDType() { return SCE_KERNEL_TMID_Callback; }
|
||||
int GetIDType() const override { return SCE_KERNEL_TMID_Callback; }
|
||||
|
||||
void DoState(PointerWrap &p) override
|
||||
{
|
||||
auto s = p.Section("Callback", 1);
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
Do(p, nc);
|
||||
// Saved values were moved to mips call, ignoring here.
|
||||
u32 legacySaved = 0;
|
||||
Do(p, legacySaved);
|
||||
Do(p, legacySaved);
|
||||
Do(p, legacySaved);
|
||||
Do(p, legacySaved);
|
||||
Do(p, legacySaved);
|
||||
}
|
||||
|
||||
NativeCallback nc;
|
||||
};
|
||||
Do(p, nc);
|
||||
// Saved values were moved to mips call, ignoring here.
|
||||
u32 legacySaved = 0;
|
||||
Do(p, legacySaved);
|
||||
Do(p, legacySaved);
|
||||
Do(p, legacySaved);
|
||||
Do(p, legacySaved);
|
||||
Do(p, legacySaved);
|
||||
}
|
||||
|
||||
// Owns outstanding MIPS calls and provides a way to get them by ID.
|
||||
class MipsCallManager {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
@@ -24,6 +25,7 @@
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/HLE/sceKernel.h"
|
||||
#include "Core/HLE/ErrorCodes.h"
|
||||
#include "Core/HLE/PSPThreadContext.h"
|
||||
#include "Core/HLE/KernelThreadDebugInterface.h"
|
||||
|
||||
@@ -175,6 +177,46 @@ struct NativeThread {
|
||||
s32_le numReleases;
|
||||
};
|
||||
|
||||
struct NativeCallback {
|
||||
SceUInt_le size;
|
||||
char name[32];
|
||||
SceUID_le threadId;
|
||||
u32_le entrypoint;
|
||||
u32_le commonArgument;
|
||||
|
||||
s32_le notifyCount;
|
||||
s32_le notifyArg;
|
||||
};
|
||||
|
||||
// Exposed here (rather than kept private to sceKernelThread.cpp) so the WebSocket debugger can
|
||||
// read a live object's state directly via kernelObjects.Get<PSPCallback>()/Iterate<PSPCallback>()
|
||||
// - see HLEKernelObjectSubscriber.cpp. That's a read-only use: nothing outside this file should
|
||||
// call DoState() or otherwise mutate a PSPCallback - it's public here for this file's own use as
|
||||
// before, not an invitation to write to it from elsewhere.
|
||||
class PSPCallback : public KernelObject {
|
||||
public:
|
||||
const char *GetName() override { return nc.name; }
|
||||
const char *GetTypeName() override { return GetStaticTypeName(); }
|
||||
static const char *GetStaticTypeName() { return "CallBack"; }
|
||||
|
||||
void GetQuickInfo(char *ptr, int size) override {
|
||||
snprintf(ptr, size, "thread=%i, argument= %08x",
|
||||
nc.threadId,
|
||||
nc.commonArgument);
|
||||
}
|
||||
|
||||
~PSPCallback() {
|
||||
}
|
||||
|
||||
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_CBID; }
|
||||
static int GetStaticIDType() { return SCE_KERNEL_TMID_Callback; }
|
||||
int GetIDType() const override { return SCE_KERNEL_TMID_Callback; }
|
||||
|
||||
void DoState(PointerWrap &p) override;
|
||||
|
||||
NativeCallback nc;
|
||||
};
|
||||
|
||||
struct ThreadWaitInfo {
|
||||
u32 waitValue;
|
||||
u32 timeoutPtr;
|
||||
|
||||
Reference in new issue
Block a user