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:
Henrik RydgårdandClaude Sonnet 5 committed 2026-08-17 16:01:23 +02:00
1 parent ce33c964f7
commit 21b7ce7392
11 files changed
+513 -408

No files matched your search

+14
View File
@@ -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++) {
+11 -54
View File
@@ -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 */
+61
View File
@@ -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
View File
@@ -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()
{
+81
View File
@@ -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
View File
@@ -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
{
+40
View File
@@ -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);
+11 -44
View File
@@ -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;
+48
View File
@@ -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);
+15 -48
View File
@@ -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 {
+42
View File
@@ -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;