Skip to content

fix(libkernel): serialize Windows vectored file transfers - #5833

Merged
adriadam10 merged 2 commits into
boykopovar:mainfrom
Ondra97:codex/fix-windows-vector-io-lock
Oct 15, 2026
Merged

adriadam10 merged 2 commits into
boykopovar:mainfrom
Ondra97:codex/fix-windows-vector-io-lock

Conversation

@Ondra97

@Ondra97 Ondra97 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

What

On Windows, TransferIovecs releases the descriptor's shared position lock between iovec chunks. Concurrent writev calls through duplicated descriptors can interleave parts of one record; readv can split a record across readers. On main 2aad30682, the regression finds record label 1 mixed with label 256 at byte 64 of an actual file.

Hold the existing position lock across the whole vector operation. Extract the native transfer bodies so the scalar wrappers retain their locking and the vector loop can call them while already holding the lock. This also covers preadv/pwritev and preserves the shared file cursor. The locking reference is POSIX thread interactions with regular file operations; these are reference semantics, not console measurements.

Update the existing technical-debt entry to describe serialization across a descriptor's aliases and the remaining limit: independent opens and other processes do not share this lock. Socket and random-device paths keep their existing transfer mechanisms.

The overlapping concurrent preadv/pwritev phase in guest_file_vector runs only on Windows, where the shared lock guarantees whole records. Concurrent writev/readv and the existing sequential positioned-I/O checks continue to run on both platforms.

Tested

I validated head cbb6ddc885c07c8a3c6ff3542505e9c30ff6b51c, based on main f633042dfdf0d51f4af58fafbade3fa9d47fd169, using real guest file APIs and file contents.

Windows 11 Pro 10.0.26300, WinLibs MinGW-w64 GCC 15.2.0, Release, bundled dependencies. Hardware: AMD Ryzen 9 7900X, 32 GB RAM, NVIDIA GeForce RTX 4070 Ti SUPER.

  • Full default Windows runtime build passed.
  • Eight related CTest cases passed with no skips: guest_file_vector, guest_posix_scalar_io, guest_positioned_race, guest_kernel_fcntl, guest_filesystem, file_position, file_streams, and guest_shm_open.
  • guest_file_vector passed ten consecutive runs, including the overlapping positioned phase. Four threads write and read 1,024 distinct 64-byte records through duplicated descriptors, each split across 32 iovecs. Every record remains whole and appears exactly once; positioned transfers leave the shared cursor unchanged.

Ubuntu 24.04.5 LTS x86-64 in VirtualBox, GCC 13.3.0, Release, bundled dependencies. The VM has 8 vCPUs and 8 GB RAM on the same host.

  • Built libc, libkernel and the eight relevant test targets from the same pinned sources.
  • Independently reproduced the reported Linux failure at line 164 with the original test in five of five runs.
  • With the platform guard, all eight related tests passed with no skips, and guest_file_vector passed ten consecutive runs. The overlapping positioned phase is excluded on Linux.
Linux test portability Before this review fix After this review fix
guest_file_vector Failed 5/5 runs at line 164 Passed 10/10 runs

Conventions and whitespace checks passed. The original Windows defect was reproduced on main 2aad30682 with writev interleaved a record at byte 64: 1 versus 256. No speed improvement is claimed. I did not run a title, the full Linux build or the full hosted CI suite.

Integration with open PRs

The agreement with #5831 remains: #5833 lands first, then #5831 rebases onto it. Its append branch belongs in NativeTransferBuffer, and the gathered O_APPEND write must call that helper while the vector already holds the position lock. Calling the locking NativeTransfer wrapper there would lock the same mutex twice.

An exact-head merge preview on main d92a8dad390af1807cc91622b924252141816a5a also reproduces the conflict with #5835 e4036135e. In the Windows TransferIovecs path, preserve the archive-read routing before File::LockPositionedTransfer(d), then keep this PR's unlocked NativePositionedTransfer/NativeTransferBuffer calls under that lock. Dropping either side loses archive routing or whole-vector serialization.

This #5835 composition was inspected statically; no combined archive/native runtime build or test was run. The archive feature and vector serialization are independent, and their merge order is not yet agreed.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source; none added
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes, Codex

@ahmad3a4

Copy link
Copy Markdown
Contributor

Hi @Ondra97, the overlap comment pairs this with #5831 (atomic O_APPEND appends on Windows). The two don't overlap in purpose: this one keeps the aliases of one descriptor from interleaving in vectored transfers, and #5831 makes O_APPEND writes atomic across independent opens with WriteFile at the end-of-file offset.

Whichever lands second needs two adjustments:

I built main 84b01f9 with this PR and #5831 resolved that way. guest_append_writes, guest_file_vector (with your concurrent records) and guest_kernel_fcntl pass 5 of 5 runs.

I'm happy for this one to go first and will rebase #5831 onto it once it's merged. If #5831 lands first instead, the two points above are the whole resolution.

AI-assisted.

@Ondra97

Ondra97 commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for testing the combined changes. I'm happy for #5833 to land first and for you to rebase #5831 onto it. Agreed on both adjustments to use NativeTransferBuffer and avoid locking the position mutex twice.

AI-assisted: yes. — Codex

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 5835 core/libs/prx/libkernel/File/src/Stdio.cpp L1075

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 3891 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4156 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4396 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4397 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4422 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4423 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4424 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4520 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4521 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4522 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4523 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4566 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4599 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4718 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4740 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4749 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4760 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 4813 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 5021 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 5973 docs/dev/debt/libkernel.md
PR 6044 core/libs/tests/GuestFileVector.cpp
PR 6222 core/libs/prx/libkernel/File/src/Stdio.cpp
docs/dev/debt/libkernel.md
PR 6343 docs/dev/debt/libkernel.md
PR 6510 docs/dev/debt/libkernel.md
PR 6542 docs/dev/debt/libkernel.md

This was referenced Oct 15, 2026

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, the lock change in TransferIovecs looks right. One problem:

  • core/libs/tests/GuestFileVector.cpp:164: the new guest_file_vector aborts on Linux (File vector check failed at line 164, 5 of 5 runs). The concurrent preadv at offset 0 against pwritev at offset 0 reads torn records: on Linux a buffered read is not atomic against an overlapping write, and the Linux path does not use the Windows lock. The whole-record check on overlapping preadv/pwritev only holds with the Windows lock, so it must not run on Linux (guard that phase with _WIN32, or make the readers not overlap the writers), while the writev/readv phases can stay.

Please rerun guest_file_vector on Linux after the change.

@Ondra97
Ondra97 force-pushed the codex/fix-windows-vector-io-lock branch from 9ea6db1 to cbb6ddc Compare October 15, 2026 13:55
@Ondra97

Ondra97 commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @adriadam10, fixed in cbb6ddc. I guarded only the overlapping concurrent preadv/pwritev phase with _WIN32. The concurrent writev/readv phases and existing sequential positioned-I/O checks still run on both platforms.

I independently reproduced the Linux failure at line 164 in 5/5 runs before the change. After the fix:

  • Ubuntu 24.04.5 LTS, GCC 13.3: built libc, libkernel and the relevant test targets; all 8 related tests passed without skips, and guest_file_vector passed 10 consecutive runs.
  • Windows 11, GCC 15.2: the full Release build passed, all 8 related tests passed without skips, and guest_file_vector passed 10 consecutive runs, including the overlapping positioned phase.

Validated against main f633042. I updated the PR description with these results.

AI-assisted: yes. — Codex

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-checked against cbb6ddc: the _WIN32 guard on the overlapping preadv/pwritev phase fixes the Linux abort; guest_file_vector passes here on Linux with the writev/readv phases still running. TransferIovecs holds the position lock and only calls the unlocked NativeTransferBuffer/NativePositionedTransfer, the scalar wrappers keep their lock, and the technical-debt entry matches. Merging once the Windows build is green.

@adriadam10
adriadam10 merged commit f35c13f into boykopovar:main Oct 15, 2026
9 checks passed
@Ondra97
Ondra97 deleted the codex/fix-windows-vector-io-lock branch October 17, 2026 14:38
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.

3 participants