Skip to content

posix, fcntl, io: re-issue interrupted syscalls instead of reporting EINTR - #1255

Merged
youknowone merged 1 commit into
mainfrom
ec-wiring
Aug 16, 2026
Merged

posix, fcntl, io: re-issue interrupted syscalls instead of reporting EINTR#1255
youknowone merged 1 commit into
mainfrom
ec-wiring

Conversation

@youknowone

@youknowone youknowone commented Aug 15, 2026

Copy link
Copy Markdown
Owner

wrap_oserror(..., eintr_retry=True) runs the pending Python signal handlers and re-issues the call; the loop ends only when a handler raises. These sites reported the interruption directly.

interp_posix.pyopen, stat/fstat by descriptor, fchdir, fsync, fdatasync, waitpid, mkfifo, mknod, fstatvfs, chmod, lchmod, fchmod, chown, lchown, fchown, sched_yield, sched_get_priority_max, sched_get_priority_min, sched_rr_get_interval, sched_getscheduler, sched_setscheduler, sched_getparam, sched_setparam. posix.wait is posix.waitpid(-1, 0) and takes that call's loop.

chmod reads an ENOTSUP/EOPNOTSUPP refusal of follow_symlinks=False as the modifier being unavailable rather than as an OS error, and reads it ahead of the retry (interp_posix.py:1247-1251).

interp_fcntl.pyfcntl, flock and lockf take their descriptor through space.c_filedescriptor_w and retry through _raise_error_maybe. ioctl takes its descriptor the same way; it reports through _raise_error_always and does not retry.

interp_fileio.py:135-147 _open_fd — the same loop around the open in open_raw_file.

Verification (arm64 linux container, dynasm)

  • test_eintr: 25 tests, OK (skipped=7). Before this change the suite died on its first test.
  • test_fcntl: 8 errors → 3. The remaining three are all fcntl.fcntl(fd, cmd, bytes), the buffer form the module documents as unimplemented; they fail identically without this change.

Not from this change

test.test_io deadlocks on linux at PyBufferedRandomTest.test_writes_and_readsW_Lock::acquire on a _pyio threading.Lock, zero CPU, 8/8 runs. A control binary built with these three files reverted deadlocks 8/8 as well, and PYRE_NO_JIT=1 clears it (0/2), so it is a pre-existing JIT-only defect on main. It goes unnoticed because baseline.json records test.test_io as IMPORTERROR, and a module recorded non-PASS is never executed by the runner.

authored by Claude

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability of file, descriptor, scheduling, locking, and synchronization operations when interrupted by signals.
    • File-opening and POSIX operations now retry safely after temporary interruptions while preserving existing error behavior.
    • File-control operations now support file objects and other supported descriptor wrappers, not only integer descriptors.
    • Improved validation and error reporting for locking and control operations.

…EINTR

`wrap_oserror(..., eintr_retry=True)` runs the pending Python signal
handlers and re-issues the call; the loop ends only when a handler
raises.  These sites reported the interruption directly.

interp_posix.py: open, stat/fstat by descriptor, fchdir, fsync,
fdatasync, waitpid, mkfifo, mknod, fstatvfs, chmod, lchmod, fchmod,
chown, lchown, fchown, sched_yield, sched_get_priority_max,
sched_get_priority_min, sched_rr_get_interval, sched_getscheduler,
sched_setscheduler, sched_getparam, sched_setparam.  `posix.wait` is
`posix.waitpid(-1, 0)` and takes that call's loop.

chmod reads an ENOTSUP/EOPNOTSUPP refusal of `follow_symlinks=False` as
the modifier being unavailable rather than as an OS error, and reads it
ahead of the retry (interp_posix.py:1247-1251).

interp_fcntl.py: fcntl, flock and lockf take their descriptor through
`space.c_filedescriptor_w` and retry through `_raise_error_maybe`.
ioctl takes its descriptor the same way; it reports through
`_raise_error_always` and does not retry.

interp_fileio.py:135-147 `_open_fd`: the same loop around the open in
`open_raw_file`.

Assisted-by: Claude
@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0efe4186-5c74-4256-b80d-8c547460a744

📥 Commits

Reviewing files that changed from the base of the PR and between bc56455 and 963f3bf.

📒 Files selected for processing (3)
  • pyre/pyre-interpreter/src/builtins.rs
  • pyre/pyre-interpreter/src/module/fcntl/interp_fcntl.rs
  • pyre/pyre-interpreter/src/module/posix/interp_posix.rs

Walkthrough

The interpreter now retries selected OS-level EINTR failures across file opening, fcntl, locking, scheduling, waiting, synchronization, filesystem creation, permission, and ownership operations. Descriptor wrappers are accepted by updated fcntl functions.

Changes

System-call interruption handling

Layer / File(s) Summary
File opening retries
pyre/pyre-interpreter/src/builtins.rs, pyre/pyre-interpreter/src/module/posix/interp_posix.rs
File-opening calls retry OS-level EINTR errors and preserve filename-aware error conversion.
Descriptor and locking operations
pyre/pyre-interpreter/src/module/fcntl/interp_fcntl.rs
fcntl, flock, and lockf resolve descriptors with c_filedescriptor_w and retry selected interrupted operations. ioctl preserves interruption as an error.
Scheduling, waiting, and synchronization retries
pyre/pyre-interpreter/src/module/posix/interp_posix.rs
Scheduling, wait, synchronization, metadata, and node-creation calls retry interrupted system calls.
Permission and ownership updates
pyre/pyre-interpreter/src/module/posix/interp_posix.rs
chmod and chown variants retry interrupted updates while preserving existing unsupported and direct-error cases.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🟡 Moderate · up to 963f3

The change retries interrupted system calls, but sandbox-backed open paths still return EINTR as an error without retrying, leaving callers with inconsistent behavior and avoidable interruption failures. The PR is not merge-ready until those paths are updated or the exception is explicitly accepted.

Sequence Diagram(s)

sequenceDiagram
  participant PythonAPI
  participant InterpreterWrapper
  participant OperatingSystem
  PythonAPI->>InterpreterWrapper: Invoke POSIX or fcntl operation
  InterpreterWrapper->>OperatingSystem: Execute system call
  OperatingSystem-->>InterpreterWrapper: EINTR or operation result
  InterpreterWrapper->>OperatingSystem: Retry interrupted operation
  OperatingSystem-->>InterpreterWrapper: Final result or OS error
  InterpreterWrapper-->>PythonAPI: Return value or converted exception
Loading

Possibly related PRs

Poem

A rabbit watched the syscall hop,
Then caught EINTR before the stop.
It tried once more through every gate,
While locks and files resumed their state.
“No startled pause shall block the way!”
Said Bunny, nibbling code today.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: retrying interrupted POSIX, fcntl, and I/O system calls.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ec-wiring

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

🤖 Codex parity review

Static analysis of this diff vs the local RPython/PyPy sources (commit 963f3bf).
Updated: 2026-08-15T19:07:08.227Z

Files in the reviewed diff
pyre/pyre-interpreter/src/builtins.rs
pyre/pyre-interpreter/src/module/fcntl/interp_fcntl.rs
pyre/pyre-interpreter/src/module/posix/interp_posix.rs

1. Regressions to PyPy parity introduced by this patch

None.

2. Other mismatches introduced by this patch

  • pyre/pyre-interpreter/src/module/posix/interp_posix.rs:5683 ↔ pypy/module/posix/interp_posix.py:3062 — Pyre retries sched_rr_get_interval() after a handled EINTR; PyPy calls wrap_oserror(..., eintr_retry=True) outside a loop, so it returns None after the handler rather than reissuing the syscall.

  • pyre/pyre-interpreter/src/module/posix/interp_posix.rs:5719 ↔ pypy/module/posix/interp_posix.py:3074 — Pyre loops and retries sched_getscheduler() on handled EINTR; PyPy has no enclosing retry loop and falls through with None.

  • pyre/pyre-interpreter/src/module/posix/interp_posix.rs:5750 ↔ pypy/module/posix/interp_posix.py:3104 — Pyre loops and retries sched_getparam() on handled EINTR; PyPy does not retry.

  • pyre/pyre-interpreter/src/module/posix/interp_posix.rs:5792 ↔ pypy/module/posix/interp_posix.py:3086 — Pyre loops and retries sched_setscheduler() on handled EINTR; PyPy does not retry.

  • pyre/pyre-interpreter/src/module/posix/interp_posix.rs:5825 ↔ pypy/module/posix/interp_posix.py:3119 — Pyre loops and retries sched_setparam() on handled EINTR; PyPy does not retry.

3. Pre-existing mismatches (already present before this patch)

  • pyre/pyre-interpreter/src/module/fcntl/interp_fcntl.rs:24 ↔ pypy/module/fcntl/interp_fcntl.py:117fcntl.fcntl() rejects a bytes buffer argument and implements only the integer form; PyPy passes the buffer to fcntl_str() and returns the resulting bytes.

  • pyre/pyre-interpreter/src/module/fcntl/interp_fcntl.rs:75 ↔ pypy/module/fcntl/interp_fcntl.py:214fcntl.ioctl() accepts only two or three integer arguments. PyPy supports the fourth mutate_flag argument and immutable/mutable buffer forms.

  • pyre/pyre-interpreter/src/module/fcntl/interp_fcntl.rs:127 ↔ pypy/module/fcntl/interp_fcntl.py:142flock() checks only for fewer than two arguments and ignores extras; PyPy’s two-argument gateway rejects them.

  • pyre/pyre-interpreter/src/module/posix/interp_posix.rs:5510 ↔ pypy/module/posix/interp_posix.py:3018 — Pyre returns None from sched_yield(); PyPy returns the syscall’s integer result.

  • pyre/pyre-interpreter/src/module/posix/interp_posix.rs:5801 ↔ pypy/module/posix/interp_posix.py:3086 — Pyre returns None from successful sched_setscheduler(); PyPy returns its integer syscall result.

  • pyre/pyre-interpreter/src/module/posix/interp_posix.rs:5834 ↔ pypy/module/posix/interp_posix.py:3119 — Pyre returns None from successful sched_setparam(); PyPy returns its integer syscall result.

  • pyre/pyre-interpreter/src/module/posix/interp_posix.rs:7670 ↔ pypy/module/posix/interp_posix.py:2501 — Pyre always routes pathname chown through fchownat, including the unmodified AT_FDCWD/follow-symlinks case; PyPy selects os.chown for that case and os.lchown for lchown (pypy/module/posix/interp_posix.py:2515).

4. Structural adaptations

None.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 963f3bf60d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if unsafe { libc::sched_rr_get_interval(pid, &mut interval) } == -1 {
return Err(io_err(std::io::Error::last_os_error(), ""));
// interp_posix.py:3062-3068 `sched_rr_get_interval`: retry on EINTR.
loop {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Match PyPy's one-shot scheduler control flow

When sched_rr_get_interval is interrupted and the Python signal handler returns normally, pypy/module/posix/interp_posix.py:3065-3070 has only one try/except: wrap_oserror(..., eintr_retry=True) returns and the function falls through to None. This added loop instead reissues the syscall and returns a float; the same divergence occurs in the adjacent sched_getscheduler, sched_getparam, sched_setscheduler, and sched_setparam ports. Preserve PyPy's control-flow shape unless each site is recorded and supported as a CPython 3.14 spec deviation.

AGENTS.md reference: AGENTS.md:L249-L254

Useful? React with 👍 / 👎.

@youknowone
youknowone merged commit 5b0ce89 into main Aug 16, 2026
14 of 17 checks passed
@youknowone
youknowone deleted the ec-wiring branch August 16, 2026 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant