From 0b46b3080a61bba279974c2ad5db8c8d32cadeb1 Mon Sep 17 00:00:00 2001 From: ducloc080202 Date: Tue, 13 Oct 2026 15:39:31 +0700 Subject: [PATCH] test(libkernel): cover concurrent pread against seek and read --- core/libs/prx/libkernel/CMakeLists.txt | 9 +++ core/libs/tests/GuestPositionedRace.cpp | 77 +++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 core/libs/tests/GuestPositionedRace.cpp diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 72ce92342..561975af1 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -292,6 +292,15 @@ if(BUILD_TESTING) set_tests_properties(guest_aio PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_positioned_race_tests ${LIBS_INCLUDE_DIR}/tests/GuestPositionedRace.cpp) + target_include_directories(guest_positioned_race_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_positioned_race_tests PRIVATE libkernel libc) + configure_windows_unwind(guest_positioned_race_tests) + add_test(NAME guest_positioned_race COMMAND guest_positioned_race_tests) + if(WIN32) + set_tests_properties(guest_positioned_race PROPERTIES TIMEOUT 60 ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_once_tests ${LIBS_INCLUDE_DIR}/tests/GuestOnce.cpp) target_include_directories(guest_once_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_once_tests PRIVATE libkernel libc) diff --git a/core/libs/tests/GuestPositionedRace.cpp b/core/libs/tests/GuestPositionedRace.cpp new file mode 100644 index 000000000..6c936c56b --- /dev/null +++ b/core/libs/tests/GuestPositionedRace.cpp @@ -0,0 +1,77 @@ +#include "SceTypes.hpp" +#include "prx/libc/include/general/VabiMacros.hpp" +#include "prx/libkernel/File/include/FileFlags.hpp" +#include "prx/libkernel/KernelErrors.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +extern "C" { +int APS5_VABI sceKernelOpen(const char* path, int flags, std::uint16_t mode); +int APS5_VABI sceKernelClose(int d); +std::int64_t APS5_VABI sceKernelRead(int d, void* buf, std::size_t nbytes); +std::int64_t APS5_VABI sceKernelPread(int d, void* buf, std::size_t nbytes, std::int64_t offset); +std::int64_t APS5_VABI sceKernelLseek(int d, std::int64_t offset, int whence); +} +static void Check(bool value, int line) { + if (!value) { + std::fprintf(stderr, "Positioned race check failed at line %d\n", line); + std::abort(); + } +} +#define Require(value) Check((value), __LINE__) +int main() { + const auto root = std::filesystem::path("anyps5-positioned-race-" + + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); + Require(std::filesystem::create_directory(root)); + const auto file = root / "blocks.bin"; + constexpr std::int64_t kBlocks = 8; + constexpr std::size_t kBlockSize = 4096; + { + std::ofstream stream(file, std::ios::binary); + Require(static_cast(stream)); + for (std::int64_t block = 0; block < kBlocks; ++block) { + stream << std::string(kBlockSize, static_cast('A' + block)); + } + } + const int fd = sceKernelOpen(file.string().c_str(), SCE_KERNEL_O_RDONLY, 0); + Require(fd >= 0); + std::array expected{}; + expected.fill('A'); + std::atomic stop{false}; + std::atomic misread{false}; + std::array seeked{}; + seeked.fill('H'); + std::thread seeker([&] { + std::array sink{}; + while (!stop.load()) { + if (sceKernelLseek(fd, (kBlocks - 1) * kBlockSize, 0) != (kBlocks - 1) * kBlockSize) { + misread = true; + return; + } + if (sceKernelRead(fd, sink.data(), sink.size()) != static_cast(sink.size()) || + std::memcmp(sink.data(), seeked.data(), seeked.size()) != 0) { + misread = true; + return; + } + } + }); + for (int i = 0; i < 5000 && !misread.load(); ++i) { + std::array bytes{}; + const auto got = sceKernelPread(fd, bytes.data(), bytes.size(), 0); + if (got != static_cast(bytes.size()) || std::memcmp(bytes.data(), expected.data(), expected.size()) != 0) { + misread = true; + } + } + stop = true; + seeker.join(); + Require(!misread); + Require(sceKernelClose(fd) == 0); + Require(std::filesystem::remove_all(root) > 0); +}