Skip to content

Close the lock file when lock() fails to get the lock - #58

Open
gaborcsardi wants to merge 2 commits into
r-lib:mainfrom
PredictiveEcology:fix/close-fd-on-failed-lock
Open

gaborcsardi wants to merge 2 commits into
r-lib:mainfrom
PredictiveEcology:fix/close-fd-on-failed-lock

Conversation

@gaborcsardi

Copy link
Copy Markdown
Member

filelock_lock() opens the lock file before it tries to lock it, but it only kept or closed the file descriptor when the lock succeeded. Every failed lock() call leaked one: a timeout = 0 call that finds the file locked, a timeout > 0 call that times out, a locking error, and on Unix an interrupt while waiting. A process that polls a busy lock accumulates descriptors until it hits the fd limit or select()'s FD_SETSIZE.

On Unix, close the descriptor on the no-lock return paths and before the error() calls in filelock_lock(). The wait in
filelock__interruptible() can longjmp out through error() or R_CheckUserInterrupt(), so it now runs under R_UnwindProtect(), which closes the descriptor and then continues the jump. The interrupt is still an interrupt. R_UnwindProtect() needs R 3.5.0, so Depends is now R (>= 3.5).

On Windows, close the handle on the timeout return path and before each filelock__error() call after CreateFileW(). The interrupt paths already closed it. The WAIT_FAILED paths now read GetLastError() before the cleanup calls, so the reported error code is not clobbered.

The new tests hold the lock from a child process and check that failed lock() calls do not leave the lock file open, for timeouts and for an interrupt. ps is added to Suggests for this.

Fixes #28.

Claude-Session: https://claude.ai/code/session_01CwcjqqK59FmTJscyi7xUqv

eliotmcintire and others added 2 commits September 28, 2026 21:15
filelock_lock() opens the lock file before it tries to lock it, but it
only kept or closed the file descriptor when the lock succeeded. Every
failed lock() call leaked one: a timeout = 0 call that finds the file
locked, a timeout > 0 call that times out, a locking error, and on
Unix an interrupt while waiting. A process that polls a busy lock
accumulates descriptors until it hits the fd limit or select()'s
FD_SETSIZE.

On Unix, close the descriptor on the no-lock return paths and before
the error() calls in filelock_lock(). The wait in
filelock__interruptible() can longjmp out through error() or
R_CheckUserInterrupt(), so it now runs under R_UnwindProtect(), which
closes the descriptor and then continues the jump. The interrupt is
still an interrupt. R_UnwindProtect() needs R 3.5.0, so Depends is now
R (>= 3.5).

On Windows, close the handle on the timeout return path and before
each filelock__error() call after CreateFileW(). The interrupt paths
already closed it. The WAIT_FAILED paths now read GetLastError()
before the cleanup calls, so the reported error code is not clobbered.

The new tests hold the lock from a child process and check that failed
lock() calls do not leave the lock file open, for timeouts and for an
interrupt. ps is added to Suggests for this.

Fixes r-lib#28.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CwcjqqK59FmTJscyi7xUqv
The Windows wait loop closes the handle and raises "Locking interrupted
for file ..." when an interrupt is pending, so the condition is a
simpleError there. Check that on Windows and the interrupt class on
Unix; the file-closed check applies to both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eliotmcintire added a commit to PredictiveEcology/filelock that referenced this pull request Oct 16, 2026
Merge fix/close-fd-on-failed-lock (r-lib#58) and take its
files whole, dropping the fork's earlier fix a2fe838: r-lib#58 closes the
lock file on the same failure paths and also on an error or interrupt
while waiting. The fork's main now differs from r-lib#58 only in Version
(1.0.3.9001), and will equal upstream once r-lib#58 is merged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oFSqD7YBJZdkFfStooVtz
eliotmcintire added a commit to PredictiveEcology/filelock that referenced this pull request Oct 16, 2026
main now carries r-lib#58 instead of a2fe838. The retry loop in
filelock_lock() (start over when the locked file is no longer the one
at `path`, or on ESTALE) now wraps r-lib#58's code, including its
R_UnwindProtect() around the wait, so a failed or interrupted attempt
still closes the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oFSqD7YBJZdkFfStooVtz

This branch has not been deployed

No deployments
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.

File not closed on timeout or SIGINT

2 participants