The core and the headless tools no longer use mutexes, condition variables,
spinlocks, std::atomic, std::thread or volatile flags. Threads hand off through
libretro-common's retro_atomic and rthreads plus a few small pieces on top of
them in Common/Thread: Thread, MpscQueue, EventCounter, ParkingLot waits and
ReaderGate. Every wait blocks until it's woken; nothing polls on a timer.
State that only one thread touches just loses its lock. The PSP filesystem,
sceIo and the savedata dialog now belong to the emulation thread: async reads
and writes run when they're queued and complete later in emulated time, and
the IO and savedata threads are gone. The friend finder only talks to the
adhoc server and hands what it gets to the emulation thread. GPU init always
runs on the thread that starts the game.
The Vulkan render and compile queues, pipeline and render pass caches, delete
list and SPIR-V cache, the GL render queue, the software renderer's jit caches,
the file loader caches, the web debugger, UPnP, input, savestates, logging and
the GE debugger all hand off through queues and atomics. The IR jit's spinning
profiler thread goes.
A CPU thread with nothing to run now sleeps until work is queued instead of
spinning. Headless with --debugger waits for a client while stopped, and ends
the run once every client that connected has left. Web server shutdown keeps
servicing the CPU queue until the server has finished.
The libretro core is the frontend, and the headless tools are the test and
debugging hosts. The Windows, UWP, Android, iOS, macOS and SDL apps, the UI
framework and imgui go, along with what only they used: the Win32 debugger
hooks (g_frameMutex, the shutdown lock), JNI thread attachment, the Android
text drawer, RAIntegration, Discord, recent files, game installs, tilt input,
the audio resamplers, the control mapper, memstick moving and the remote ISO
and upload server modes. The web server keeps the debugger. The MSVC, Gradle
and ndk-build projects go too.
Homebrew written for a PSP-2000+ under custom firmware can use the top
32MB of RAM directly without setting MEMSIZE, which leaves the user
partition at its normal size. NJEMU's slim builds do this (#8925).
Previously we didn't map that memory at all for PBPs; MEMSIZE=1 isn't a
workaround either, since it grows the partition and the heap and stacks
land where the program writes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reject sizes past the end of the state before allocating (FPL, PGF,
achievements, SAS grain, savedata list, the memory fast path), fail
instead of desyncing on a SAS voice count mismatch, and free what old
states' paths and shrinking pointer containers dropped.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Memory::IsValidAddress and friends tested the extended-RAM range with
(address & 0x3F000000), i.e. at 16MB granularity, so they accepted the whole 16MB
block containing the end of RAM. That's harmless at 32MB and 64MB, but the Sora no
Kiseki SC/3rd HD remasters run with 0x04C00000, so addresses from 0x0CC00000 to
0x0CFFFFFF read as valid, and MaxSizeAtAddress then underflowed to ~4GB there -
which defeats ClampValidSizeAt and IsValidRange entirely for that window. Mask
with 0x3FFFFFFF instead, in all five helpers and the copies in MemMapFunctions.cpp.
IsValidTextureAddress's extended-RAM branch repeated the first branch's whole mask
rather than just its alignment bits, so it was dead code and extended RAM was never
accepted as a texture source.
ComputeTextureHash checked IsValidAddress(addr + sizeInRAM), i.e. only the end
address, which can land in a different valid region than the start - a VRAM texture
with a large enough computed size ends exactly at the base of RAM and "passes"
while reading far past the 8MB VRAM view. Use IsValidRange.
TextureReplacer::ComputeHash's strided path had no range check at all, unlike the
contiguous path right above it. Also clamp the pack-supplied reduce-hash factor to
1.0 - it's a reduction, and the ini parser only rejects exactly 0.
ZipExtractFileToMemory read an uninitialized zip_stat when zip_stat_index failed
(it ignored the return value) and sized a host allocation directly from the zip's
declared uncompressed size. Reached just by opening an archive.
Memory::Reinit ignored Init()'s return value, and DoState fed it a memory size
taken straight from the savestate. A bogus size made the map fail to allocate and
left base null, after which DoMemoryVoid wrote RAM through it. Validate the size,
propagate the failure, and roll back to the previous size if reinit fails.
314 pspautotests pass, all unit tests pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCPmm7FoQUoqrbMdhfqhQ2
It stopped being about memory when CPU_Shutdown started holding it across the
whole teardown - it's what keeps kernel objects, the symbol map and the memory
map from being freed while another thread reads them. The old name invited the
reading that it locks memory *access*, which it has never done.
Memory::Reinit() now holds it across both halves rather than relying on
Memory::Shutdown()'s own acquire: between Shutdown() and Init() there is no
memory map at all, and a reader could slip into that gap.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
The scratchpad (PSP's repurposed-cache scratch RAM, 0x00010000+) was only
mirrored for user-mode access (cached 0x00010000, uncached 0x40010000) -
kernel-mode code sees it at 0x80010000/0xC0010000 (the kernel bit,
0x80000000, is independent of and combinable with the uncached bit,
0x40000000 - not "the uncached bit" as an earlier doc note in this branch
mistakenly called it). Missing entirely from MemMap.cpp's views[] table,
causing a real SIGSEGV the first time kernel-mode code (flash0:/reboot.bin)
touched it.
Adding the two missing views wasn't sufficient: the scratchpad range check
is duplicated eight times (IsValidAddress/IsValid2AlignedAddress/
IsValid4AlignedAddress/MaxSizeAtAddress in MemMap.h, and four more in
MemMapFunctions.cpp), and all eight used a mask (0xBFFFC000) that cleared
the uncached bit but kept the kernel bit, rejecting 0x80010000 as invalid
before ever reaching the now-mapped memory. Fixed all eight to 0x3FFFC000,
matching Memory::MEMVIEW32_MASK (which the JIT backends already used
correctly for the equivalent runtime check).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
Turns out these were needed after all. For some reason, on Windows and
Mac, <algorithm> gets auto-included by something else so I don't notice
when it's missing, and MSVC's include dependency tracker doesn't see it
either.
* Rename LogType to Log
* Explicitly use the Log:: enum when logging. Allows for autocomplete when editing.
* Mac/ARM64 buildfix
* Do the same with the hle result log macros
* Rename the log names to mixed case while at it.
* iOS buildfix
* Qt buildfix attempt, ARM32 buildfix