-
Notifications
You must be signed in to change notification settings - Fork 107
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
Add advisory file locking API #111
base: main
Are you sure you want to change the base?
Add advisory file locking API #111
Conversation
6a4dcd0
to
eb5dd59
Compare
We'll probably want to use OFD locks. |
@@ -203,6 +224,10 @@ final class FileOperationsTest: XCTestCase { | |||
XCTAssertEqual(readBytesAfterTruncation, Array("ab".utf8)) | |||
} | |||
} | |||
|
|||
func testFlock() throws { | |||
// TODO: We need multiple processes in order to test blocking behavior |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's been many years since I wrote a flock(2)
wrapper, but in those tests I ended up shelling out to flock(1)
as an interop test on Linux. I guess we won't be able to do anything like that in this project?
What do you have in mind for this test?
We could potentially at least check the lock-lock, lock-unlock, and lock-upgrade flows?
Given that two independent file descriptors for the same underlying file can still block each other there are some tests we can write in a single process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should pursue OFD locks which would be easier to check. Two separate open
calls in the same process should have different lock sets. When we get fork
support we can also test some behavior there.
Sources/System/FileLock.swift
Outdated
/// | ||
/// A shared lock may be upgraded to an exclusive lock, and vice versa, simply by specifying the appropriate | ||
/// lock type; this results in the previous lock being released and the new lock | ||
/// applied (possibly after other processes have gained and released the lock). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I appreciate this is as specific as man 2 flock
gets and its implicit that the upgrade only works on the same file descriptor I suppose, i.e. if a process has two independent file descriptors to the same underlying file, they can still block each other.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe flock(2)
would be associated between process/file, while OFD
locks would be file-description associated (but not descriptor, i.e. dup would share locks).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may already be on the same page here, but I believe that, with flock(2)
, using dup(2)
will allow the new fd to upgrade a lock, but using open(2)
will not.
Here's a toy program that accepts NEW_FD_METHOD
as an environment variable and attempts to upgrade a shared lock to an exclusive one using a new file descriptor pointing to the same file as was already locked:
https://gist.github.com/simonjbeaumont/7bd12f1f97e162734b6fec847d3a969f
Running it with both options and dup
is fine, open
fails:
❯ clang -o flock-test flock-test.c && NEW_FD_METHOD=dup ./flock-test
❯ clang -o flock-test flock-test.c && NEW_FD_METHOD=open ./flock-test
error obtaining exclusive lock with new fd 4: Resource temporarily unavailable
In a previous life I wrote a I guess the question is: how faithful/thin a wrapper do you want this to be over |
I am definitely going to use OFD locks for the higher level API. This means that there is a Since it's easier/better for the developer to call |
Ok, this has been switched over entirely to OFD locks. Questions still open:
|
Also note that An alternative is to drop the API and have We could craft some philosophy around this as well which could extend to e.g. |
I feel like the API here may still be more faithful to the history of file locking APIs than that history deserves. In mutexing APIs it's common to have a Footnotes
|
I like the idea of For the lock operation, it seems there are 3 variants:
When you say using For example, we could have: extension FileDescriptor {
var ownedLock: throws FileLock.Kind?
func tryLock(...) throws -> Bool
func lock(...) throws
func unlock(...) throws
#if !os(Linux)
func tryLock(..., blockUntilTimeout:...) throws -> Bool
#endif
} Though I'm sure we'll iterate on the shape and names of those API during review. E.g. I'm not clear if the timeout taking lock should be a variant of |
This has the API as This is ready for review and close to mergeable. We will need to go through some steps before the release:
|
2a64ac6
to
a1cbeeb
Compare
Early draft of API doc: https://gist.github.com/milseman/fbf1ec12ac9a6635d830d9457fec0776 |
384b14d
to
a15d9a7
Compare
@swift-ci please test |
When trying to resuscitate
fcntl
support, file locks came up again. Here I try to carve offflock
support.Some questions:
advisoryLock
?unlock
exist or should we have a 3-way enum forshared, exclusive, unlock
?