Skip to content

feat(libkernel): implement fcntl record locks on Windows - #4480

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
milescoviello:carry/fcntl-record-locks-windows-4479
Oct 13, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
milescoviello:carry/fcntl-record-locks-windows-4479

Conversation

@milescoviello

@milescoviello milescoviello commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

What

Fixes #4479. Carries @screamm's fix commit from that issue (screamm:feat/libkernel-record-locks-windows, b7cc2856), keeping the commit and its authorship, as the issue invites. The branch sits on top of #3649's two commits, which this commit does not need (#3649 is only how the titles reach the call); it cherry-picks onto current main without changes and builds there.

On Windows, fcntl record locks (F_GETLK, F_SETLK, F_SETLKW and the old F_OGETLK/F_OSETLK/F_OSETLKW forms) throw fcntl: record locks are not implemented on Windows, which #3840 recorded as a limit. With #3649 applied, ProsperoRadio (PPSA99001) stops there on F_SETLK, and ProsperoTV (PPSA99003) does too once #4478's fix is applied. This commit applies FreeBSD 11.4's argument checks in FreeBSD's order (kern_fcntl, lf_advlockasync) and then follows lf_blocks, where only locks of different owners conflict: every lock belongs to the one guest process, so F_SETLK and F_SETLKW succeed without waiting and F_GETLK reports F_UNLCK. Nothing is held on the host, because Windows byte-range locks are mandatory and would fail the guest's own reads and writes through its other descriptors; the fcntl TechnicalDebt entry records that other host processes neither see nor are seen by these locks. A SEEK_END lock on a directory and locks on character devices still throw. Linux is unchanged.

guest_kernel_fcntl replaces the old Windows "throws" expectation with the round trip and adds RecordLocksBelongToTheProcess (overlap merge and split, two descriptors on one range, another thread's F_SETLK/F_SETLKW/F_GETLK, SEEK_CUR/SEEK_END, negative lengths, the 24-byte old struct, EINVAL, EBADF on read-only and write-only descriptors, relock after close); the EOVERFLOW, error-order, F_GETLK write-back and directory cases are Windows-only.

Tested

Checklist

🤖 Generated with Claude Code

ProsperoRadio (PPSA99001) stops at 33.7 s on Windows, with boykopovar#3649
applied so that it gets past poll, on the throw from boykopovar#3840:

    what():  fcntl: record locks are not implemented on Windows, command=12, fd=3

F_GETLK, F_SETLK, F_SETLKW and the F_OGETLK, F_OSETLK and F_OSETLKW
forms now follow FreeBSD 11.4's kern_fcntl and lf_advlockasync
(8c639945): an SEEK_CUR start is resolved against the descriptor offset
(EOVERFLOW past OFF_MAX), F_RDLCK needs read access and F_WRLCK write
access (EBADF), another type is EINVAL, and the range is checked against
the file size for SEEK_END (EINVAL for a negative start or an unknown
whence, EOVERFLOW past OFF_MAX). The guest is one process, and
lf_blocks never lets locks of one owner conflict, so whichever
descriptor or thread asks, F_SETLK and F_SETLKW succeed without waiting
and F_GETLK reports F_UNLCK, as on the console. Nothing is held on the
host: Windows byte-range locks are mandatory and would fail the guest's
own reads and writes through other descriptors. A lock from the end of
a directory throws.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 12, 2026
@milescoviello
milescoviello force-pushed the carry/fcntl-record-locks-windows-4479 branch from b8e52d2 to 43ffba8 Compare October 12, 2026 18:45
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 12, 2026
@oneandonlydean oneandonlydean self-assigned this Oct 12, 2026
@LeandroLP23

Copy link
Copy Markdown
Contributor

The PR body says the Windows path wasn't run for this carry, so I ran it. Windows 11 (26300), WinLibs GCC 15.2.0, Ninja, Release, BUILD_TESTING=ON, head 43ffba87e merged onto main 2f4cc8983.

Tests

ProsperoRadio (PPSA99001, catalog 01.000.010), 60 s runs with --windows:

Build Result
main + #3649 exit 0xc0000409 after 40 s. download0/prosperoradio.log ends at the sceKernelOpen of /download0/radio-browser.sqlite3 (125 lines)
main + #3649 + this PR exit 0xc0000409 after 36 s. The log goes past the database to [ProsperoRadio][http] non-blocking sockets: NOT set (143 lines)

The title's log redirect doesn't keep the what() line in my runs, so the table shows how far the log gets, not the stop message. That fits the description: the title gets past the record lock on the database and stops later in its HTTP code, where #4479 expects the POLLPRI stop (#4525).

ProsperoTV (PPSA99003) exits after 10 s both with and without this PR. It needs #4525 first, and #4525 doesn't merge together with #3649: both change Socket/src/Udp.cpp and tests/GuestPoll.cpp. So I couldn't build the combination for it.

AI-assisted: yes (Claude Code)

@Dextroid17 Dextroid17 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of #4480 (fcntl record locks on Windows), head 43ffba8

Verdict: approve, relying on LeandroLP23's Windows run for the host path. One non-blocking note.

Scope first: I ran Linux only. On Linux the #else branch uses the host's POSIX locks, so the Windows ProcessRecordLock is not compiled there. What I could check is the range/ordering/errno logic, by copying it into a stub harness.

What I ran (PR head merged onto upstream/main ee49621, clean env):

  • guest_kernel_fcntl_tests (Linux build): 10/10 runs exit 0.
  • Differential check of the range/ordering logic against my own transcription of FreeBSD fcntl() + lf_advlockasync() (freebsd-src 8c639945a): 2,352,000 cases (F_GETLK/F_SETLK, lock types incl. unknown, whence incl. invalid, start/length at int64 extremes, open mode, file size, file offset >= 0 only), built with UBSan. 0 mismatches. LockRangeError and the ProcessRecordLock checks are copied from the PR with the host calls stubbed. The reference is only as independent as my reading of FreeBSD, and it covers range and error order only, not F_SETLKW blocking, F_UNLCKSYS or the HostWrite write-back.
  • Mutation controls: dropping the length - 1 overflow term gives 6,912 mismatches; dropping the F_GETLK unknown-type check gives 241,704; dropping the start == 0 guard gives 0 (when length < 0, start + length < 0 already catches it, so the guard is redundant but harmless).
  • git merge-tree of this PR with #4555 (also edits DescriptorControl.cpp InitialEntry) is clean.

From LeandroLP23's comment, not run by me: Windows 554/554, guest_kernel_fcntl 3/3, and the failure message with main's DescriptorControl.cpp restored. In their ProsperoRadio runs the title exits 0xc0000409 both before and after this PR; with the PR the log gets past the database open to the HTTP code.

Not checked: the "SEEK_END on a directory still throws" case (the test asserts it under _WIN32 only).

Note: the start == 0 guard in LockRangeError is redundant when length < 0. Fine to leave.

@babyhuey

Copy link
Copy Markdown
Contributor

A title that needs this: ProsperoTV (PPSA99003) on Windows 11 (WinLibs GCC 15.2, RTX 3080). On main d1fd937 + #4525 (POLLPRI under WSAPoll) it gets into its HUI and then stops at:

what():  fcntl: record locks are not implemented on Windows, command=12, fd=4

That's F_SETLK on a regular file. With this PR stacked on top (fork test/verify-tvlock, c28bb5e; merges clean), it gets past the lock and renders its menu for the full 60 s with no throw: [TV] splash hidden=1, then [TV] frames=600 mean=23.50ms max=25.38ms ... draws=2 shapes=407. guest_kernel_fcntl passes 5/5 on that head, with no warnings in DescriptorControl.cpp or GuestKernelFcntl.cpp.

@oneandonlydean
oneandonlydean merged commit 37a0a36 into boykopovar:main Oct 13, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

libkernel: fcntl record locks throw on Windows (ProsperoRadio, ProsperoTV after #3649); fix branch ready

6 participants