Merge pull request #633 from Celegans12/feat/kernel-sync-on-address-wait-sized
feat(libkernel): implement sceKernelSyncOnAddressWait32 and sceKernelSyncOnAddressWait64
This commit is contained in:
commit
e8893dfc64
|
|
@ -25,23 +25,16 @@ struct AddressWaiter {
|
|||
std::mutex g_waitersLock;
|
||||
std::unordered_map<std::uintptr_t, std::list<AddressWaiter*>> g_waiters;
|
||||
|
||||
bool IsWordAddress(std::uintptr_t address) {
|
||||
return address != 0 && address % alignof(std::uint32_t) == 0;
|
||||
template <class TValue>
|
||||
bool IsAlignedAddress(std::uintptr_t address) {
|
||||
return address != 0 && address % alignof(TValue) == 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceKernelSyncOnAddressWait(std::uint32_t* address, std::uint32_t expected, const KernelUseconds* timeout, const char* name) {
|
||||
(void)name;
|
||||
template <class TValue>
|
||||
int WaitOnAddress(TValue* address, TValue expected, const KernelUseconds* timeout, const void* caller) {
|
||||
const auto key = reinterpret_cast<std::uintptr_t>(address);
|
||||
if (!IsWordAddress(key)) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(g_waitersLock);
|
||||
if (std::atomic_ref<std::uint32_t>(*address).load() != expected) {
|
||||
if (std::atomic_ref<TValue>(*address).load() != expected) {
|
||||
return SYNC_ON_ADDRESS_OK;
|
||||
}
|
||||
|
||||
|
|
@ -65,13 +58,41 @@ int APS5_VABI sceKernelSyncOnAddressWait(std::uint32_t* address, std::uint32_t e
|
|||
}
|
||||
}
|
||||
lock.unlock();
|
||||
KernelTraceWait_nid_postfix("addr", __builtin_return_address(0), static_cast<std::uint64_t>(waited.count()), !woken);
|
||||
KernelTraceWait_nid_postfix("addr", caller, static_cast<std::uint64_t>(waited.count()), !woken);
|
||||
return woken ? SYNC_ON_ADDRESS_OK : SCE_KERNEL_ERROR_ETIMEDOUT;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceKernelSyncOnAddressWait(std::uint32_t* address, std::uint32_t expected, const KernelUseconds* timeout, const char* name) {
|
||||
(void)name;
|
||||
if (!IsAlignedAddress<std::uint32_t>(reinterpret_cast<std::uintptr_t>(address))) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
return WaitOnAddress(address, expected, timeout, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelSyncOnAddressWait32(std::uint32_t* address, std::uint32_t expected, const KernelUseconds* timeout, const char* name) {
|
||||
(void)name;
|
||||
if (!IsAlignedAddress<std::uint32_t>(reinterpret_cast<std::uintptr_t>(address))) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
return WaitOnAddress(address, expected, timeout, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelSyncOnAddressWait64(std::uint64_t* address, std::uint64_t expected, const KernelUseconds* timeout, const char* name) {
|
||||
(void)name;
|
||||
if (!IsAlignedAddress<std::uint64_t>(reinterpret_cast<std::uintptr_t>(address))) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
return WaitOnAddress(address, expected, timeout, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelSyncOnAddressWake(void* address, std::int32_t count) {
|
||||
const auto key = reinterpret_cast<std::uintptr_t>(address);
|
||||
if (!IsWordAddress(key) || count < 0) {
|
||||
if (!IsAlignedAddress<std::uint32_t>(key) || count < 0) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
extern "C" {
|
||||
int APS5_VABI sceKernelSyncOnAddressWait(std::uint32_t* address, std::uint32_t expected, const KernelUseconds* timeout, const char* name);
|
||||
int APS5_VABI sceKernelSyncOnAddressWait32(std::uint32_t* address, std::uint32_t expected, const KernelUseconds* timeout, const char* name);
|
||||
int APS5_VABI sceKernelSyncOnAddressWait64(std::uint64_t* address, std::uint64_t expected, const KernelUseconds* timeout, const char* name);
|
||||
int APS5_VABI sceKernelSyncOnAddressWake(void* address, std::int32_t count);
|
||||
}
|
||||
|
||||
|
|
@ -130,14 +132,87 @@ static void WaitsOnTheLowHalfOfAnOnceState() {
|
|||
waiter.join();
|
||||
}
|
||||
|
||||
static void SizedWaitsTimeOutWhileTheValueMatches() {
|
||||
std::uint32_t word = 5;
|
||||
std::uint64_t value = 0x500000005ULL;
|
||||
const KernelUseconds none = 0;
|
||||
Require(sceKernelSyncOnAddressWait32(&word, 4, &none, "differs") == SCE_OK);
|
||||
Require(sceKernelSyncOnAddressWait32(&word, 5, &none, "timeout") == SCE_KERNEL_ERROR_ETIMEDOUT);
|
||||
Require(sceKernelSyncOnAddressWait64(&value, 0x500000005ULL, &none, "timeout") == SCE_KERNEL_ERROR_ETIMEDOUT);
|
||||
|
||||
const KernelUseconds timeout = 20000;
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
Require(sceKernelSyncOnAddressWait32(&word, 5, &timeout, "timeout") == SCE_KERNEL_ERROR_ETIMEDOUT);
|
||||
Require(std::chrono::steady_clock::now() - start >= std::chrono::milliseconds(15));
|
||||
start = std::chrono::steady_clock::now();
|
||||
Require(sceKernelSyncOnAddressWait64(&value, 0x500000005ULL, &timeout, "timeout") == SCE_KERNEL_ERROR_ETIMEDOUT);
|
||||
Require(std::chrono::steady_clock::now() - start >= std::chrono::milliseconds(15));
|
||||
}
|
||||
|
||||
static void Wait64ComparesTheHighHalf() {
|
||||
std::uint64_t value = 0x100000001ULL;
|
||||
const KernelUseconds none = 0;
|
||||
Require(sceKernelSyncOnAddressWait64(&value, 0x1ULL, &none, "high") == SCE_OK);
|
||||
Require(sceKernelSyncOnAddressWait64(&value, 0x200000001ULL, &none, "high") == SCE_OK);
|
||||
Require(sceKernelSyncOnAddressWait64(&value, 0x100000001ULL, &none, "high") == SCE_KERNEL_ERROR_ETIMEDOUT);
|
||||
}
|
||||
|
||||
static void WakeReleasesWaitersOfEverySize() {
|
||||
std::uint64_t value = 1;
|
||||
auto* word = reinterpret_cast<std::uint32_t*>(&value);
|
||||
std::atomic<int> started{0};
|
||||
std::atomic<int> finished{0};
|
||||
std::vector<int> results(3, -1);
|
||||
|
||||
std::vector<std::thread> waiters;
|
||||
waiters.emplace_back([&] {
|
||||
++started;
|
||||
results[0] = sceKernelSyncOnAddressWait(word, 1, &FAILSAFE_TIMEOUT, "sized");
|
||||
++finished;
|
||||
});
|
||||
waiters.emplace_back([&] {
|
||||
++started;
|
||||
results[1] = sceKernelSyncOnAddressWait32(word, 1, &FAILSAFE_TIMEOUT, "sized");
|
||||
++finished;
|
||||
});
|
||||
waiters.emplace_back([&] {
|
||||
++started;
|
||||
results[2] = sceKernelSyncOnAddressWait64(&value, 1, &FAILSAFE_TIMEOUT, "sized");
|
||||
++finished;
|
||||
});
|
||||
AwaitCount(started, 3);
|
||||
std::this_thread::sleep_for(SETTLE);
|
||||
|
||||
Require(sceKernelSyncOnAddressWake(&value, 1) == SCE_OK);
|
||||
AwaitCount(finished, 1);
|
||||
std::this_thread::sleep_for(SETTLE);
|
||||
Require(finished.load() == 1);
|
||||
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
|
||||
while (finished.load() != 3) {
|
||||
Require(std::chrono::steady_clock::now() < deadline);
|
||||
Require(sceKernelSyncOnAddressWake(&value, INT_MAX) == SCE_OK);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
for (auto& waiter : waiters) waiter.join();
|
||||
for (const int result : results) Require(result == SCE_OK);
|
||||
}
|
||||
|
||||
static void RejectsInvalidArguments() {
|
||||
std::uint32_t words[2] = {1, 1};
|
||||
auto* misaligned = reinterpret_cast<std::uint32_t*>(reinterpret_cast<unsigned char*>(words) + 1);
|
||||
Require(Rejects([&] { sceKernelSyncOnAddressWait(nullptr, 1, nullptr, nullptr); }));
|
||||
Require(Rejects([&] { sceKernelSyncOnAddressWait(misaligned, 1, nullptr, nullptr); }));
|
||||
Require(Rejects([&] { sceKernelSyncOnAddressWait32(nullptr, 1, nullptr, nullptr); }));
|
||||
Require(Rejects([&] { sceKernelSyncOnAddressWait32(misaligned, 1, nullptr, nullptr); }));
|
||||
Require(Rejects([&] { sceKernelSyncOnAddressWake(nullptr, 1); }));
|
||||
Require(Rejects([&] { sceKernelSyncOnAddressWake(misaligned, 1); }));
|
||||
Require(Rejects([&] { sceKernelSyncOnAddressWake(words, -1); }));
|
||||
|
||||
std::uint64_t values[2] = {1, 1};
|
||||
auto* wordAligned = reinterpret_cast<std::uint64_t*>(reinterpret_cast<unsigned char*>(values) + sizeof(std::uint32_t));
|
||||
Require(Rejects([&] { sceKernelSyncOnAddressWait64(nullptr, 1, nullptr, nullptr); }));
|
||||
Require(Rejects([&] { sceKernelSyncOnAddressWait64(wordAligned, 1, nullptr, nullptr); }));
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
|
@ -146,5 +221,8 @@ int main() {
|
|||
WakeReleasesAWaiterWithoutAValueChange();
|
||||
WakeHonoursTheCountAndTheAddress();
|
||||
WaitsOnTheLowHalfOfAnOnceState();
|
||||
SizedWaitsTimeOutWhileTheValueMatches();
|
||||
Wait64ComparesTheHighHalf();
|
||||
WakeReleasesWaitersOfEverySize();
|
||||
RejectsInvalidArguments();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [sceAgcDcbPrimeUtcl2](../../core/libs/prx/libSceAgc/DcbState/src/Display.cpp) (libSceAgc) - assumed to write the packet `sceAgcAcbPrimeUtcl2` writes: PRIME_UTCL2 has the same 5 dwords on the graphics and compute rings. Not confirmed on a title. Both write a NOP of that size, as there is no TLB to prime
|
||||
- [sceKernelInternalMemoryGetModuleSegmentInfo](../../core/libs/prx/libkernel/Module/src/Module.cpp) (libkernel) - unknown signature
|
||||
- [sceKernelSyncOnAddressWait](../../core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp) (libkernel) - the only known caller passes a null timeout and a name string as the fourth argument; the timeout is assumed to point to microseconds like the other kernel waits, and the name is ignored
|
||||
- [sceKernelSyncOnAddressWait32 / sceKernelSyncOnAddressWait64](../../core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp) (libkernel) - libc's `__std_atomic_wait_direct_32/64` pass a microsecond timeout or null and ignore the result; the by-value comparand, return values and Wait64's 8-byte alignment are assumed
|
||||
- [sceKernelMapNamedFlexibleMemoryInternal](../../core/libs/prx/libkernel/DirectMemory/Export.cpp) (libkernel) - flag 0x8000 unknown; only the sceKernelMapNamedFlexibleMemory flags are accepted
|
||||
- [sceLibcInternalBacktraceForGame](../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - unknown signature
|
||||
- [sceLibcInternalHeapErrorReportForGame](../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - unknown signature
|
||||
|
|
|
|||
Loading…
Reference in New Issue