Every import lookup walked the lease out from the containing range to find
the run of continuing registry ranges, which is linear in the run length.
That walk depends only on the lease snapshot, the containing range and the
import window block, so its first and last index are now memoized per
thread under a weak reference to the snapshot, the range index, the window
and the window's low end. APS5_NO_RUN_MEMO walks every time.
Everything that depends on other state runs on each call as before: the
largest size, the budget, the failed blocks, the remembered refusals and
the halving block search. importRunBounds returns the chosen indices, so
sameRange, runCovers and the whole-run lookup in importAllocation compare
against the lease without copying the run's range pointers.
importRunFailureShrinkTests records a failed 8 MiB block between two
lookups on an unchanged registry and requires the run to shrink to its
4 MiB blocks. importRunGrowthTests and importRunReplacedTests check that a
run grows and shrinks when its ranges are registered or replaced.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Two threads writing to the same stream each ran FileStream::SyncStatus
after their stdio call, outside the FILE lock, with plain writes to the
read and write counters and a read-modify-write of the flag word, so the
flags of one thread could be lost. Publish the counters with atomic
stores and the flags with a compare-exchange loop; the values stay the
same.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
FreeBSD 11 fopen opens the path with _open, and vn_open_vnode refuses a
directory only for FWRITE or O_TRUNC, so fopen of a directory in a read
mode succeeds; Linux hosts already open it. On Windows CreateFileW
without FILE_FLAG_BACKUP_SEMANTICS refused it with EACCES.
fopen now opens such a path as a libkernel directory descriptor, which
libkernel registers in libc when it loads, and fclose and freopen remove
it from libkernel's table. Reads on the stream fail with EISDIR and set
the error indicator, as on a Linux host and FreeBSD's tmpfs: the Windows
CRT fails them already but reports EINVAL. Writes fail with EBADF, and
fseek, ftell and ungetc work, as on any read-only stream.
Trimming every trailing separator turned the Windows drive root "C:\" into
the drive-relative "C:". Trim only while the parent is longer than its
root_path(), and cover "/", "C:\" and "C:/" in guest_filesystem.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
RenameDotError_nid_no_patch resolved the parent with the separator kept
("file/"). On Linux std::filesystem::exists("file/") is false for a regular
file, so unlink, rmdir and rename of "file/." answered ENOENT instead of
ENOTDIR. Trim the trailing separators first (a lone "/" stays) and cover
rename of "file/." and "file/.." in guest_filesystem.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
_Call_once, _Tss_create, _Tss_delete, _Tss_get, _Tss_set, _Thrd_equal,
_Thrd_current and _Thrd_exit call scePthreadOnce, scePthreadKeyCreate,
scePthreadKeyDelete, scePthreadGetspecific, scePthreadSetspecific,
scePthreadEqual, scePthreadSelf and scePthreadExit and return their
results unchanged, as the console's libSceLibcInternal does.
_Mtx_current_owns returns 1 there. libkernel registers the calls with
libc at load, next to the pthread_create registration.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RbLgECLQ2bpDM7oe2urGZK
- strtol, strtoul, strtoll, strtoull and strtouq return 0 for a base
outside 0 and 2 to 36, leave the end at the start and set EINVAL;
strtoimax, strtoumax, _Stoull, _Stoul and the wide forms do the same
without errno.
- A 0x prefix with no hexadecimal digit after it (base 0 or 16) is no
conversion: the end stays at the start of the string.
- wcstol, wcstoll and wcstoimax take one sign, convert the rest with
wcstoull and clamp to the signed range with ERANGE, so "- 1" is -1
and "+\t-1" overflows.
- atoi, atol and atoll truncate strtoull's result.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RbLgECLQ2bpDM7oe2urGZK
The title allocates, maps and releases about 48 direct-memory blocks per frame.
Each allocation created a pagefile-backed section and each release closed it.
A released section whose pages were never stored to (resident pages keep their
data in private memory and saved buffers, not in the section) is still blank,
so up to 256 such sections of at most 16 MiB, 256 MiB in total, are kept by
size and handed to the next allocation of that size instead of a new
CreateFileMappingW. APS5_NO_DIRECT_MEMORY_SECTION_POOL keeps the old behaviour.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The crash and abort reports printed the last 64 traced calls of the whole
process, so a thread that loops on a traced import (a mutex lock/unlock
pair, a profiler marker) pushes the calls of the faulting thread out of the
report. The ring already keeps the last 65536 calls with their thread.
CopyThreadCallTrace_nid_no_patch returns the most recent calls of one
thread from that ring, oldest first, and both reports (Windows crash and
abort, Linux abort) list up to 32 of them for the reporting thread after
the process-wide list.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On Windows, freopen reopened a stream through _wfreopen, which opens the file without FILE_SHARE_DELETE, so remove or rename of the file failed while the stream was open, unlike fopen streams and POSIX unlink/rename of an open file. freopen now closes the previous stream and opens the new file through the same CreateFileW path fopen uses, so the reopened stream shares delete access. Host standard streams and an empty filename keep the _wfreopen path. Reference: FreeBSD 11 unlink(2) and rename(2) succeed on a file that is open.
Animal Well (PPSA22520) imports _Thrd_detach (L7f7zYwBvZA) from libc.prx and nothing exports it, so the Windows loader stops at startup. Register libkernel's pthread_detach with libc like pthread_create and pthread_join, and return _Thrd_error (4) when it fails.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01GtXavMkgCTCdmRpweviHcQ
On Windows the UCRT does not switch an r+, w+ or a+ stream from writing
to reading without fflush or fseek: fread after fwrite returned bytes of
the write buffer and the next flush wrote them over the file, and ungetc
failed on an update stream that was not already reading. On Linux glibc
does not switch either for ungetc, which pushes the byte back over the
pending write, drops the write and can corrupt the heap on the next read,
or for an fread of at least the buffer size, which drops the write and
reads from the old offset.
fread, fgetc, getc_unlocked, fgets, ungetc, ungetwc and fscanf now flush
pending output and mark the stream as reading first, as FreeBSD 11.4's
__srefill and __ungetc do, through _IO_switch_to_get_mode on glibc.
Writes directly after reads still go through the UCRT on Windows; the
gap is recorded in the libc technical debt.
RenameDotError looked at the parent of a dot component with exists() and
is_directory(), so a stat failure other than a missing path, such as
permission denied, answered ENOENT or ENOTDIR. The parent is now read with
status(): a missing path is ENOENT (ENOTDIR when a component is a file), any
other error is mapped through FilesystemError, and only a successful status
decides between a directory (EINVAL) and a non-directory (ENOTDIR).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The Windows -include of HostStandardStreams.hpp pulled in stdio.h before
libstdc++ could set __USE_MINGW_ANSI_STDIO, so long double formatting
bound to UCRT. Define the MinGW flag and include cstdio for C++.
Host logging opens its own FILE on the saved descriptor. The guest FILE stays on the replaced descriptor, so guest printf, puts and fprintf still reach the pipe.
readdir and readdir_r returned d_fileno 0 for every entry on Windows,
because MinGW's _wreaddir reports no inode. FreeBSD 11.4's
_readdir_unlocked (lib/libc/gen/readdir.c) never returns an entry whose
d_ino is 0, and d_fileno is the number lstat reports as st_ino.
The Windows directory stream now reads the listing from the directory
handle with GetFileInformationByHandleEx(FileIdBothDirectoryInfo), so
each entry carries the file ID of the entry itself, symbolic links
included, without opening it. The ID is folded as stat and lstat fold
nFileIndexHigh:nFileIndexLow; the fold moves to WindowsDescriptor.hpp so
both libraries use it. rewinddir restarts the scan, telldir and seekdir
count entries as _wtelldir and _wseekdir did, and opendir fails with the
open error of the directory. A file system that lists no file ID gets a
nonzero number hashed from the name. Linux truncates the host inode to
32 bits as stat does instead of reporting 0 above 2^32.
guest_directory checks that every entry has a nonzero file number, that
the numbers of a file, a subdirectory and symbolic links to both equal
lstat's st_ino, that an entry created or removed while the directory is
open shows up after rewinddir, that a delete-pending entry no lstat can
open is still listed, and that seekdir returns the same number.