Skip to content

Tracking Issue for File lock API #130994

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
4 of 6 tasks
cberner opened this issue Sep 28, 2024 · 64 comments · Fixed by #136794 · May be fixed by #142125
Open
4 of 6 tasks

Tracking Issue for File lock API #130994

cberner opened this issue Sep 28, 2024 · 64 comments · Fixed by #136794 · May be fixed by #142125
Labels
C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. to-announce Announce this issue on triage meeting

Comments

@cberner
Copy link
Contributor

cberner commented Sep 28, 2024

Feature gate: #![feature(file_lock)]

This is a tracking issue for rust-lang/libs-team#412

This feature exposes advisory file locks on File. They allow a file handle to acquire an exclusive or shared file lock, which blocks other file handles to the same file from acquiring a conflicting lock. Some semantics are platform dependent, and these are documented in the API documentation.

Public API

impl File {
    fn lock(&self) -> io::Result<()>;
    fn lock_shared(&self) -> io::Result<()>;
    fn try_lock(&self) -> io::Result<bool>;
    fn try_lock_shared(&self) -> io::Result<bool>;
    fn unlock(&self) -> io::Result<()>;
}

Steps / History

Unresolved Questions

  • None yet.

Footnotes

  1. https://std-dev-guide.rust-lang.org/feature-lifecycle/stabilization.html

@cberner cberner added C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Sep 28, 2024
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Nov 11, 2024
Implement file_lock feature

This adds lock(), lock_shared(), try_lock(), try_lock_shared(), and unlock() to File gated behind the file_lock feature flag

This is the initial implementation of rust-lang#130994 for Unix and Windows platforms. I will follow it up with an implementation for WASI preview 2
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Nov 11, 2024
Rollup merge of rust-lang#130999 - cberner:flock_pr, r=joboet

Implement file_lock feature

This adds lock(), lock_shared(), try_lock(), try_lock_shared(), and unlock() to File gated behind the file_lock feature flag

This is the initial implementation of rust-lang#130994 for Unix and Windows platforms. I will follow it up with an implementation for WASI preview 2
mati865 pushed a commit to mati865/rust that referenced this issue Nov 12, 2024
Implement file_lock feature

This adds lock(), lock_shared(), try_lock(), try_lock_shared(), and unlock() to File gated behind the file_lock feature flag

This is the initial implementation of rust-lang#130994 for Unix and Windows platforms. I will follow it up with an implementation for WASI preview 2
@workingjubilee
Copy link
Member

apparently not supported on all tier 2 OS: #132921

@eric-seppanen
Copy link

eric-seppanen commented Nov 27, 2024

Note that this triggers the unstable_name_collisions lint for code using the fs2/fs3/fs4 crates, all of which have a FileExt trait with methods called lock_shared, try_lock_shared, and unlock.

This lint fires on the 1.84 beta release, so there might be a number of people who discover this once 1.84 stable goes out.

@NilsIrl
Copy link

NilsIrl commented Dec 20, 2024

It would also be useful to atomically create and lock a file. This is possible on MacOS using the O_SHLOCK and O_EXLOCK flags on open and on Linux using O_TMPFILE.

@joshtriplett
Copy link
Member

While there are more features we may want to add to this in the future, the current state of this seems useful, and works. Stabilizing it would let people who encounter the warnings about a future conflict switch to the new API.

Shall we stabilize the current File locking APIs?

@rfcbot merge

@rfcbot
Copy link
Collaborator

rfcbot commented Jan 26, 2025

Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members:

Concerns:

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Jan 26, 2025
@m-ou-se
Copy link
Member

m-ou-se commented Jan 30, 2025

I'd like to see some documentation on the lock methods that make it clear that you don't need to manually unlock / that the lock is automatically unlocked when the File is dropped. Right now, that's only documented on the unlock method.

Other than that, the documentation could be made a lot less confusing by adding 'by another process' and 'by the same process' in a few places. Right now, the try methods say "Returns false if the file is locked.", but then go on to say it might deadlock if it's already locked. I assume the former should be "locked by another process" and the latter should be "locked by this process".

@m-ou-se
Copy link
Member

m-ou-se commented Jan 30, 2025

The unlock method should document whether it's okay to call it if no locks are held.

If calling unlock() is only acceptable when a lock is actually held, this should probably be a Guard style of API. If it's always okay to call unlock(), the current design makes sense to me.

@m-ou-se
Copy link
Member

m-ou-se commented Jan 30, 2025

I'm checking my box with the assumption that these are just small docs changes that we'll do during/before stabilization.

@rfcbot rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels Jan 30, 2025
@rfcbot
Copy link
Collaborator

rfcbot commented Jan 30, 2025

🔔 This is now entering its final comment period, as per the review above. 🔔

@joshtriplett
Copy link
Member

I've submitted #136288 which should address all the documentation requests.

@m-ou-se wrote:

I'd like to see some documentation on the lock methods that make it clear that you don't need to manually unlock / that the lock is automatically unlocked when the File is dropped. Right now, that's only documented on the unlock method.

Done.

Other than that, the documentation could be made a lot less confusing by adding 'by another process' and 'by the same process' in a few places. Right now, the try methods say "Returns false if the file is locked.", but then go on to say it might deadlock if it's already locked. I assume the former should be "locked by another process" and the latter should be "locked by this process".

"process" isn't the granularity here, but I've added clear distinctions about locks acquired via the same handle/descriptor (may deadlock) vs locks acquired via a different handle/descriptor (will block the blocking methods or make the try_ methods return Ok(false)). I've also clarified the return value documentation to avoid that ambiguity.

The unlock method should document whether it's okay to call it if no locks are held.

It's always safe to call (in the Rust sense). I've documented that it'll either return an error or return without doing anything. It'll never explode.

If calling unlock() is only acceptable when a lock is actually held, this should probably be a Guard style of API. If it's always okay to call unlock(), the current design makes sense to me.

It's always OK (unlike a mutex or similar). It'll never explode.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Jan 30, 2025
…ith-some-locks--would-you-could-you-in-some-docs, r=m-ou-se

Improve documentation for file locking

Add notes to each method stating that locks get dropped on close.

Clarify the return values of the try methods: they're only defined if
the lock is held via a *different* file handle/descriptor. That goes
along with the documentation that calling them while holding a lock via
the *same* file handle/descriptor may deadlock.

Document the behavior of unlock if no lock is held.

r? `@m-ou-se`
(Documentation changes requested in rust-lang#130994 .)
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Jan 31, 2025
Rollup merge of rust-lang#136288 - joshtriplett:would-you-could-you-with-some-locks--would-you-could-you-in-some-docs, r=m-ou-se

Improve documentation for file locking

Add notes to each method stating that locks get dropped on close.

Clarify the return values of the try methods: they're only defined if
the lock is held via a *different* file handle/descriptor. That goes
along with the documentation that calling them while holding a lock via
the *same* file handle/descriptor may deadlock.

Document the behavior of unlock if no lock is held.

r? `@m-ou-se`
(Documentation changes requested in rust-lang#130994 .)
github-actions bot pushed a commit to rust-lang/miri that referenced this issue Jan 31, 2025
…locks--would-you-could-you-in-some-docs, r=m-ou-se

Improve documentation for file locking

Add notes to each method stating that locks get dropped on close.

Clarify the return values of the try methods: they're only defined if
the lock is held via a *different* file handle/descriptor. That goes
along with the documentation that calling them while holding a lock via
the *same* file handle/descriptor may deadlock.

Document the behavior of unlock if no lock is held.

r? `@m-ou-se`
(Documentation changes requested in rust-lang/rust#130994 .)
@rfcbot rfcbot added finished-final-comment-period The final comment period is finished for this PR / Issue. to-announce Announce this issue on triage meeting and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. labels Feb 9, 2025
@rfcbot
Copy link
Collaborator

rfcbot commented Feb 9, 2025

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

This will be merged soon.

@joshtriplett
Copy link
Member

@BurntSushi I no longer think those cases are worth distinguishing myself, as the error case largely can't happen. For instance, you can't normally get EBADF because the file is known valid.

@BurntSushi
Copy link
Member

I'm not sure that changes my opinion here unless there is a guarantee that other errors can't happen. For me personally, if I were using this API, I would want to treat the two cases separately, if only to use different error messages.

@joshtriplett
Copy link
Member

@BurntSushi No objection, if you feel that you would actually use that.

@cberner
Copy link
Contributor Author

cberner commented May 20, 2025

I have a slight preference for keeping TryLockError::WouldBlock, but I don't have a strong opinion either way. I'll put together a PR to add the From impl for the current API, but lemme know if you all decide you'd rather go with returning io::Error

Here's an example of how I use the current API, and you can see that I already handle multiple special error cases:

  • lock not acquired because it's already held by another process
  • lock not acquired because filesystem does not support locking
  • lock not acquired due to some other I/O error

@Amanieu
Copy link
Member

Amanieu commented May 20, 2025

I personally have a very slight preference towards keeping TryLockError but would be happy with either choice. Given the shape of the discussion in the last few comments, I think we should just merge #141312 and close #140718.

@cuviper cuviper removed this from the 1.87.0 milestone May 22, 2025
@joshtriplett
Copy link
Member

@rfcbot resolved api-is-unergonomic

@rfcbot rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels May 27, 2025
@rfcbot
Copy link
Collaborator

rfcbot commented May 27, 2025

🔔 This is now entering its final comment period, as per the review above. 🔔

@Amanieu Amanieu removed finished-final-comment-period The final comment period is finished for this PR / Issue. I-libs-api-nominated Nominated for discussion during a libs-api team meeting. labels May 27, 2025
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue May 27, 2025
Add From<TryLockError> for io::Error

Adds a `From` impl to make error propagation easier, as discussed in the tracking issue

`TryLockError` is unstable under the "file_lock" feature. The related tracking issue is rust-lang#130994

This PR also cleanups the Windows implementation of `try_lock()` and `try_lock_shared()` as [discussed here](rust-lang#140718 (comment))
rust-timer added a commit that referenced this issue May 27, 2025
Rollup merge of #141312 - cberner:filelock_from, r=joshtriplett

Add From<TryLockError> for io::Error

Adds a `From` impl to make error propagation easier, as discussed in the tracking issue

`TryLockError` is unstable under the "file_lock" feature. The related tracking issue is #130994

This PR also cleanups the Windows implementation of `try_lock()` and `try_lock_shared()` as [discussed here](#140718 (comment))
github-actions bot pushed a commit to rust-lang/miri that referenced this issue May 28, 2025
Add From<TryLockError> for io::Error

Adds a `From` impl to make error propagation easier, as discussed in the tracking issue

`TryLockError` is unstable under the "file_lock" feature. The related tracking issue is rust-lang/rust#130994

This PR also cleanups the Windows implementation of `try_lock()` and `try_lock_shared()` as [discussed here](rust-lang/rust#140718 (comment))
github-actions bot pushed a commit to model-checking/verify-rust-std that referenced this issue May 30, 2025
Add From<TryLockError> for io::Error

Adds a `From` impl to make error propagation easier, as discussed in the tracking issue

`TryLockError` is unstable under the "file_lock" feature. The related tracking issue is rust-lang#130994

This PR also cleanups the Windows implementation of `try_lock()` and `try_lock_shared()` as [discussed here](rust-lang#140718 (comment))
@fishface60
Copy link

Having banged my head against the Java file lock APIs recently I'm glad Rust hasn't decided to use the fcntl F_SETLK interfaces just because they include file range locking. It's been a lot of work to work around the limitations caused by that.

I like that this implementation documents what the platform-specific behaviours are by platform. It was irritating that the Java implementation documented that "some platforms may convert a request for a shared lock into an exclusive lock" and then I could find no trace of which one.

To its credit however, it gave recommendations for how to use the interfaces in a way that is safe cross-platform. I'm not sure how obvious it is from the Rust documentation that you should treat all locks as advisory and open files for write if you take an exclusive lock or read for a shared lock.

I think it may be important to document the platform-specific lock inheritance behaviour. It wasn't hugely relevant for Java since all implementations' locks were per-process, but with flock the lock will be inherited if the file descriptor is and with LockFileEx subprocesses don't inherit the lock.

@cberner
Copy link
Contributor Author

cberner commented Jun 6, 2025

@fishface60

I'm not sure how obvious it is from the Rust documentation that you should treat all locks as advisory

I tried to make this clear in this paragraph of the docs, but feel free to suggest improvements!:

    /// This lock may be advisory or mandatory. This lock is meant to interact with [`lock`],
    /// [`try_lock`], [`lock_shared`], [`try_lock_shared`], and [`unlock`]. Its interactions with
    /// other methods, such as [`read`] and [`write`] are platform specific, and it may or may not
    /// cause non-lockholders to block.

open files for write if you take an exclusive lock or read for a shared lock

As far as I can tell that is not required. The tests pass on Linux and Windows even if I change them to use just read permission when acquiring an exclusive lock, and the Windows documentation says: The handle must have been created with either the GENERIC_READ or GENERIC_WRITE access right.

@cberner
Copy link
Contributor Author

cberner commented Jun 6, 2025

@joshtriplett it has been 10 days since the FCP started, but it seems like @rfcbot is stuck. Maybe due to this issue

Alright if I submit the stabilization PR?

@cberner
Copy link
Contributor Author

cberner commented Jun 13, 2025

@m-ou-se it looks like you fixed this rfcbot issue the last time it happened. This FCP should have completed a week ago. Could you help get the FCP unstuck?

@ChrisDenton ChrisDenton added finished-final-comment-period The final comment period is finished for this PR / Issue. to-announce Announce this issue on triage meeting and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. labels Jun 13, 2025
@ChrisDenton
Copy link
Member

I'm not rustbot but I can do my best impression...

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

This will be merged soon.

jhpratt added a commit to jhpratt/rust that referenced this issue Jun 14, 2025
…enton

Stabilize "file_lock" feature

Closes rust-lang#130994

r? `@joshtriplett`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. to-announce Announce this issue on triage meeting
Projects
None yet
Development

Successfully merging a pull request may close this issue.