#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 #include #include #include #ifdef _WIN32 #include extern "C" _invalid_parameter_handler _set_thread_local_invalid_parameter_handler(_invalid_parameter_handler); #endif 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 sceKernelPread(int, void*, std::size_t, std::int64_t); std::int64_t APS5_VABI sceKernelPwrite(int, const void*, std::size_t, std::int64_t); std::int64_t APS5_VABI sceKernelLseek(int d, std::int64_t offset, int whence); int APS5_VABI sceKernelAioDeleteRequest(std::int32_t id, std::int32_t* ret); int APS5_VABI sceKernelAioInitializeImpl(void* param, std::int32_t size); void APS5_VABI sceKernelAioInitializeParam(void* param); int APS5_VABI sceKernelAioSetParam(KernelAioSchedulingParam* param, std::int32_t schedulingWindowSize, std::int32_t delayedCountLimit, std::uint32_t enableSplit, std::uint32_t splitSize, std::uint32_t splitChunkSize); int APS5_VABI sceKernelAioSubmitReadCommands(KernelAioRwRequest* req, std::int32_t size, std::int32_t prio, std::int32_t* id); int APS5_VABI sceKernelAioSubmitWriteCommands(KernelAioRwRequest* req, std::int32_t size, std::int32_t prio, std::int32_t* id); int APS5_VABI sceKernelAioPollRequest(std::int32_t id, std::int32_t* state); int APS5_VABI sceKernelAioWaitRequest(std::int32_t id, std::int32_t* state, std::uint32_t* usec); int APS5_VABI sceKernelAioSubmitReadCommandsMultiple(KernelAioRwRequest* req, std::int32_t size, std::int32_t prio, std::int32_t* id); int APS5_VABI sceKernelAioSubmitWriteCommandsMultiple(KernelAioRwRequest* req, std::int32_t size, std::int32_t prio, std::int32_t* id); int APS5_VABI sceKernelAioWaitRequests(std::int32_t* id, std::int32_t num, std::int32_t* state, std::uint32_t mode, std::uint32_t* usec); int APS5_VABI sceKernelAioPollRequests(std::int32_t* id, std::int32_t num, std::int32_t* state); int APS5_VABI sceKernelAioCancelRequest(std::int32_t id, std::int32_t* state); int APS5_VABI sceKernelAioCancelRequests(std::int32_t* id, std::int32_t num, std::int32_t* state); int APS5_VABI sceKernelAioDeleteRequests(std::int32_t* id, std::int32_t num, std::int32_t* ret); } static void Require(bool value, int line) { if (!value) { std::fprintf(stderr, "Aio check failed at line %d\n", line); std::abort(); } } #define Check(value) Require((value), __LINE__) #ifdef _WIN32 static int g_parameterReports = 0; static void CountParameterReport(const wchar_t*, const wchar_t*, const wchar_t*, unsigned int, std::uintptr_t) { ++g_parameterReports; } static void CheckBadPositionedDescriptors() { const auto file = std::filesystem::path("anyps5-aio-bad-descriptor-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); { std::ofstream stream(file, std::ios::binary); stream << "data"; Check(static_cast(stream)); } const int closed = sceKernelOpen(file.string().c_str(), SCE_KERNEL_O_RDWR, 0); Check(closed >= 0 && sceKernelClose(closed) == 0); const auto previous = _set_thread_local_invalid_parameter_handler(CountParameterReport); for (int descriptor : {-1, closed}) { for (std::size_t count : {std::size_t{0}, std::size_t{1}}) { char buffer = 'x'; Check(sceKernelPread(descriptor, &buffer, count, 0) == SCE_KERNEL_ERROR_EBADF); Check(sceKernelPwrite(descriptor, &buffer, count, 0) == SCE_KERNEL_ERROR_EBADF); for (bool write : {false, true}) { KernelAioResult result{-1, 0}; KernelAioRwRequest request{0, count, &buffer, &result, descriptor}; std::int32_t id = 0; Check((write ? sceKernelAioSubmitWriteCommands(&request, 1, 0, &id) : sceKernelAioSubmitReadCommands(&request, 1, 0, &id)) == 0); Check(result.state == 4 && result.return_value == SCE_KERNEL_ERROR_EBADF); std::int32_t removed = -1; Check(sceKernelAioDeleteRequest(id, &removed) == 0); } Check(buffer == 'x'); Check(g_parameterReports == 0); } } Check(::_get_osfhandle(closed) == -1); Check(g_parameterReports == 1); Check(_set_thread_local_invalid_parameter_handler(previous) == CountParameterReport); Check(std::filesystem::remove(file)); } #endif int main(int argc, char** argv) { #ifdef _WIN32 if (argc > 1 && std::strcmp(argv[1], "bad-descriptors") == 0) { CheckBadPositionedDescriptors(); return 0; } #endif const auto root = std::filesystem::path("anyps5-aio-test-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); Check(std::filesystem::create_directory(root)); const auto file = root / "data.bin"; const std::string seed = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; { std::ofstream stream(file, std::ios::binary); stream << seed; } static_assert(sizeof(KernelAioSchedulingParam) == 20); std::array schedulingParams{}; schedulingParams.fill({7, 7, 7, 7, 7}); Check(sceKernelAioSetParam(&schedulingParams[1], 64, 16, 1, 0x200000, 0x40000) == 0); Check(schedulingParams[1].schedulingWindowSize == 64 && schedulingParams[1].delayedCountLimit == 16); Check(schedulingParams[1].enableSplit == 1 && schedulingParams[1].splitSize == 0x200000 && schedulingParams[1].splitChunkSize == 0x40000); Check(schedulingParams[0].schedulingWindowSize == 7 && schedulingParams[0].splitChunkSize == 7); Check(schedulingParams[2].schedulingWindowSize == 7 && schedulingParams[2].splitChunkSize == 7); Check(sceKernelAioSetParam(&schedulingParams[2], 128, 128, 0, 0x1000000, 0x1000000) == 0); Check(schedulingParams[2].schedulingWindowSize == 128 && schedulingParams[2].delayedCountLimit == 128); Check(schedulingParams[2].enableSplit == 0 && schedulingParams[2].splitSize == 0x1000000 && schedulingParams[2].splitChunkSize == 0x1000000); const auto setParamThrows = [&](std::int32_t window, std::int32_t delayed, std::uint32_t split, std::uint32_t size, std::uint32_t chunk) { KernelAioSchedulingParam untouchedParam{7, 7, 7, 7, 7}; bool thrown = false; try { sceKernelAioSetParam(&untouchedParam, window, delayed, split, size, chunk); } catch (const std::exception&) { thrown = true; } return thrown && untouchedParam.schedulingWindowSize == 7 && untouchedParam.delayedCountLimit == 7 && untouchedParam.enableSplit == 7 && untouchedParam.splitSize == 7 && untouchedParam.splitChunkSize == 7; }; Check(setParamThrows(129, 32, 1, 0x100000, 0x100000)); Check(setParamThrows(-1, 32, 1, 0x100000, 0x100000)); Check(setParamThrows(32, 129, 1, 0x100000, 0x100000)); Check(setParamThrows(32, -1, 1, 0x100000, 0x100000)); Check(setParamThrows(32, 32, 2, 0x100000, 0x100000)); Check(setParamThrows(32, 32, 1, 0x1000001, 0x100000)); Check(setParamThrows(32, 32, 1, 0x100000, 0x1000001)); bool nullThrew = false; try { sceKernelAioSetParam(nullptr, 32, 32, 1, 0x100000, 0x100000); } catch (const std::invalid_argument&) { nullThrew = true; } Check(nullThrew); Check(sceKernelAioInitializeImpl(schedulingParams.data(), static_cast(sizeof(schedulingParams))) == 0); int initParam = 0; sceKernelAioInitializeParam(&initParam); const int fd = sceKernelOpen(file.string().c_str(), SCE_KERNEL_O_RDONLY, 0); Check(fd >= 0); std::array first{}; std::array second{}; KernelAioResult firstResult{-1, 0}; KernelAioResult secondResult{-1, 0}; KernelAioRwRequest requests[2] = { {0, first.size(), first.data(), &firstResult, fd}, {32, second.size(), second.data(), &secondResult, fd}, }; std::int32_t id = 0; Check(sceKernelAioSubmitReadCommands(requests, 2, 0, &id) == 0); Check(id > 0); std::int32_t state = 0; Check(sceKernelAioWaitRequest(id, &state, nullptr) == 0); Check(state == 3); Check(firstResult.state == 3 && firstResult.return_value == 16); Check(secondResult.state == 3 && secondResult.return_value == 16); Check(std::memcmp(first.data(), seed.data(), 16) == 0); Check(std::memcmp(second.data(), seed.data() + 32, 16) == 0); KernelAioResult invalidReadResult{-1, 0}; KernelAioRwRequest invalidReadRequest{-1, first.size(), first.data(), &invalidReadResult, fd}; std::int32_t invalidReadId = 0; Check(sceKernelAioSubmitReadCommands(&invalidReadRequest, 1, 0, &invalidReadId) == 0); std::int32_t invalidReadState = 0; Check(sceKernelAioWaitRequest(invalidReadId, &invalidReadState, nullptr) == 0); Check(invalidReadState == 4); Check(invalidReadResult.state == 4 && invalidReadResult.return_value == SCE_KERNEL_ERROR_EINVAL); std::array aggregateReadBuffer{}; KernelAioResult aggregateReadSuccessResult{-1, 0}; KernelAioResult aggregateReadErrorResult{-1, 0}; KernelAioRwRequest aggregateReads[2] = { {4, aggregateReadBuffer.size(), aggregateReadBuffer.data(), &aggregateReadSuccessResult, fd}, {-1, aggregateReadBuffer.size(), aggregateReadBuffer.data(), &aggregateReadErrorResult, fd}, }; std::int32_t aggregateReadId = 0; Check(sceKernelAioSubmitReadCommands(aggregateReads, 2, 0, &aggregateReadId) == 0); std::int32_t aggregateReadState = 0; Check(sceKernelAioWaitRequest(aggregateReadId, &aggregateReadState, nullptr) == 0); Check(aggregateReadState == 4); Check(aggregateReadSuccessResult.state == 3 && aggregateReadSuccessResult.return_value == 4); Check(aggregateReadErrorResult.state == 4 && aggregateReadErrorResult.return_value == SCE_KERNEL_ERROR_EINVAL); Check(std::memcmp(aggregateReadBuffer.data(), seed.data() + 4, aggregateReadBuffer.size()) == 0); std::int32_t polled = 0; Check(sceKernelAioPollRequest(id, &polled) == 0); Check(polled == 3); std::int32_t deleted = -1; Check(sceKernelAioDeleteRequest(id, &deleted) == 0); Check(deleted == 0); Check(sceKernelClose(fd) == 0); const int writeFd = sceKernelOpen(file.string().c_str(), SCE_KERNEL_O_RDWR, 0); Check(writeFd >= 0); std::array written = {'H', 'E', 'L', 'L', 'O'}; KernelAioResult writeResult{-1, 0}; KernelAioRwRequest writeRequest{0, written.size(), written.data(), &writeResult, writeFd}; std::int32_t writeId = 0; Check(sceKernelAioSubmitWriteCommands(&writeRequest, 1, 0, &writeId) == 0); std::int32_t writeState = 0; Check(sceKernelAioWaitRequest(writeId, &writeState, nullptr) == 0); Check(writeState == 3 && writeResult.state == 3 && writeResult.return_value == 5); KernelAioResult invalidWriteResult{-1, 0}; KernelAioRwRequest invalidWriteRequest{-1, written.size(), written.data(), &invalidWriteResult, writeFd}; std::int32_t invalidWriteId = 0; Check(sceKernelAioSubmitWriteCommands(&invalidWriteRequest, 1, 0, &invalidWriteId) == 0); std::int32_t invalidWriteState = 0; Check(sceKernelAioWaitRequest(invalidWriteId, &invalidWriteState, nullptr) == 0); Check(invalidWriteState == 4); Check(invalidWriteResult.state == 4 && invalidWriteResult.return_value == SCE_KERNEL_ERROR_EINVAL); std::array aggregateWriteBuffer = {'G', 'O', 'O', 'D'}; KernelAioResult aggregateWriteSuccessResult{-1, 0}; KernelAioResult aggregateWriteErrorResult{-1, 0}; KernelAioRwRequest aggregateWrites[2] = { {8, aggregateWriteBuffer.size(), aggregateWriteBuffer.data(), &aggregateWriteSuccessResult, writeFd}, {-1, aggregateWriteBuffer.size(), aggregateWriteBuffer.data(), &aggregateWriteErrorResult, writeFd}, }; std::int32_t aggregateWriteId = 0; Check(sceKernelAioSubmitWriteCommands(aggregateWrites, 2, 0, &aggregateWriteId) == 0); std::int32_t aggregateWriteState = 0; Check(sceKernelAioWaitRequest(aggregateWriteId, &aggregateWriteState, nullptr) == 0); Check(aggregateWriteState == 4); Check(aggregateWriteSuccessResult.state == 3 && aggregateWriteSuccessResult.return_value == 4); Check(aggregateWriteErrorResult.state == 4 && aggregateWriteErrorResult.return_value == SCE_KERNEL_ERROR_EINVAL); Check(sceKernelClose(writeFd) == 0); { std::ifstream stream(file, std::ios::binary); std::string contents; std::getline(stream, contents); Check(contents.rfind("HELLO", 0) == 0); } Check(std::filesystem::file_size(file) == seed.size()); { std::ifstream stream(file, std::ios::binary); std::string contents{std::istreambuf_iterator(stream), std::istreambuf_iterator()}; Check(contents.size() == seed.size()); Check(contents.substr(8, aggregateWriteBuffer.size()) == "GOOD"); Check(contents.substr(12, 4) == seed.substr(12, 4)); } Check(sceKernelAioSubmitReadCommands(nullptr, 1, 0, &id) == SCE_KERNEL_ERROR_EFAULT); Check(sceKernelAioSubmitReadCommands(requests, 0, 0, &id) == SCE_KERNEL_ERROR_EINVAL); Check(sceKernelAioSubmitReadCommands(requests, 1, 0, nullptr) == SCE_KERNEL_ERROR_EFAULT); Check(sceKernelAioWaitRequest(9999, &state, nullptr) == SCE_KERNEL_ERROR_EINVAL); Check(sceKernelAioWaitRequest(id, nullptr, nullptr) == SCE_KERNEL_ERROR_EFAULT); Check(sceKernelAioPollRequest(id, nullptr) == SCE_KERNEL_ERROR_EFAULT); Check(sceKernelAioPollRequest(0, &polled) == SCE_KERNEL_ERROR_EINVAL); Check(sceKernelAioDeleteRequest(id, nullptr) == SCE_KERNEL_ERROR_EFAULT); const int batchFd = sceKernelOpen(file.string().c_str(), SCE_KERNEL_O_RDWR, 0); Check(batchFd >= 0); Check(sceKernelLseek(batchFd, 2, 0) == 2); std::array batchReadBuffer{}; KernelAioResult batchReadSuccessResult{-1, 0}; KernelAioResult batchReadErrorResult{-1, 0}; KernelAioRwRequest batchReads[2] = { {0, batchReadBuffer.size(), batchReadBuffer.data(), &batchReadSuccessResult, batchFd}, {-1, batchReadBuffer.size(), batchReadBuffer.data(), &batchReadErrorResult, batchFd}, }; std::int32_t batchReadIds[2] = {0, 0}; Check(sceKernelAioSubmitReadCommandsMultiple(batchReads, 2, 0, batchReadIds) == 0); Check(batchReadIds[0] > 0 && batchReadIds[1] > 0 && batchReadIds[0] != batchReadIds[1]); Check(batchReadSuccessResult.state == 3 && batchReadSuccessResult.return_value == 4); Check(batchReadErrorResult.state == 4 && batchReadErrorResult.return_value == SCE_KERNEL_ERROR_EINVAL); std::int32_t batchReadStates[2] = {0, 0}; Check(sceKernelAioWaitRequests(batchReadIds, 2, batchReadStates, 1, nullptr) == 0); Check(batchReadStates[0] == 3 && batchReadStates[1] == 4); std::array batchWriteData = {'A', 'B', 'C', 'D'}; KernelAioResult batchWriteSuccessResult{-1, 0}; KernelAioResult batchWriteErrorResult{-1, 0}; KernelAioRwRequest batchWrites[2] = { {12, batchWriteData.size(), batchWriteData.data(), &batchWriteSuccessResult, batchFd}, {-1, batchWriteData.size(), batchWriteData.data(), &batchWriteErrorResult, batchFd}, }; std::int32_t batchWriteIds[2] = {0, 0}; Check(sceKernelAioSubmitWriteCommandsMultiple(batchWrites, 2, 0, batchWriteIds) == 0); Check(batchWriteIds[0] > 0 && batchWriteIds[1] > 0 && batchWriteIds[0] != batchWriteIds[1]); Check(batchWriteSuccessResult.state == 3 && batchWriteSuccessResult.return_value == 4); Check(batchWriteErrorResult.state == 4 && batchWriteErrorResult.return_value == SCE_KERNEL_ERROR_EINVAL); std::int32_t batchWriteStates[2] = {0, 0}; Check(sceKernelAioWaitRequests(batchWriteIds, 2, batchWriteStates, 1, nullptr) == 0); Check(batchWriteStates[0] == 3 && batchWriteStates[1] == 4); std::array patch = {'W', 'X', 'Y', 'Z'}; KernelAioResult patchResult{-1, 0}; KernelAioResult badResult{-1, 0}; KernelAioRwRequest writeBatch[2] = { {8, patch.size(), patch.data(), &patchResult, batchFd}, {0, patch.size(), patch.data(), &badResult, -1}, }; std::int32_t writeIds[2] = {0, 0}; Check(sceKernelAioSubmitWriteCommandsMultiple(writeBatch, 2, 0, writeIds) == 0); Check(sceKernelLseek(batchFd, 0, 1) == 2); Check(writeIds[0] > 0 && writeIds[1] > 0 && writeIds[0] != writeIds[1]); Check(patchResult.state == 3 && patchResult.return_value == 4); Check(badResult.state == 4 && badResult.return_value == SCE_KERNEL_ERROR_EBADF); std::int32_t writeStates[2] = {0, 0}; Check(sceKernelAioWaitRequests(writeIds, 2, writeStates, 1, nullptr) == 0); Check(writeStates[0] == 3 && writeStates[1] == 4); std::int32_t polledStates[2] = {0, 0}; Check(sceKernelAioPollRequests(writeIds, 2, polledStates) == 0); Check(polledStates[0] == 3 && polledStates[1] == 4); std::array readBack{}; std::array tail{}; KernelAioResult readBackResult{-1, 0}; KernelAioResult tailResult{-1, 0}; KernelAioRwRequest readBatch[2] = { {8, readBack.size(), readBack.data(), &readBackResult, batchFd}, {60, tail.size(), tail.data(), &tailResult, batchFd}, }; std::int32_t readIds[2] = {0, 0}; Check(sceKernelAioSubmitReadCommandsMultiple(readBatch, 2, 0, readIds) == 0); Check(readIds[0] > 0 && readIds[1] > 0 && readIds[0] != readIds[1]); Check(readBackResult.state == 3 && readBackResult.return_value == 4); Check(std::memcmp(readBack.data(), "WXYZ", 4) == 0); Check(tailResult.state == 3 && tailResult.return_value == 4); Check(std::memcmp(tail.data(), seed.data() + 60, 4) == 0); std::int32_t readStates[2] = {0, 0}; std::uint32_t timeout = 1000; Check(sceKernelAioWaitRequests(readIds, 2, readStates, 2, &timeout) == 0); Check(readStates[0] == 3 && readStates[1] == 3); std::int32_t cancelled = 0; Check(sceKernelAioCancelRequest(readIds[0], &cancelled) == 0); Check(cancelled == 4); Check(sceKernelAioPollRequest(readIds[0], &polled) == 0); Check(polled == 4); Check(sceKernelAioCancelRequest(0, &cancelled) == 0); Check(cancelled == 2); std::int32_t cancelIds[2] = {0, readIds[1]}; std::int32_t cancelStates[2] = {0, 0}; Check(sceKernelAioCancelRequests(cancelIds, 2, cancelStates) == 0); Check(cancelStates[0] == 2 && cancelStates[1] == 4); Check(sceKernelAioPollRequest(readIds[1], &polled) == 0); Check(polled == 4); Check(sceKernelAioPollRequests(readIds, 2, polledStates) == 0); Check(polledStates[0] == 4 && polledStates[1] == 4); std::int32_t deletedRets[2] = {-1, -1}; Check(sceKernelAioDeleteRequests(writeIds, 2, deletedRets) == 0); Check(deletedRets[0] == 0 && deletedRets[1] == 0); Check(sceKernelAioPollRequest(writeIds[0], &polled) == 0); Check(polled == 4); Check(sceKernelAioDeleteRequests(writeIds, 0, deletedRets) == 0); Check(sceKernelClose(batchFd) == 0); std::int32_t badIds[2] = {readIds[0], 9999}; std::int32_t untouched[2] = {7, 7}; Check(sceKernelAioSubmitReadCommandsMultiple(nullptr, 1, 0, readIds) == SCE_KERNEL_ERROR_EFAULT); Check(sceKernelAioSubmitReadCommandsMultiple(readBatch, 1, 0, nullptr) == SCE_KERNEL_ERROR_EFAULT); Check(sceKernelAioSubmitWriteCommandsMultiple(writeBatch, 0, 0, writeIds) == SCE_KERNEL_ERROR_EINVAL); KernelAioRwRequest noResult{0, 1, readBack.data(), nullptr, batchFd}; Check(sceKernelAioSubmitWriteCommandsMultiple(&noResult, 1, 0, writeIds) == SCE_KERNEL_ERROR_EFAULT); Check(sceKernelAioWaitRequests(readIds, 2, nullptr, 1, nullptr) == SCE_KERNEL_ERROR_EFAULT); Check(sceKernelAioWaitRequests(nullptr, 2, readStates, 1, nullptr) == SCE_KERNEL_ERROR_EFAULT); Check(sceKernelAioWaitRequests(readIds, -1, readStates, 1, nullptr) == SCE_KERNEL_ERROR_EINVAL); Check(sceKernelAioWaitRequests(badIds, 2, untouched, 1, nullptr) == SCE_KERNEL_ERROR_EINVAL); Check(untouched[0] == 7 && untouched[1] == 7); Check(sceKernelAioPollRequests(readIds, 2, nullptr) == SCE_KERNEL_ERROR_EFAULT); Check(sceKernelAioPollRequests(nullptr, 2, readStates) == SCE_KERNEL_ERROR_EFAULT); Check(sceKernelAioPollRequests(readIds, -1, readStates) == SCE_KERNEL_ERROR_EINVAL); Check(sceKernelAioPollRequests(badIds, 2, untouched) == SCE_KERNEL_ERROR_EINVAL); Check(untouched[0] == 7 && untouched[1] == 7); Check(sceKernelAioPollRequests(readIds, 0, untouched) == 0); Check(untouched[0] == 7 && untouched[1] == 7); bool threw = false; try { sceKernelAioWaitRequests(readIds, 2, readStates, 3, nullptr); } catch (const std::runtime_error&) { threw = true; } Check(threw); threw = false; std::array manyIds{}; std::array manyStates{}; manyIds.fill(readIds[0]); try { sceKernelAioDeleteRequests(manyIds.data(), 129, manyStates.data()); } catch (const std::runtime_error&) { threw = true; } Check(threw); threw = false; try { sceKernelAioPollRequests(manyIds.data(), 129, manyStates.data()); } catch (const std::runtime_error&) { threw = true; } Check(threw); Check(sceKernelAioCancelRequest(9999, &cancelled) == SCE_KERNEL_ERROR_EINVAL); Check(sceKernelAioCancelRequest(readIds[0], nullptr) == SCE_KERNEL_ERROR_EFAULT); Check(sceKernelAioCancelRequests(badIds, 2, untouched) == SCE_KERNEL_ERROR_EINVAL); Check(untouched[0] == 7 && untouched[1] == 7); Check(sceKernelAioCancelRequests(cancelIds, 2, nullptr) == SCE_KERNEL_ERROR_EFAULT); Check(sceKernelAioDeleteRequests(badIds, 2, untouched) == SCE_KERNEL_ERROR_EINVAL); Check(untouched[0] == 7 && untouched[1] == 7); Check(sceKernelAioDeleteRequests(writeIds, 2, nullptr) == SCE_KERNEL_ERROR_EFAULT); const auto shared = root / "shared.bin"; std::string sharedContents; for (char letter : {'A', 'B', 'C', 'D'}) sharedContents.append(1024, letter); { std::ofstream stream(shared, std::ios::binary); stream << sharedContents; } const int sharedFd = sceKernelOpen(shared.string().c_str(), SCE_KERNEL_O_RDONLY, 0); Check(sharedFd >= 0); std::atomic misread{false}; std::vector readers; for (std::int64_t offset = 0; offset < 4096; offset += 1024) { readers.emplace_back([&, offset] { for (int i = 0; i < 2000 && !misread; ++i) { std::array bytes{}; KernelAioResult result{-1, 0}; KernelAioRwRequest request{offset, bytes.size(), bytes.data(), &result, sharedFd}; std::int32_t requestId = 0; if (sceKernelAioSubmitReadCommands(&request, 1, 0, &requestId) != 0 || result.state != 3 || result.return_value != 16 || std::memcmp(bytes.data(), sharedContents.data() + offset, 16) != 0) { misread = true; } } }); } for (auto& reader : readers) reader.join(); Check(!misread); Check(sceKernelClose(sharedFd) == 0); Check(std::filesystem::remove_all(root) > 0); }