@@ -45,7 +45,7 @@ extension NSLock {
45
45
}
46
46
}
47
47
48
- enum ProcessLockError : Error {
48
+ public enum ProcessLockError : Error {
49
49
case unableToAquireLock( errno: Int32 )
50
50
}
51
51
@@ -89,7 +89,7 @@ public final class FileLock {
89
89
/// Try to acquire a lock. This method will block until lock the already aquired by other process.
90
90
///
91
91
/// Note: This method can throw if underlying POSIX methods fail.
92
- public func lock( type: LockType = . exclusive) throws {
92
+ public func lock( type: LockType = . exclusive, blocking : Bool = true ) throws {
93
93
#if os(Windows)
94
94
if handle == nil {
95
95
let h : HANDLE = lockFile. pathString. withCString ( encodedAs: UTF16 . self, {
@@ -112,17 +112,17 @@ public final class FileLock {
112
112
overlapped. Offset = 0
113
113
overlapped. OffsetHigh = 0
114
114
overlapped. hEvent = nil
115
+ var dwFlags = Int32 ( 0 )
115
116
switch type {
116
- case . exclusive:
117
- if !LockFileEx( handle, DWORD ( LOCKFILE_EXCLUSIVE_LOCK) , 0 ,
118
- UInt32 . max, UInt32 . max, & overlapped) {
119
- throw ProcessLockError . unableToAquireLock ( errno: Int32 ( GetLastError ( ) ) )
120
- }
121
- case . shared:
122
- if !LockFileEx( handle, 0 , 0 ,
123
- UInt32 . max, UInt32 . max, & overlapped) {
124
- throw ProcessLockError . unableToAquireLock ( errno: Int32 ( GetLastError ( ) ) )
125
- }
117
+ case . exclusive: dwFlags |= LOCKFILE_EXCLUSIVE_LOCK
118
+ case . shared: break
119
+ }
120
+ if !blocking {
121
+ dwFlags |= LOCKFILE_FAIL_IMMEDIATELY
122
+ }
123
+ if !LockFileEx( handle, DWORD ( dwFlags) , 0 ,
124
+ UInt32 . max, UInt32 . max, & overlapped) {
125
+ throw ProcessLockError . unableToAquireLock ( errno: Int32 ( GetLastError ( ) ) )
126
126
}
127
127
#else
128
128
// Open the lock file.
@@ -133,11 +133,17 @@ public final class FileLock {
133
133
}
134
134
self . fileDescriptor = fd
135
135
}
136
+ var flags = Int32 ( 0 )
137
+ switch type {
138
+ case . exclusive: flags = LOCK_EX
139
+ case . shared: flags = LOCK_SH
140
+ }
141
+ if !blocking {
142
+ flags |= LOCK_NB
143
+ }
136
144
// Aquire lock on the file.
137
145
while true {
138
- if type == . exclusive && flock ( fileDescriptor!, LOCK_EX) == 0 {
139
- break
140
- } else if type == . shared && flock ( fileDescriptor!, LOCK_SH) == 0 {
146
+ if flock ( fileDescriptor!, flags) == 0 {
141
147
break
142
148
}
143
149
// Retry if interrupted.
@@ -172,23 +178,22 @@ public final class FileLock {
172
178
}
173
179
174
180
/// Execute the given block while holding the lock.
175
- public func withLock< T> ( type: LockType = . exclusive, _ body: ( ) throws -> T ) throws -> T {
176
- try lock ( type: type)
181
+ public func withLock< T> ( type: LockType = . exclusive, blocking : Bool = true , _ body: ( ) throws -> T ) throws -> T {
182
+ try lock ( type: type, blocking : blocking )
177
183
defer { unlock ( ) }
178
184
return try body ( )
179
185
}
180
186
181
187
/// Execute the given block while holding the lock.
182
- public func withLock< T> ( type: LockType = . exclusive, _ body: ( ) async throws -> T ) async throws -> T {
183
- try lock ( type: type)
188
+ public func withLock< T> ( type: LockType = . exclusive, blocking : Bool = true , _ body: ( ) async throws -> T ) async throws -> T {
189
+ try lock ( type: type, blocking : blocking )
184
190
defer { unlock ( ) }
185
191
return try await body ( )
186
192
}
187
193
188
194
private static func prepareLock(
189
195
fileToLock: AbsolutePath ,
190
- at lockFilesDirectory: AbsolutePath ? = nil ,
191
- _ type: LockType = . exclusive
196
+ at lockFilesDirectory: AbsolutePath ? = nil
192
197
) throws -> FileLock {
193
198
// unless specified, we use the tempDirectory to store lock files
194
199
let lockFilesDirectory = try lockFilesDirectory ?? localFileSystem. tempDirectory
@@ -233,19 +238,21 @@ public final class FileLock {
233
238
fileToLock: AbsolutePath ,
234
239
lockFilesDirectory: AbsolutePath ? = nil ,
235
240
type: LockType = . exclusive,
241
+ blocking: Bool = true ,
236
242
body: ( ) throws -> T
237
243
) throws -> T {
238
- let lock = try Self . prepareLock ( fileToLock: fileToLock, at: lockFilesDirectory, type )
239
- return try lock. withLock ( type: type, body)
244
+ let lock = try Self . prepareLock ( fileToLock: fileToLock, at: lockFilesDirectory)
245
+ return try lock. withLock ( type: type, blocking : blocking , body)
240
246
}
241
247
242
248
public static func withLock< T> (
243
249
fileToLock: AbsolutePath ,
244
250
lockFilesDirectory: AbsolutePath ? = nil ,
245
251
type: LockType = . exclusive,
252
+ blocking: Bool = true ,
246
253
body: ( ) async throws -> T
247
254
) async throws -> T {
248
- let lock = try Self . prepareLock ( fileToLock: fileToLock, at: lockFilesDirectory, type )
249
- return try await lock. withLock ( type: type, body)
255
+ let lock = try Self . prepareLock ( fileToLock: fileToLock, at: lockFilesDirectory)
256
+ return try await lock. withLock ( type: type, blocking : blocking , body)
250
257
}
251
258
}
0 commit comments